With latest Google I/O came a few changes to Android. All of this deserves separate write-up but for now let’s focus on one cool thing - WorkManager.

TL;DR WorkManager looks awesome and you should use it as soon as it stable version is released

To shortly introduce WorkManager, I will just cite official docs:

WorkManager is a library used to enqueue work that is guaranteed to execute after its constraints are met. WorkManager allows observation of work status and the ability to create complex chains of work.1

WorkManager is intended for tasks that require a guarantee that the system will run them even if the app exits.2

Difference between WorkManager, JobScheduler and Firebase JobDispatcher

If you remember there is JobScheduler already in Android, which surprisingly is made for… scheduling jobs! What is cool about it, you can set constraint e.g. to do some work only when internet connection is available. API is pretty simple. So it looks similar to WorkManager, right?

On the surface, yes. Problem? It’s available since Android 5.0 (API 21) only. So if you want to target older devices (and you probably want) you need to:

a) support two cases, one for newest phones with JobScheduler and second with alternative method (e.g. AlarmManager or Google Play services scheduling)

b) use Firebase JobDispatcher which uses Google Play Services scheduling and because of that it support all devices from Gingerbread (API 14).

And when you are thinking if you want to make your app depend on Google Play Services… that’s where WorkManager enters.

Big entrance

WorkManager is part of AndroidX (former app-compat) libraries. It combines best of both worlds as it uses the best method available for current API. If JobScheduler is available, it will use it. If not it will check for Firebase JobDispatcher availability and try to use it. Otherwise, it will fallback to AlarmManager and BroadcastReceivers.

Below is schema of WorkManager operation from this presentation at Google I/O.

WorkManager under the hood

So… is it any better than Firebase JobDispatcher?

It supports native JobScheduler when it is available, which sounds like a good thing to do. Internally WorkManager will use the best tool available to schedule jobs.

All of Firebase JobDispatcher, JobScheduler and WorkManager have ability to run tasks only under certain conditions like: network connection available, device is charging. All of them also allow to easily schedule periodic (minimum period is 15 minutes for WorkManager) jobs and cancel them.

But WorkManager can do a bit more.

First of all, it integrates with LiveData from Architecture Components, so you can observe its status and output data in a simple manner:

workManager.getStatusById(workRequest.id)
        .observe(lifecycleOwner, Observer { workStatus: WorkStatus ->
            if (workStatus != null && workStatus.state.isFinished) {
                process(workStatus.outputData)
            }
        })

Where WorkManager really shines is its ability to chain and combine tasks. It reminds to me of RxJava operators, where you can combine tasks however you want and create complicated task graphs with ease. That is the biggest advantage of RxJava and now similar thing coming ‘natively’ to Android… I’m impressed. You might have one task dependent on another, do some tasks simultaneously and combine their output etc. Almost sky is the limit. I won’t go into details as official docs are really good at explaining that.

Finally, there is also a matter of exchanging data between app and Worker. Interprocess communication is hard so there is no simple way to pass objects as they are, but we can pass key-value pairs which is very similar to Bundle. Setting input data is as simple as:

val inputData = mapOf("inputMessage" to "this is input message").toWorkData()
val workRequest = PeriodicWorkRequestBuilder<ProcessDataWorker>(15, MINUTES)
        .setInputData(inputData)
        .build()

class ProcessDataWorker : Worker() {
    override fun doWork(): WorkerResult {
        inputData.getString("inputMessage", "defaultInputMessage")
        return WorkerResult.SUCCESS
    }
}

and setting outputData:

class ProcessDataWorker : Worker() {
    override fun doWork(): WorkerResult {
        outputData = mapOf("outputMessage" to "this is output message").toWorkData()
        return WorkerResult.SUCCESS
    }
}

workManager.getStatusById(workRequest.id)
    .observe(this, Observer { workStatus ->
        if (workStatus != null && workStatus.state.isFinished) {
            val outputMessage = workStatus.outputData.getString("outputMessage", "defaultValue")
            Log.i(TAG, "Output message: $outputMessage")
        }
    })

This process is made really simple. We lose type safety to some extent but hey, nothing is perfect.

Bonus: there is also module with test helpers, that might be useful when checking if app behaves correctly with given constraints and given time: androidTestImplementation "android.arch.work:work-testing:$version"

Just use it

Currently I see no reason why someone might want to NOT use WorkManager.

Ok, if you are satisfied with your current solution and don’t need that fancy LiveData cooperation and composable task - there is no reason why you should switch.

But at the same time WorkManager is integrated with system and handles all its intricacies. Its API is simple, tasks are composable in many ways, passing data is not complicated. Unless some issues arise when more people start to use it, this should be the default option on Android.