Workout your tasks with WorkManager — Advanced Topics
“WorkManager is a library for managing deferrable and guaranteed background work.”
In my previous two posts about WorkManage I covered topics like:
- Android memory model
- Android battery optimizations
- Current background processing solutions
- Where is WorkManager placed in the background work schema
- WorkManager components: Worker, WorkRequest and WorkManager
- Constraints
Input/Output Data
In this blog post I’ll cover some extra features of the WorkManager library like:
- how to identify a task
- how to get the status of a task
- BackoffPolicy
- how to combine the tasks and the graphs of tasks (chaining the work)
- how to merge the inputs and outputs
- what are the Threading options in WorkManager
1️⃣ Identify a task
After a task (work) was created we will be interested in knowing the status of it, but in order to obtain this objective we should have some mechanisms that could be used to identify the task (work). There are 3 main ways that could be used to identify the work:
- Unique id (UUID): the id associated to the WorkRequest is generated by the library and it is not developer-friendly
- Tag : a task could contain many tags
- Unique name : a task could have only one unique name
2️⃣ Get the status of a task
By having the possibility to identify a task we are able to know more about its status by using LiveData or we also have the possibility to cancel it.
WorkManager & LiveData = ❤️
*Cancel a task * ❌
3️⃣ WorkManager Policies
❗ Existing Work Policy enums
- KEEP — keeps the existing unfinished WorkRequest. Enqueues it if one does not already exist.
- REPLACE — always replace the WorkRequest. Cancels and deletes the old one, if it exists.
- APPEND — appends work to an existing chain or create a new chain.
KEEP + REPLACE + APPEND = ExistingWorkPolicy
KEEP + REPLACE = ExistingPeriodicWorkPolicy
❗ BackoffPolicy enum
EXPONENTIAL — Used to indicate that WorkManager should increase the backoff time exponentially
LINEAR — Used to indicate that WorkManager should increase the backoff time linearly
For a BackoffPolicy of 15 seconds, will be as next:
- For linear: work start time + (15 * run attempt count)
- For exponential: work start time + Math.scalb(15, run attempt count — 1)
The work start time , is when the work was first executed (the 1st run attempt).
Run attempt count is how many times the WorkManager has tried to execute an specific Work.
Also note that the maximum delay will be capped at WorkRequest.MAX_BACKOFF_MILLIS and take into consideration that a retry will only happen if returning WorkerResult.RETRY
4️⃣ Chaining work
Sometimes it is necessary to run some tasks in parallel or to chain them one after another one, or even to create groups of tasks chained or in parallel. These features are available also in WorkManager library.
The code for the previous scheme looks like this:
5️⃣ Merge inputs and outputs
Like we already saw when we chain the work the outputs are inputs of the tasks, but they should be merged somehow in order to get the correct data. To do the merge we have 2 main strategies in place that are actually the provided implementations of the abstract class InputMerger:
6️⃣ Threading options in WorkManager
- ListenableWorker
- Worker
- CoroutineWorker
- RxWorker
- Our own implementation :)
🧵 ListenableWorker
Overview
- A ListenableWorker only signals when the work should start and stop
- The start work signal is invoked on the main thread, so we go to a background thread of our choice manually
- A ListenableFuture is a lightweight interface: it is a Future that provides functionality for attaching listeners and propagating exceptions
Stop work
- It is always cancelled when the work is expected to stop. Use a CallbackToFutureAdapter to add a cancellation listener
🧵 Worker
Overview
- Worker.doWork() is called on a background thread, synchronously
- The background thread comes from the Executor specified in WorkManager’s Configuration, but it could also be customised
Stop work
- Worker.onStopped() is called. This method could be overridden or we could call Worker.isStopped() to checkpoint the code and free up resources when necessary
🧵 CoroutineWorker
Overview
- For Kotlin users, WorkManager provides first-class support for coroutines
- Instead of extending Worker, we should extend CoroutineWorker
- CoroutineWorker.doWork() is a suspending function
- The code runs on Dispatchers.Default, not on Executor (customisation by using CoroutineContext)
Stop work
- CoroutineWorkers handle stoppages automatically by cancelling the coroutine and propagating the cancellation signals
🧵 RxWorker
Overview
- For RxJava2 users, WorkManager provides interoperability
- Instead of extending Worker, we should extend RxWorker
- RxWorker.createWork() method returns a Single indicating the Result of the execution, and it is called on the main thread, but the return value is subscribed on a background thread by default. Override RxWorker.getBackgroundScheduler() to change the subscribing thread.
Stop work
- Done by default
🎉WorkManager — Recap
- WorkManager is a wrapper for the existing background processing solutions
- Create one time or periodic work requests
- Identify our tasks by using ids, tags and unique names
- Add constraint, delay and retry policy
- Use input/output data and merge them
- Create chains of tasks
- Use the available threading options or create your own
That’s all folks! 🐰 Enjoy and feel free to leave a comment if something is not clear or if you have questions. And if you like it please 👏 and share !
Thank you for reading! 🙌🙏😍✌
Top comments (0)