Android JetPack WorkManager Example

Google released Android Jetpack. It’s a set of libraries, tools and architectural guidance to help make it quick and easy to build great Android apps. In this Android Jetpack, team at Google release one library specifically designed for scheduling and managing the background tasks. It’s called “WorkManager”.

WorkManager

The WorkManager API makes it easy to specify deferrable, asynchronous tasks and when they should run. These APIs let you create a task and hand it off to WorkManager to run immediately or at an appropriate time. For example, an app might need to download new resources from the network from time to time. Using these classes, you can set up a task, choose appropriate circumstances for it to run (like “only while device is charging and online”), and hand it off to WorkManager to run when the conditions are met. The task is still guaranteed to run, even if your app is force-quit or the device is rebooted.

Why need WorkManager

For background task which needs guaranteed execution and can be deferrable we have lot of options to do.We can use JobScheduler API but it is supported for API≥23. To overcome this we have FirebaseJobDispatcher library which provide backward compatibility upto API 14 but it requires Google Play services.So to avoid all these handling Work manager comes to rescue.
You don’t need to write device logic to figure out what capabilities the device has and choose an appropriate API; instead, you can just hand your task off to WorkManager and let it choose the best option.

How WorkManager works

WorkManager chooses the appropriate way to run your task based on such factors as the device API level and the app state. If WorkManager executes one of your tasks while the app is running, WorkManager can run your task in a new thread in your app’s process. If your app is not running, WorkManager chooses an appropriate way to schedule a background task depending on the device API level and included dependencies, WorkManager might use JobSchedulerFirebase JobDispatcher, or AlarmManager.

  1. For devices whose API>=23 it will use JobScheduler as the best option to schedule the task.
  2. For devices whose API>=14 and in which google play services installed, it will use FirebaseJobDispatcher.
  3. For devices whose API<14 it will use AlarmManager or BroadcastReceivers.

FEATURES OF WORKMANAGER

  • Guaranteed, constraint-aware execution : Let’s say If I wanna upload a photo. I only wanna do it when the device has the network, that’s the constraint.
  • Respectful of system background restrictions : Let’s say If your app in a doze mode it won’t wake up your phone just to do those work.
  • Backwork compatible with or without Google play services : It uses internally AlarmManager and BroadcatsReceiver for device which don’t have google play services installed.
  • Queryable If you have a queue of work, you can actually check what’s the state. Is it running right now, has it succeeds or fails.
  • Chainable It is also chainable like you have work A depending on work B and C which in turn out a dependent on work D.
  • Opportunistic This means WorkManager tried to execute the work in your process as soon as the constraint match- without actually needing JobScheduler.

WORKMANAGER CORE CLASSES

There are few WorkManager classes you need to know about.

  • Worker : specifies what task you need to perform. The WorkManager APIs include an abstract Worker You extend this class and perform the work here.
  • WorkRequestrepresents an individual task. A WorkRequest object specifies which Worker class should perform the task. However, you can also add details to the WorkRequest object, specifying things like the circumstances under which the task should run. Every WorkRequest has an autogenerated unique ID; you can use the ID to do things like cancel a queued task or get the task’s state. WorkRequest is an abstract class; in your code, you’ll be using one of the direct subclasses, OneTimeWorkRequest or PeriodicWorkRequest.

          Types of WorkRequest

           OneTimeWorkRequest : For task that you need to run only once.

           PeriodicWorkRequest : For task that you need to perform repeatedly.

  • WorkManager : enqueues and manages the work requests. You pass your WorkRequest object to WorkManager to enqueue the task.

 How to use WorkManager

So, enough of this theory let’s see how we can integrate WorkManager in Android app.

First, open your app level build. gradle file and add a dependency.

Add dependency 

dependencies {
implementation "android.arch.work:work-runtime:1.0.0-alpha02" }
Extend the worker class

Now the setup part is done. Let’s see how we can make a simple Worker.  

Class UploadImageWorker: Worker()  {

override fun doWork(): WorkerResult {

// Upload image to FirebaseStorage
uploadImage()

// Indicate success or failure with your return value:
return WorkerResult.SUCCESS

// (Returning RETRY tells WorkManager to try this task again
// FAILURE says not to try again.)

}

}

The doWork method actually runs on a background thread. WorkManager automatically runs this function on a background thread you don’t need to do run. You see doWork method have return type WorkerResultSo, we need to return Success if everything goes well and Failure if any error occurred.

Create a  WorkRequest

Now let’s see how we can call this UploadImageWorker class by creating request.

val uploadImageRequest = OneTimeWorkRequest.Builder(UploadImageWorker::class.java)
        .build()
val workManager = WorkManager.getInstance()
workManager.enqueue(uploadImageRequest)
Adding Contraints
  • Constraints specifies restrictions on when the task should run (for example, “only when connected to the network”). You create the Constraints object with Constraints.Builder, and pass the Constraints to the WorkRequest.Builder before creating the WorkRequest.
  • Soon after this request enqueued it will start uploading images. Now, what if you lose connectivity in the middle of this or even before this what if you do not have connectivity. You actually wanna constraints in this case.
  • The following shows how to add constraints in WorkRequest.
val constraints = Constraints.Builder()
        .setRequiredNetworkType(NetworkType.CONNECTED)
        .build()


val uploadImageRequest = OneTimeWorkRequest.Builder(UploadImageWorker::class.java)
        .setConstraints(constraints)
        .build()
val workManager = WorkManager.getInstance()
workManager.enqueue(uploadImageRequest)
Observing Work

Now let’s say I want to observe the request. I wanna show a progress bar while this work is executing and I wanna hide the spinner when it’s done.

The following shows how to observe a WorkRequest.

workManager.getStatusById(uploadImageRequest.id).observe(this, Observer {
    if(it!=null && it.state.isFinished)
    {
        progressBar.setVisbility(GONE)
    }
});

it : refers to WorkStatus  contains information about a particular task.

The WorkManager provides a LiveData for each WorkRequest .The LiveData holds a WorkStatus object; by observing that LiveData, you can determine the current status of the task, and get any returned values after the task finishes.

Canceling a Task

You can cancel a task after you enqueue it. To cancel the task, you need its work ID, which you can get from the WorkRequest object. For example, the following code cancels the uploadImageRequest :

val workManager = WorkManager.getInstance()
workManager.cancelWorkById(uploadImageRequest.id)

Note:-We can also set tag in work request using addTag() method and can use this tag for query work status or cancelling the task instead of using workId.We can also set multiple tags on single request.

Running Multiple Request

Now let’s say I want to upload multiple images in parallel. The following shows how to execute requests in parallel.

val uploadImageRequest1 = OneTimeWorkRequest.Builder(UploadImageWorker::class.java)
        .build()

val uploadImageRequest2 = OneTimeWorkRequest.Builder(UploadImageWorker::class.java)
        .build()

val uploadImageRequest3 = OneTimeWorkRequest.Builder(UploadImageWorker::class.java)
        .build()

val workManager = WorkManager.getInstance()
workManager.enqueue(uploadImageRequest1,uploadImageRequest2,uploadImageRequest3)

These all work requests will be executed in parallel and everytime the order may differ.

Recurring tasks

You might have a task that you need to perform repeatedly. For example, before uploading the list of images to firebase storage you want to  check whether the list of images is in the compressed form or not by calling compressCheckRequest every one hour.

To create a recurring task, use the PeriodicWorkRequest.Builder class to create a PeriodicWorkRequest object, then enqueue the PeriodicWorkRequest the same way you would a OneTimeWorkRequest object.

val compressCheckRequest = PeriodicWorkRequest.Builder(CompressCheckWorker::class.java, 1,
        TimeUnit.HOURS)
        .build()

val workManager = WorkManager.getInstance()
workManager.enqueue(compressCheckRequest)
Chained tasks

Your app might need to run several tasks in a particular order. WorkManager allows you to create and enqueue a work sequence that specifies multiple tasks, and what order they should run in.

For example,here I will first compress the list of images  then upload it in firebase storage and then I will save the imageUrl in firebase database .The tasks must be run in the same order .

class CompressImageWorker : Worker() {
    override fun doWork(): WorkerResult {
      //compress image
        //compressImage()
        return WorkerResult.SUCCESS;
    }
}

class UploadImageWorker : Worker() {
    override fun doWork(): Worker.WorkerResult {
    //upload image to firebase storage
        //uploadImage()
        return WorkerResult.SUCCESS
    }
}

class SaveImageWorker : Worker() {
    override fun doWork(): WorkerResult {
     //save image to firebase database
        //saveImage()
        return WorkerResult.SUCCESS
    }
}

val compressImageRequest = OneTimeWorkRequest.Builder(CompressImageWorker::class.java)
        .setConstraints(constraints)
        .build()

val uploadImageRequest = OneTimeWorkRequest.Builder(UploadImageWorker::class.java)
        .setConstraints(constraints)
        .build()
val saveImageRequest = OneTimeWorkRequest.Builder(SaveImageWorker::class.java)
        .setConstraints(constraints)
        .build()

val workManager = WorkManager.getInstance()

workManager.beginWith(compressImageRequest).then(uploadImageRequest).then(saveImageRequest).enqueue()

I hope that this post helps you in understanding the basics of WorkManager.Thank You for being Here.

Leave a Reply