Android Development, Kotlin

Creating battery friendly background tasks on Android

In my previous article, I wrote about concurrency on Android. Using a simple sentence to summarize, this means running heavy tasks on a separate thread (as opposed to the main thread), to make a user’s experience of your app better when they are using the app.

Background tasks are different from the previous conversations about concurrency. Some applications may require some tasks to be run when the app is in the background or not running (some of the tasks include – downloading updates or syncing data with the one on the server). Solutions like Alarm Manager + Broadcast receivers, JobSchedulers and Firebase JobDispatcher already exist and they have their own unique use cases. In this article, I would try to explain WorkManager, which is part of Android Jetpack. In my opinion, it is an easier solution.

 

Complexity running background tasks before WorkManager

As mentioned earlier, there are APIs for doing background work. However, not using these APIs in the correct way can have a negative effect on our user’s battery and this can make them uninstall our app eventually. To help with this problem, Android introduced battery saving features (such as battery saver & doze mode) to help properly manage the power usage of apps.

For developers, this solution has introduced more problems because we would need to work with the APIs that do background work and the battery saving features while trying to maintain backward compatibility in the apps we are building. Our code could become very complex which could be a problem in a few months when a new team member is trying to go through the codebase. Thankfully, WorkManager came to save the day.

 

WorkManager

WorkManager provides a generic solution for background work automatically taking battery and API level into consideration for us. Other important things to note about WorkManager are listed below:

  • Compatible with API levels 14+
  • Manages deferrable background work
  • Manages guaranteed background work
  • Not useful for tasks that should run at a precise time – Use Alarm manager instead

With WorkManager, you might also choose to run tasks under some certain constraints. For example, you might want a particular task to run when the device is idle, charging and there is internet connectivity.

 

Deferrable & Guaranteed work

  • Deferrable: Must it run now? A deferrable work is a task that can be run later and is still useful when run. For example, fetching data from the server so that a user gets fresh content the next time they open the app is deferrable work but sending an instant message is not deferrable work because the message has to be sent immediately.
  • Guaranteed: A guaranteed work is one that would run even if the android device is restarted.

 

Creating battery friendly tasks

When creating battery friendly background tasks, there are three things that should ultimately be considered.

  • Make sure the work isn’t using excessive CPU or disk. Try to optimize your code to properly use these resources.
  • Try to schedule the longest possible delay for repeating tasks.
  • Be sure it’s important to use the API before implementing it. The most battery friendly background work is the one that does not exist.

 

Using WorkManager

To use the WorkManager API, define your Worker class, implement Worker and write the code for the task in the doWork() function.

class FetchWorker(ctx: Context, params: WorkerParameters) : Worker(ctx, params){
   override fun doWork(): Result{
      val res = fetchAllData() // the actualt work to be done
      if(res){
         return Result.success()
      }else{
         return Result.failure()
      }
   }
}

To do this work, we need to build the WorkRequest. This basically helps us configure when and how our tasks should be run. When doing this, we can also add some constraints like this:

val constraints = Constraints.Builder()  //if you want to add constraints
     .setRequiredNetworkType(NetworkType.CONNECTED)  
     .setRequiresDeviceIdle(true)
     .setRequiresCharging(true)
     .build()

// create a OneTimeWorkRequest that uses the constraints we just built
// OneTimeWorkRequestBuilder is used for non-repeating work
val fetchDataRequest = OneTimeWorkRequestBuilder<FetchWorker>() 
        .setConstraints(constraints)
        .build()

After creating the WorkRequest, the final step would be to enqueue the request like this:

WorkManager.getInstance().enqueue(fetchDataRequest)

That’s all we need to do. And now, we are certain that the task will be performed in the future when all the three specified constraints are met.

Conclusion

In this article, I tried to explain WorkManager and how to use it for battery friendly background tasks. For further reading about the WorkManager API, you can go to the android documentation.

 

 

Spread the love

Leave a Reply

Your email address will not be published. Required fields are marked *