[JET ROARING] LYLA FUJIWARA: Your app won’t always be in the foreground. But you still need to do important background work like downloading updates and syncing with your server. Now, there are many existing APIs for this, each with their own particular use cases. Unfortunately, when these APIs are used incorrectly, we see this happen. To save power for users, Android has introduced many battery-saving features in recent releases.
These features manage and balance the power usage of apps. As a developer, you need to work with these battery-saving features to ensure your background tasks run across API levels. This adds code complexity. But if you don’t do this properly, you risk your background work not running for all users. This is where the WorkManager library comes in. WorkManager provides a unified solution for background work, taking Android’s power-saving features and the user’s API level into account. It’s backwards compatible to API level 14, part of Android Jetpack, and it runs with or without Google Play services. These features and more make it the recommended solution for most background work on Android.
There are two key attributes that make a task suitable for WorkManager– first, that the task is deferrable, and second, that the task needs to be guaranteed to run. Deferrable work is any task which is still useful even if it’s not run immediately. So sending analytic data to your server is totally deferrable work. But sending an instant message is not deferrable. Guaranteed work means that the task will run even if the app process is killed or the device restarts. So a great use case for WorkManager would be backing up pictures to a server. This work can be deferred, but it should also be guaranteed to run. WorkManager can be paired with other APIs, like Firebase Cloud Messaging, to trigger background work. So for example, when there is data ready to sync on your server, you can use an FCM message to notify your app and then do the actual syncing with WorkManager. While WorkManager is powerful, background work isn’t a one-size-fits-all approach. So for example, while I’ve been talking a lot about background work, what I don’t mean is background threading. WorkManager works in conjunction with, but is not a replacement for, Kotlin Coroutines, threadpools, or libraries like RxJava. WorkManager is also not designed to trigger work at an exact time. For that, you’re going to need to use an API like AlarmManager or start a foreground service if your users expect your tasks to happen immediately. For a complete view of modern background execution, check out this blog post on the “Android Developer” blog which I have linked below. Now, let me show you how to actually create and do some work. Let’s say that I want to upload a photo using WorkManager. First, you define what your task does using a worker, like this one. All the code goes into the doWork method. Then you make a request to do this work, using a class called WorkRequest. As part of the WorkRequest, you could add constraints, specify the input, and choose to run the work once or periodically. The WorkRequest is basically a class for configuring when and how your task will be run. Since this is non-repeating work, I’m going to go ahead and use a OneTimeWorkRequest. I’ve added a constraint here that it should only do this upload when there’s actually network. Finally, this passes in the URI of the photo as input data, using WorkManager’s Data Object. Once you have a WorkRequest, you can enqueue it by calling WorkManager.enqueue. And well, that’s it. At this point, I know that this photo will be uploaded at some point in the future when the network constraint is met, even if the app is closed or the phone restarts. Now, let’s say that I want the option of updating the UI when my work finishes. I can use the method getWorkInfoByIdLiveData to get a WorkInfo LiveData. The fact that the WorkInfo is wrapped by LiveData makes it observable. If you haven’t used LiveData before, you can read about it in the documentation linked below. WorkInfo has all of the information about the current state of my work, including the status of the work and the optional output. So back in doWork, I could return an output and then add this LiveData observation code to my UI to display the output when available. So what’s going on behind the scenes when you enqueue work? As mentioned, WorkManager is backwards compatible to API level 14 and runs without Google Play services. To ensure this, WorkManager chooses between JobScheduler and a combination of AlarmManager and broadcast receivers when running. To guarantee that the work will keep running, all of that information about enqueued work is kept in a WorkManager-managed database so that it can be resumed if it’s ever stopped. Now, by default, Worker does work off of the main thread, using an executor behind the scenes. If you want to handle threading in some other way, there’s RxWorker and CoroutineWorker, which are available out of the box. Or for even more control, you can make your own worker class by extending ListenableWorker and defining your exact threading strategy. One area where WorkManager truly shines is when you create chains of dependent work. Let’s say that I want to add a filter to multiple pictures, compress those pictures together, and then batch upload the compressed file to share with the world. Assuming you created all of your work requests with the appropriate constraints, the code to create this dependent chain of work looks like this. I enqueued the WorkRequests in the order that I want them to run. I’ve also enqueued these filter image WorkRequests as a list, which causes them to run in parallel. WorkManager will take care of passing through the output and input in a chain. So here, the output of these filter WorkRequests will be passed as the input of the compressed WorkRequest. There is a ton more that you could do with WorkManager. You can specify things like periodic work for work that needs to run repeatedly, unique work, tagged work so that you can query or cancel related work, and backoff policies for retrying work. For even more features, tips, and tricks, check out the “Working with Work Manager” talk linked below. So to summarize, by using WorkManager, you can enqueue one-off and periodic tasks with constraints, chain tasks with input and output, and run these chained tasks in parallel or sequentially, display the state of your tasks using observation, and customize WorkManager’s threading strategy. WorkManager does all of this while handling complex compatibility issues, following system health best practices, and guaranteeing your work will run. If you’re ready to get working with WorkManager, I’ve linked tons of helpful documentation, code labs, presentations, and blog posts below. Happy coding. [JET ROARING]