Search code examples
androidkotlinandroid-workmanagerandroid-background

Background processing in modern Android


I have implemented some background task with WorkManager and with usage this Guide. One time I needed to use CoroutineWorker. I find these guide pretty good but every time I need new requirement for background processing I start new research for what I need. So my question is what is the correct way to address the following use cases with modern Android. Also please consider that different phone brands kill background task defiantly (Some brands more harsh on background processing). And when do I need to use persistent notification and when it is not required, and when to use AlarmManger. Please write for each the best way to implement and if Persistent notification required.

Use cases:

1. Downloading large file (500mb)

a. Once a day on exact time

b Once a day when the OS can, in any time

2. Downloading small file (5k)

a. Once a day on exact time

b Once a day when the OS can, in any time

3. Setting alarm at exact time

4. Sync DB with server (large data about 5min runtime)

a. Every 15 min (Minimal time?)

b. Once a Day

5. Sync DB with server (small data less than 1sec runtime)

a. Every 5 min

b. Every 15 min

c. Once a Day


Solution

  • According to this reference of WorkManager, there are three types of persistent work you can schedule through it,

    • Immediate: Tasks that must begin immediately and complete soon. May be expedited.
    • Long Running: Tasks which might run for longer, potentially longer than 10 minutes.
    • Deferrable: Scheduled tasks that start at a later time and can run periodically.

    This means most of your use cases are achievable by using WorkManager except two cases where you require it to run "Every 5 min" & "Setting alarm at exact time".

    It is recommended from here that if you want to set alarm at exact time then WorkManager shouldn’t be used, instead use AlarmManager which will wake device even from doze mode at specified time.


    Checkout all possible executions that work manager provides: WorkManager flow (Courtesy of WorkManager guide)

    Hence, following are all possible approach you can use according to your use-cases:

    1. Downloading large file (500mb)

      a. Once a day on exact time

      Answer: You can use PeriodicWork here scheduled for every 24 hours from given start time, since it's long running operation you can make it expedited/foreground to track ongoing progress. Although bear in mind that downloading requires active internet connection and at specified interval if it's not available then it will be scheduled for delayed/later execution.

      PeriodicWorkRequestBuilder<LongRunningWorker>(1, TimeUnit.DAYS)
          .setExpedited(OutOfQuotaPolicy.RUN_AS_NON_EXPEDITED_WORK_REQUEST)
          // Additional configuration
          .build()
      
      //Calling setForeground() from doWork() will show ongoing notification
      

      b. Once a day when the OS can, in any time

      Answer: This is the best example where you can execute some task indeterminately using PeriodicWork. Here you can make it deferrable since it's not required to be executed at specific time.

      PeriodicWorkRequestBuilder<LongRunningWorker>(1, TimeUnit.DAYS)
           // Additional configuration
           .build()
      
      //Calling setForeground() from doWork() will show ongoing notification
      
    2. Downloading small file (5k)

      a. Once a day on exact time

      Answer: It can be PeriodicWork with immediate execution (expedited work) scheduled for 24 hours, since it won't take long to download small file.

      PeriodicWorkRequestBuilder<ShortSpanWorker>(1, TimeUnit.DAYS)
          .setExpedited(OutOfQuotaPolicy.RUN_AS_NON_EXPEDITED_WORK_REQUEST)
          // Additional configuration
          .build()
      

      b. Once a day when the OS can, in any time

      Answer: It can be PeriodicWork with deferrable execution that can be scheduled at 24 hours.

      PeriodicWorkRequestBuilder<ShortSpanWorker>(1, TimeUnit.DAYS)
         // Additional configuration
         .build()
      
    3. Setting alarm at exact time

      Answer: For setting alarm to be executed on exact time, you should use AlarmManager. This usecase is not possible with WorkManager because, it doesn't interrupt Doze mode (Device's deep sleep).

    4. Sync DB with server (large data about 5min runtime)

      a. Every 15 min (Minimal time?)

      Answer: This can be PeriodicWork scheduled every 15 mins being foreground work since it is long running.

       PeriodicWorkRequestBuilder<SyncToServerWorker>(15, TimeUnit.MINUTES)
           // Additional configuration
           .build()
      
       //Calling setForeground() from doWork() will show ongoing notification
      

      b. Once a Day

      Answer: This can be PeriodicWork scheduled every 24 hours being foreground work since it is long running.

       PeriodicWorkRequestBuilder<SyncToServerWorker>(1, TimeUnit.DAYS)
           // Additional configuration
           .build()
      
       //Calling setForeground() from doWork() will show ongoing notification
      
    5. Sync DB with server (small data less than 1sec runtime)

      a. Every 5 min

      Answer: This use-case is not achievable since minimum time-frame for WorkManager's periodic work is 15 mins. Hence, it is suggested to manage this periodic work manually by foreground service/alarm manager combination.

      b. Every 15 min

      Answer: This is achievable by PeriodicWork scheduled for every 15 mins which can either expedited or deferrable based on how urgent/relaxed it is required.

       PeriodicWorkRequestBuilder<SyncDataWorker>(15, TimeUnit.MINUTES)
           .setExpedited(OutOfQuotaPolicy.RUN_AS_NON_EXPEDITED_WORK_REQUEST) // Decide whether to make it expedited or deferrable here conditionally
           // Additional configuration
           .build()
      

      c. Once a Day

      Answer: This is example of PeriodicWork scheduled for every 24 hours which can either expedited or deferrable based on how urgent/relaxed it is required.

       PeriodicWorkRequestBuilder<SyncDataWorker>(1, TimeUnit.DAYS)
           .setExpedited(OutOfQuotaPolicy.RUN_AS_NON_EXPEDITED_WORK_REQUEST) // Decide whether to make it expedited or deferrable here conditionally
           // Additional configuration
           .build()
      

    Reference for creating long running workers: https://developer.android.com/topic/libraries/architecture/workmanager/advanced/long-running#long-running

    Hope this helps!