Android JetPack Series: WorkManager

Neha Nathani
VersionN Studios
Published in
4 min readMay 30, 2018

What is WorkManager?

When writing Android apps, there are situations in which multiple operations need to be performed asynchronously. For e.g. processing data, syncing data, uploading or downloading files, etc. There are multiple threading options that enable us to do this viz. AsyncTask, ThreadPool, Intent Service, Job Scheduler etc. 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.

WorkManager over other existing techniques

In recent past, we have seen numerous battery optimisation techniques which are related to background processing. Doze mode and App Standby mode in Marshmallow, limited broadcasts in Nougat, background limitations in Oreo and App standby buckets, background restricted apps in Android P. While using these techniques, we need to consider the device API level and accordingly implement the technique. The great thing about WorkManager is backward compatibility, as with all Android Architecture components. What this essentially means is that WorkManager may use JobScheduler, Firebase JobDispatcher, or AlarmManager depending on device API level. It does all the heavy lifting and the developer doesn’t need to worry about what underlying functionality it uses.

All we need to do is Fire and Forget. In this post, we take take a quick look at the features of WorkManager and it’s usage.

Features of WorkManager

  1. Takes care of constraints with respect OS battery optimized features like Doze, standby etc and still assures guaranteed execution.
  2. Does not necessarily need Google Play Services.
  3. Backward compatible 👍
  4. Chaining of tasks can be done where one task depends on other.
  5. State of work being done can be determined i.e running, enqueued, succeeded or cancelled.
  6. Does not require job scheduler to batch work and executes the task on its own.

Core Classes

Worker

Worker is an abstract class that should be extended and perform specific work here. For e.g. Upload file, compress data, etc. In below snippet, doWork() runs on background thread.

class UploadWorker : Worker() {

override fun doWork(): Worker.WorkerResult {
var imageUriInput: String? = null
try
{

//TODO: upload logic here
return
Worker.WorkerResult.SUCCESS

} catch (e: Exception) {
val message = String.format("Failed to upload image with URI %s", imageUriInput)
Log.e(TAG, message)
return Worker.WorkerResult.FAILURE
}
}

companion object {
private val TAG = "UploadWorker"
}
}

Constraints

Constraints specifies restrictions on when the task should run. Create an object of Constraints as shown in snippet below.

// Create a Constraints that defines when the task should run
Constraints myConstraints = new Constraints.Builder()
.setRequiresCharging(true)
// Many other constraints are available, see the
// Constraints.Builder reference
.build();

WorkRequest

It is a task that worker class should perform. WorkRequest can be

  • OneTimeWorkRequest: A request that executes only once(non-repeating).
// ...then create a OneTimeWorkRequest that uses those constraints
OneTimeWorkRequest upload =
new OneTimeWorkRequest.Builder(UploadWorker.class)
.setInputData(createInputData())
.addTag(TAG_OUTPUT)
.setConstraints(myConstraints)
.build();
  • PeriodicWorkRequest: Request which has to be executed multiple times(repeating). For example while uploading images, when there are multiple images to be uploaded, periodic work request is fired.
PeriodicWorkRequest.Builder uploadBuilder =
new PeriodicWorkRequest.Builder(UploadWorker.class, 1,
TimeUnit.MINUTES)
.setInputData(createInputData())
.addTag(TAG_OUTPUT)
.setConstraints(myConstraints)
.build();

WorkManager

WorkManager is responsible for spread load of WorkRequests and honoring constraints. It enqueues and manages the WorkRequests. The WorkRequest created in snippet above will be executed only if it has been queued in WorkManager as

// Enqueue Task
WorkManager.getInstance().enqueue(upload);

WorkStatus

WorkStatus has an id associated with WorkRequest and has corresponding states like ENQUED, RUNNING, BLOCKED, SUCCEEDED, FAILED & CANCELED. It is the LiveData (ref Architecture Components) returned by WorkManager.

//to know status of the work request
WorkManager.getInstance().getStatusById(upload.getId())
.observe(lifecycleOwner, workStatus -> {
// Do something with the status
if (workStatus != null && workStatus.getState().isFinished()) { /*...*/ }
});

Advanced Functionality

Cancel task

A task that is enqueued can be cancelled by its work id. Multiple tasks in work sequence or specific tag can also be cancelled together using this functionality. The below snippet shows how a task can be cancelled using a workId.

//to cancel and work request at any point
UUID compressionWorkId = upload.getId();
WorkManager.getInstance().cancelWorkById(compressionWorkId);

Chained Task

We can queue several tasks in particular order which returns a WorkContinuation object that defines a sequence of tasks. One of the major issues that can occur with this approach is if any task returns Worker.WorkerResult.FAILURE, the whole sequence ends. The snippet below shows an example where chaining of WorkRequest is demonstrated. First upload WorkRequest is executed followed by WorkRequest workB and workC

WorkManager.getInstance()
.beginWith(upload)
// Note: WorkManager.beginWith() returns a
// WorkContinuation object; the following calls are
// to WorkContinuation methods
.then(workB) // FYI, then() returns a new WorkContinuation instance
.then(workC)
.enqueue();

Tagged Work

Query and cancel work by tags. The WorkRequest ids are auto-generated and not easily readable. While creating OneTimeWorkRequest or PeriodicWorkRequest we can set a tag using method addTag(TAG_OUTPUT). It becomes really simple to cancel and manage multiple WorkRequests which have a same tag.

Conclusion

If you are familiar with how Evernote’s Android Job works, you will be able easily to relate it to WorkManager. WorkManager comes with an additional Firebase Job Dispatcher use. Constraints, tagging and chained tasks make WorkManager more interesting and useful.

My two cents, if you already have a perfect scheduling mechanism in place for existing project, it’s not advisable to change it. However, if you are starting a new implementation, Jetpack’s WorkManager is definitely the way to go.

In the next part of the series, we will explore Paging, Slices and Android KTX. Stay tuned!

Exploring Navigation in JetPack, read the first part in this series here.

--

--

Neha Nathani
VersionN Studios

Work on #android, #LoveCode, Organizer @ #BlrDroid, #WomenTechMaker Believe in #Karma. Twitter @nehadhanwani