How to use WorkManager with RxJava

Paulina Sadowska
ProAndroidDev
Published in
7 min readApr 10, 2019

--

Work manager is one of the Android Architecture Components. It allows running a deferrable background work which will be executed even after app exits or device restarts.

Since WorkManager is part of Jetpack, it was created with MVVM architecture and LiveData in mind. But what about all of us heavily using RxJava? Should we just accept that WorkManager is not for us?

Of course not! In this article, I’ll show you how to use WorkManager with RxJava and what problems you may encounter. Also, I created the library to help you deal with those problems 💪.

(The library that I created RxWorkManagerObservers allows observing the status of the work enqueued in the WorkManager in a reactive manner. If you’re only interested in the description of the library, you can skip the beginning of this article and go straight to the last section.)

When to use WorkManager?

WorkManager handles background work that is deferrable (might not be executed right away) but needs to be guaranteed to run regardless of if the app exits or device restarts.

WorkManager is definitely not a good choice for fetching data from API and showing it to the user. That task doesn’t need to survive process death because a fetched result will be not useful after the user exits the app. In this case, it’s better to use something else e.g. Kotlin’s coroutines or RxJava.

However, if you want to synchronize some data in the background, send logs or upload photos — WorkManager would be a great choice.

Creating a background task

To define the background task you need to create the class extending the Worker. In the doWork() method specify what should be done and return the result (either success, failure or retry).

The doWork() is run synchronously on the background thread provided by the WorkManager, so in most cases, there is no need to worry about threading here.

Enqueuing the work

To run the work, first, you have to create WorkRequest. For one-off WorkRequests use OneTimeWorkRequestBuilder. It allows creating the work request which will be run one time by the WorkManager.

Once the work request is created it can be scheduled by calling the enqueue method of the WorkManager

Using the above snippet the work request will be scheduled immediately. But WorkManager has much more capabilities than that. You can schedule the task to be run when specific constraints are met (e.g. the phone is charging), create recurring work using PeriodicWorkRequestBuilder or chain multiple work requests. All these features are really awesome and I highly recommend you to read about them in the documentation.

Passing and retrieving data from the worker

Another amazing feature is that WorkManager allows you to pass input data to the worker and return output data from it.

For example, you can create the worker which takes the user name as an input, process the data and returns user identifier as an output.

The input data can be created using workDataOf() method (part of work-runtime-ktx), but you can also use Data.Builder directly. Created data is passed to the OneTimeWorkRequestBuilder usingsetInputData(Data) method.

Creating a reactive worker

If you want to use observables inside your worker, that’s also not a problem. To provide interoperability between WorkManager and RxJava the work-rxjava2 dependency was created by the Android team.

After including androidx.work:work-rxjava2:$work_version in the gradle file, you can create the worker which extends the RxWorker. It’s similar to the regular worker but instead of having doWork() which returns a Result , it has createWork() method returning Single<Result>. So you can easily use reactive functions and operators inside it.

Important notes about RxWorker:

  • ThecreateWork() method is called on the main thread but returned single is subscribed on the background thread.
  • You don’t need to worry about disposing the Observer since RxWorker will dispose it automatically when the work stops.
  • Both returning Single with the value Result.failure() and single with an error will cause the worker to enter the failed state.

Observing the status of your work and retrieving output data

After enqueuing the WorkRequest, WorkManager allows checking its status. This information is stored in a WorkInfo object which holds work id, tags, its current state and the output data if it was already returned.

The diagram below shows possible states of the work and the transitions.

Credits: Working with WorkManager (Android Dev Summit ‘18)

There are three different ways to observe the work:

1You can retrieve the WorkInfo for a specific WorkRequest by its id using the following methods:

  • WorkManager.getWorkInfoByIdLiveData(UUID) —allows observing the WorkInfo using LiveData and reacting to state changes
  • WorkManager.getWorkInfoById(UUID) — returns ListenableFuture<WorkInfo> — aFuture that allows adding the listener which will be notified when the work with the specific id completes.

2You can also retrieve WorkInfo objects for all work requests with the specific tag. The work tag is passed during the work request creation:

Then you can observe the states of the list of WorkRequests which were created with the given tag:

  • using the LiveData<List<WorkInfo>> retrieved by WorkManager.getWorkInfosByTagLiveData(String),

or

  • using the ListenableFuture<List<WorkInfo>> returned by WorkManager.getWorkInfosByTag(String).

3For a unique work there is also a very similar set of methods:

  • WorkManager.getWorkInfosForUniqueWorkLiveData(String) which allows observing WorkRequests state with the LiveData<List<WorkInfo>>.

or

  • WorkManager.getWorkInfosForUniqueWork(String) which returns a ListenableFuture<List<WorkInfo>>.

Unique work is created the same way the regular work but enqueued with the specific id and the existing work policy. More about unique work you can read here.

What about reactive observers?

If you’re RxJava fan like me you may be disappointed that there is no option to observe WorkRequests using an Observable. I’m a huge advocate for making the architecture of the app coherent, so the code is easy to learn and reason about. Because of that, I thought that it may be a problem that you can only use LiveData (or ListenableFuture) to observe the status of the work.

That’s why I created the library allowing to deal with that problem 💪.

What is in this library?

To allow observing enqueued work in a reactive manner I created the set of extension functions to the WorkManager. Each of these functions transforms LiveData to Observable which emits either the Data returned from theWorkRequest or the whole WorkInfo object.

Under the hood, I use the functions of the WorkManager which return LiveData. Retrieved LiveData is observed using observeForever(Observer)which can only be called on the main thread. Because of that, the Observable returned from each of the functions from this library have to be subscribed on the main thread. The actual work, of course, will still be executed on the background thread specified by the WorkManager.

The library does not depend on work-rxjava2 so you don’t have to include it. It doesn’t matter if you use RxWorker, Worker, CoroutineWorker or any other custom ListenableWorker — it works all of them.

Observing the Data by id

If you want to observe specific work request by its id and the only information that you need is whether the work succeeded, failed or got cancelled you can use getWorkDataByIdSingle(UUID) extension function:

If the work completes onSuccess is called with the Data object from the WorkInfo (which may be empty if work doesn’t return it).

If the work fails or gets cancelled the onError is called with either WorkFailedException or WorkCancelledException.

Observing the WorkInfo by id

If you need updates whenever the status of the work changes you can observe it using the following function:

The observable emits the WorkInfo object whenever the status of the work changes. If the work succeeds it emits the value and completes. If it fails or gets cancelled— it emits the WorkInfo value and then error (WorkFailedException or WorkCancelledException).

Observing the Data by tag

You can also observe the work requests with the specific tag using the function:

It emits the Data from the WorkInfo object when some of the WorkRequests completes.

By default, if one of WorkRequests fail or got cancelled, onError is not emitted (but no value will be received from the work). This is because I wanted to allow observe WorkRequests, even after one of the requests fails or got cancelled. However, if you want the Observable to fail if one of the WorkRequests fails — you can do it by setting theignoreError flag to false (it’s true by default).

Observing the unique work

You can also observe the Unique work using the following function:

It behaves just like the observable that is created by the previous method (getWorkDatasByTagObservable), but uses getWorkInfosForUniqueWorkLiveData instead of getWorkInfosByTagLiveData to get the LiveData.

TL;DR

  • Work Manager rocks 🤘
  • You can use Observers in the worker using work-rxjava2 dependency. Then you simply extend RxWorker instead of Worker.
  • By default, it’s only possible to observe the WorkRequests with the LiveData or ListenableFuture.
  • With the RxWorkManagerObservers library, you can observe the WorkRequests in a reactive manner. It transforms LiveData to Observable or Single under the hood.

Thanks in advance for any feedback about this article and how can I further improve this library! 💛

--

--