Skip to content

Latest commit

 

History

History
514 lines (371 loc) · 16.3 KB

File metadata and controls

514 lines (371 loc) · 16.3 KB

Frequently Asked Questions (FAQ)

General Questions

Q: Will my task run if the app is force-closed?

A: It depends on the platform and how the app was closed:

Android: Yes! Tasks are registered with Android WorkManager and survive:

  • User force-close (swiping the app away)
  • System Purge (OOM kills)
  • Phone reboot Note: For maximum reliability, use isHeavyTask: true (Foreground Service) for critical tasks to ensure execution even in Doze mode.

iOS:

  • System Purge (OOM kill): Yes. If iOS kills the app in the background to reclaim memory, tasks are preserved and will trigger when iOS allocates the next background window. native_workmanager excels here because it uses a native SQLite store to recover state instantly.
  • User Force-Quit (Swipe up): No. This is a strict Apple platform limitation. If a user manually swipes the app away, iOS cancels all pending background tasks. The system will not wake the app again until the user manually relaunches it. No third-party package can bypass this.

Q: How does native_workmanager compare to flutter_background_service for long-running tasks?

A: The underlying mechanisms are fundamentally different, making native_workmanager vastly more resilient to system memory limits (OOM kills):

  1. Zero-Engine Memory Footprint (~2MB vs 50MB+): Traditional packages like flutter_background_service keep a full Flutter Engine running 24/7 inside a persistent service. When Android runs low on RAM, the OOM Killer targets heavy processes first. Once killed, it struggles to restart. native_workmanager's Native Workers run in pure Kotlin/Swift (<50ms startup, ~2MB RAM). Because it is so lightweight, the OS rarely considers it a target for termination.

  2. Event-Driven vs. Persistent Service: We do not maintain a permanent service. native_workmanager relies on native schedulers (WorkManager / BGTaskScheduler). The Foreground Service (via isHeavyTask) is only invoked at the exact moment the task runs and is torn down immediately after.

  3. System-Level State Persistence: If the OS does kill the process mid-execution, the task state is safely stored in the internal WorkManager SQLite database. Android will automatically reschedule it once resources free up.


Q: How much memory does a task actually use?

A: It depends on the worker type:

Worker Type Memory Usage Startup Time
Native Workers 2-5 MB <100ms
Dart Workers ~50 MB 200-500ms
Custom Native Workers Varies (typically 1-10 MB) <100ms

Why the difference?

  • Native workers execute without starting Flutter engine
  • Dart workers need full Flutter engine initialization

Q: Can I chain 100 tasks together?

A: Technically yes, but not recommended for several reasons:

iOS Limitation:

  • Each task in chain must complete within 30 seconds
  • iOS may cancel long chains
  • Recommendation: Keep chains to 3-5 tasks max on iOS

Android:

  • No strict limit, but very long chains can be fragile
  • Recommendation: Keep chains to 5-10 tasks max

Better approach:

// Instead of: Task1 → Task2 → ... → Task100
// Use: Periodic task that processes batches
NativeWorkManager.enqueue(
  taskId: 'batch-processor',
  trigger: TaskTrigger.periodic(Duration(hours: 1)),
  worker: DartWorker(callbackId: 'processBatch'),
);

Q: What happens if a task in a chain fails?

A: The chain stops at the failed task:

  1. Tasks before failure: ✅ Completed successfully
  2. Failed task: ❌ Marked as failed
  3. Tasks after failure: ⏸️ Not executed (cancelled)

Example:

TaskA (✅) → TaskB (❌ FAILS) → TaskC (⏸️ Skipped) → TaskD (⏸️ Skipped)

Retry behavior:

  • If you configured retry policy, the failed task retries
  • Chain continues only if retry succeeds
  • If all retries fail, chain stops permanently

Q: Is this compatible with workmanager?

A: ~90% API compatible with minor syntax changes required.

Main differences:

  1. Import: import 'package:native_workmanager/native_workmanager.dart';
  2. Initialization: NativeWorkManager.initialize() vs Workmanager.initialize()
  3. Enqueue syntax: Different trigger API
  4. Native workers: New capability not in workmanager

See full migration guide →


Q: Can I use this for continuous location tracking?

A: No, background tasks are for periodic work, not continuous tracking.

Why?

  • Tasks run at intervals (minimum 15 minutes on iOS, 15-30 minutes on Android)
  • Tasks have execution time limits (30 seconds on iOS, 10 minutes on Android)
  • OS may defer tasks to save battery

For location tracking, use:

Use native_workmanager for:

  • Upload location batches every hour
  • Process and sync location logs
  • Periodic geofence checks

Q: Do tasks run when device is in Doze mode?

A: By default, tasks are deferred during Doze mode to save battery. However, you can bypass this using specific constraints.

Should I use allowWhileIdle or isHeavyTask? (Do not use both)

They serve different purposes and combining them is redundant and may cause scheduling conflicts:

  1. isHeavyTask: true (Foreground Service)

    • Android: Promotes your task to a full Foreground Service (requires ForegroundNotificationConfig). It completely bypasses Doze mode, ignores App Standby quotas, and survives app kills. This is the highest priority execution Android allows.
    • iOS: Maps to a BGProcessingTask.
    • When to use: For critical, long-running tasks (e.g., uploading large files) that MUST finish reliably.
    • Trade-off: Shows a visible notification to the user while running.
  2. allowWhileIdle: true (Expedited Work)

    • Android: Uses WorkManager's Expedited Work mechanism to run the task even if the device is locked/in Doze mode, without showing a persistent notification.
    • iOS: Ignored (no effect).
    • When to use: For short, fast tasks (< 1 minute) that you want to trigger silently.
    • Trade-off: Strictly regulated by Android's App Standby Buckets (quotas). If you exceed the quota, Android downgrades the task. Also, WorkManager rejects Expedited jobs if combined with constraints like requiresCharging.

Summary: If you use isHeavyTask: true, the task already bypasses Doze mode. Adding allowWhileIdle: true is redundant and discouraged.


Q: Why do tasks only trigger immediately AFTER the screen is turned on (when locked)?

A: This is a strict OS-level battery saving feature, not a plugin bug.

Android (Doze Mode & Manufacturer Optimizations): When the app is killed and the screen is locked/off, Android enters Doze mode (or manufacturer-specific deep sleep, especially on Xiaomi, Huawei, Oppo, Vivo). In this state, the OS suspends all WorkManager tasks, network access, and WakeLocks to save battery. As soon as the user turns the screen on or unlocks the device, the OS exits Doze mode and all deferred tasks trigger immediately.

To mitigate this:

  1. Guide users to device Settings and set your app's Battery usage to Unrestricted.
  2. On aggressive ROMs (Xiaomi/Oppo/Vivo), users must enable Auto-start for your app.
  3. You can prompt users for the REQUEST_IGNORE_BATTERY_OPTIMIZATIONS permission if your app's core functionality requires it.

iOS: iOS BGTaskScheduler is strictly opportunistic. While iOS can execute tasks while locked, it may heavily defer them if the app was swiped away (killed) or if the device is in Low Power Mode.


Q: Can I schedule exact-time tasks (e.g., alarm at 7:00 AM)?

A: No, native_workmanager is for flexible background tasks, not exact alarms.

Why?

  • iOS doesn't support exact-time background tasks
  • Android Doze mode defers tasks anyway
  • Background tasks are designed for flexibility

For exact alarms, use:

Use native_workmanager for:

  • Periodic work that can be flexible (±15 minutes is OK)
  • Background data sync
  • File processing tasks

Q: How do I debug background tasks?

A: Follow these strategies:

1. Use logging:

worker: DartWorker(
  callbackId: 'myTask',
  onProgress: (progress) {
    print('Task progress: $progress'); // Won't show in release
    // Use proper logging instead:
    developer.log('Task progress: $progress', name: 'NativeWorkManager');
  },
)

2. Listen to task events:

NativeWorkManager.events.listen((event) {
  print('Task ${event.taskId}: ${event.success ? "✅" : "❌"}');
  print('Message: ${event.message}');
});

3. Check native logs:

iOS: Use Xcode Console while device is connected Android: Use adb logcat or Android Studio Logcat

4. Test in foreground first:

// Test task logic in foreground before background
await myTaskLogic();  // Test this works
// Then schedule as background task

Q: Can I pass data between tasks in a chain?

A: Direct data passing between tasks is not natively supported. Use shared storage as a workaround:

// Task 1: Save result
await NativeWorkManager.beginWith(
  TaskRequest(id: 'task1', worker: DartWorker(callbackId: 'saveData')),
).then(
  TaskRequest(id: 'task2', worker: DartWorker(callbackId: 'useData')),
).enqueue();

// In task1 callback:
void saveData() async {
  final prefs = await SharedPreferences.getInstance();
  await prefs.setString('task1_result', jsonEncode(result));
}

// In task2 callback:
void useData() async {
  final prefs = await SharedPreferences.getInstance();
  final data = jsonDecode(prefs.getString('task1_result')!);
  // Use data...
}

Q: What's the minimum interval for periodic tasks?

A: Platform-specific minimums:

Platform Minimum Interval Notes
Android 15 minutes WorkManager API limitation
iOS 15 minutes BGTaskScheduler limitation

Example:

// ✅ Works (1 hour)
trigger: TaskTrigger.periodic(Duration(hours: 1))

// ⚠️ Will be clamped to 15 minutes
trigger: TaskTrigger.periodic(Duration(minutes: 5))

// ❌ Will fail
trigger: TaskTrigger.periodic(Duration(seconds: 30))

Q: Do I need internet permission?

A: Only if your tasks use network:

Android (android/app/src/main/AndroidManifest.xml):

<uses-permission android:name="android.permission.INTERNET" />

iOS (ios/Runner/Info.plist):

  • Network access allowed by default
  • If using HTTP (not HTTPS), configure App Transport Security

Permissions for workers:

  • HTTP workers: Need INTERNET permission
  • File workers: No special permissions
  • Crypto workers: No special permissions

Q: Can I use this with other background plugins?

A: Yes, but avoid conflicts:

Compatible:

  • flutter_local_notifications - Different use case (notifications vs tasks)
  • geolocator - Can coexist (use geolocator for continuous, native_workmanager for periodic)
  • shared_preferences - For task data storage

Potential conflicts:

  • ⚠️ workmanager - Same underlying APIs, choose one
  • ⚠️ workmanager - Same underlying APIs, choose one

Best practice: Use native_workmanager as your primary background task solution.


Q: How do I handle task failures?

A: Use retry policies and constraints:

await NativeWorkManager.enqueue(
  taskId: 'critical-sync',
  trigger: TaskTrigger.oneTime(),
  worker: NativeWorker.httpSync(url: 'https://api.example.com/sync'),
  constraints: Constraints(
    requiresNetwork: true,
    backoffPolicy: BackoffPolicy.exponential,  // Exponential backoff
    backoffDelayMs: 30000,  // Start with 30s delay
    maxRetries: 5,  // Retry up to 5 times
  ),
);

Retry strategies:

  • BackoffPolicy.linear: Fixed delay (30s, 30s, 30s, ...)
  • BackoffPolicy.exponential: Growing delay (30s, 60s, 120s, 240s, ...)

Q: Where can I get help?

Support channels:

Before asking:

  1. Check this FAQ
  2. Read Getting Started Guide
  3. Review Use Cases for similar scenarios
  4. Search existing issues

Platform-Specific Questions

iOS: Why isn't my task running?

Common reasons:

  1. Task not triggered yet

    • iOS defers background tasks
    • Test by backgrounding app, waiting 30+ seconds
  2. 30-second limit exceeded

    • Tasks must complete in 30 seconds
    • Use native workers (faster startup, no engine overhead)
    • Split into chains
  3. Low Power Mode active

    • iOS deprioritizes background tasks
    • Add requiresCharging: true constraint
  4. BGTaskScheduler not configured

    • Check Info.plist has BGTaskSchedulerPermittedIdentifiers
    • See iOS Guide

Android: "KmpWorkManager not initialized" error?

Common reasons:

  1. Minimum SDK version too low

    • Plugin requires API 26+ (Android 8.0+)
    • Edit android/app/build.gradle:
    defaultConfig {
        minSdk 26  // Must be 26 or higher!
    }
  2. Initialization not called or not awaited

    • Ensure await NativeWorkManager.initialize() in main()
    void main() async {
      WidgetsFlutterBinding.ensureInitialized();
      await NativeWorkManager.initialize();  // ← Must await!
      runApp(MyApp());
    }
  3. Build cache corruption

    • Clean and rebuild:
    flutter clean
    rm -rf android/build android/app/build
    flutter pub get
    flutter build apk --debug
  4. Check logcat for details

    adb logcat -s NativeWorkmanagerPlugin

See also: Full Android Setup Guide


Android: Why is my task delayed?

Common reasons:

  1. Doze Mode

    • Android defers tasks in Doze
    • Use requiresCharging: true or wait for idle window
  2. Battery Saver Mode

    • Tasks have lower priority
    • Use requiresBatteryNotLow: true
  3. Network constraint not met

    • Task waits for network
    • Check requiresNetwork: true constraint
  4. Minimum interval not met

    • Periodic tasks minimum: 15 minutes
    • Check your trigger interval

Advanced Questions

Q: Can I create custom native workers in Kotlin/Swift?

A: Yes! See Custom Native Workers Guide

Quick example:

Kotlin:

class MyCustomWorker : AndroidWorker {
    override suspend fun doWork(input: String?): WorkerResult {
        // Your Kotlin code here
        return WorkerResult.success("Done!")
    }
}

Swift:

class MyCustomWorker: IosWorker {
    func doWork(input: String?) async throws -> WorkerResult {
        // Your Swift code here
        return WorkerResult.success(data: "Done!")
    }
}

Q: What's the performance impact on app startup?

A: Minimal impact:

Initialization:

  • Native platform APIs (WorkManager, BGTaskScheduler)
  • Time: <10ms on modern devices
  • Memory: <1MB

Background task execution:

  • No impact on app foreground performance
  • Tasks run in separate process/thread

Q: Is this suitable for enterprise apps?

A: Yes! Production-ready features:

  • Security audited - No critical vulnerabilities
  • 462 tests passing - 100% pass rate
  • Used in production - Apps with 1M+ users
  • MIT licensed - Commercial use allowed
  • Comprehensive docs - 20+ guides

See Production Guide →


Didn't find your question?