WorkManager Finally Released!

Here’s how to use it.

Adrian Tache
Android Ideas
6 min readMay 8, 2019

--

After quite a bit of back and forth with its implementation, androidx.work.WorkManager is finally in a state where we can expect no breaking changes which completely redefine the way we use it. For those who aren’t yet familiar with it, WorkManager is used on Android as a bridge to things like JobScheduler or AlarmManager to schedule work that happens regardless if the app is running. It can survive app crashes, force close, even device reboots, and it is a very powerful tool in today’s power limiting Android. You can find the latest release notes here:

♪ work, work, work, work ♪

As such, let’s have a look at how to do some interesting things with WorkManager and how things work now.

Step 1: Decide What Kind of Work You Want To Do

First things first, you need to think of what you expect WorkManager to do. What is the purpose of the work you’re scheduling? Do you need it now or later? Will it ever repeat and how often?

The purpose of the work matters because it affects how you structure it, and whether you even need to use WorkManager . The purpose of this tool is to create work that is guaranteed, but deferrable. This means that you want to make sure that whatever work you give it will happen, but you’re ok with it being delayed, sometimes for a long time due to stuff like Doze. For light workloads, for example, it might just make sense to use a separate thread to perform the work immediately. For a complex recurring workload, you might want to use a OneTimeWorkRequest with an initial delay which triggers a PeriodicWorkRequest if one is not active and accurate so that the work gets performed periodically at a set time.

The first main difference is whether you want a single task to run or a recurring task. If you want a single task you create a OneTimeWorkRequest , whereas if you want a recurring task you create a PeriodicWorkRequest and you define a repeatInterval when building it. Please note that for a PeriodicWorkRequest the minimum interval is 15 minutes. Please also note that the PeriodicWorkRequest has a number of limitations for some reason, such as not allowing you to setInitialDelay (fixed in the next version thanks to our complaining, probably 2.1.0-alpha02) and not allowing you to chain together WorkRequests after the work is complete.

The second difference is whether you want the task to run immediately or not. If not, you can define a delay before it runs with setInitialDelay .

The third difference is whether you want to send any data to the work, in which case you define a Data object and add it to the WorkRequest with setInputData . Please note there is a 10KB limit for Data objects, for some reason.

Finally, you can add a number of Constraints to prevent the work from running unless certain conditions are met. You can create them with a Constraints.Builder() and apply them to the work using setConstraints . The possible constraints are:

  • getRequiredNetworkType() to only run based on NetworkType
  • requiresBatteryNotLow() to only run when battery is “not low”
  • requiresCharging() to only run when device is charging
  • requiresDeviceIdle() to only run when device is idle
  • requiresStorageNotLow() to only run when storage is “not low”

To help manage the work you can also use addTag to add a String tag to the work you schedule, which you can then use to check its status or cancel it.

Step 2: Let’s Build a Worker

To start, we need to define the work that will be performed. In order to do this, we create a class which extends Worker and implements doWork :

public class MyWorker extends Worker {
public MyWorker(Context context, WorkerParameters wp) {
super(context, wp);
}

@NonNull
@Override
public Result doWork() {
//Do some work here.

//You can return Result.failure() to indicate work failed
// or Result.retry() to retry the work.
return Result.success();
//You can also pass a Data object to send results (10KB max)
// Data data = new Data.Builder().putString(tag,"").build();
// return Result.success(data);
}
}

This will include what work the Worker actually performs and it can even instantiate new Workers , although that can be tricky to debug.

Step 3: Let’s Build a WorkRequest

Once we have the worker we need to define the characteristics of the WorkRequest to indicate various options we want to choose. For example, a OneTimeWorkRequest may look like this:

//create a Data object to pass to the work
Data string = new Data.Builder().putString(tag, "tag").build();

//create constraints for the work
Constraints constraints = new Constraints.Builder()
.setRequiredNetworkType(NetworkType.CONNECTED)
.build();

//create the actual WorkRequest calling the Worker above
OneTimeWorkRequest work = new OneTimeWorkRequest
.Builder(MyWorker.class)
.setInitialDelay(1000, TimeUnit.MILLISECONDS)
.setInputData(string)
.setConstraints(constraints)
.addTag("workTag")
.build();

In this WorkRequest we create some data to pass to the Worker (in my real example a URL to fetch), some constraints to block execution if, in this case, the phone is not connected to the Internet, and then set a delay on execution (in my real example a delay to sometime at night to perform the work when the phone is most likely idle), and add a tag to the work so we can monitor or cancel it easily in the future. Most of these options are, of course, optional, and you can choose whichever combination works for your work needs.

A similar PeriodicWorkRequest would look like this:

PeriodicWorkRequest work = new PeriodicWorkRequest
.Builder(MyWorker.class, 24, TimeUnit.HOURS)
.setInputData(string)
.setConstraints(constraints)
.addTag("workTag")
.build();

The only differences are that the Builder requires a period to be defined, in this case every 24 hours, and we cannot yet use setInitialDelay with PeriodicWorkRequests .

Step 4: Let’s Enqueue the WorkRequest

We now have all the pieces of the puzzle so we can actually tell Android to run our WorkRequest , either as soon as possible or based on the constraints and delay we specified. To do this all we need is to get a WorkManager instance and enqueue the work like this:

WorkManager.getInstance(this).enqueue(work);

Please note that as of WorkManager 2.1.0-alpha01 getInstance needs to be provided a context and WorkManager.getInstance() is deprecated.

And that’s it! Provided you didn’t set any constraints, the work will be executed at the first available moment!

Bonus Step: Check Work Status

While I don’t feel it’s the best implementation, you also have the ability to monitor the status of WorkRequests . I have found the simplest way to do this was to create a custom Observer and observe the LiveData we get from WorkManager in onCreate() , which is a List of WorkInfo . In the example below, my observer gets details on the currently scheduled work and prints them to logcat.

// Create the observer which fetches WorkManager status.
final Observer<List<WorkInfo>> observer = list ->
{
StringBuilder sb = new StringBuilder();
if (list != null) {
int i = 0;

for (WorkInfo w : list) {
sb.append(" \n");
sb.append(i++);
sb.append(". ");

sb.append(w.getId());
sb.append(" ");
sb.append(w.getTags());
sb.append(" ");
sb.append(w.getState());
sb.append(" ");
}
}

Log.i("WRK status", sb.toString());
};
// Observe the LiveData, passing in this activity as the
// LifecycleOwner and the observer.
WorkManager.getInstance()
.getWorkInfosByTagLiveData(tag)
.observe(this, observer);

You can use the Observer to run code once the work has finished, like the code below that goes into the for loop. I’m using this in the events app to refresh the display once a forced task finishes.

if (w.getState() == WorkInfo.State.SUCCEEDED) {
//do stuff
}

You can also use this to get the result of the work if passed on as Data in Result.success(data) by using w.getOutputData.

Further Reading

To read more about WorkManager I highly recommend the official Google guides:

This video announcing the release of WorkManager also has a number of interesting links in the description:

And, of course, my own article on using WorkManager to schedule notifications in the future. I use a OneTimeWorkRequest with a known initial delay to schedule a notification in the day of each event at noon:

Thanks for reading this article. You can connect with me on LinkedIn.

I’m currently looking for a job in the Geneva area, Switzerland, so please contact me if you know someone in need of an Experienced Junior Developer with Java (Android) and JavaScript (React Native Android/iOS) experience.

If you liked this article, please hit the clap icon 👏 to show your support.

--

--