Schedule task with WorkManager: An Android Jetpack Library
Google released Android Jetpack in I/O 2018. WorkManager is part of Android Jetpack and an Architecture Component for scheduling the background task that guaranteed execution even if you navigate away from your app. It is much more useful api for scheduling the task and you will no more think or worry about what logic we should implement in different cases while scheduling task(periodic or one time). WorkManager will do that for you. These APIs let you create a task and hand it off to WorkManager to run immediately or at an appropriate time.
Let us come back to WorkManager Api , We had to think a lot while scheduling background task. Sometimes we use Thread, JobScheduler, FirebaseJobDispatcher, AlarmManager and Services depending upon our requirements. But there were a lot to think about for example if we want to schedule task with JobScheduler then it works only on or above api level 21, then we came with FirebaseJobDispatcher which works below api level 21 as well but this library uses the scheduling engine inside Google play service(formerly the GCM Network Manager component) to provide a backwards compatible JobScheduler like API. Google play services was required for this but you can’t force user to download this if google play in unavailable in device as many devices don’t have play service. Finally Google introduced WorkManger Api to get rid of thinking all these situation using previous api’s just hand over the task to WorkManager, it will do that for you no need to think. Have a look on attached image below you will have to think a lot of logic here.
Now comes to concept of WorkManager api how it actually work and what classes and concept it deal with.
Classes and concepts
The WorkManager API uses several different classes.
The most important classes are:
- Worker : specifies what task you need to perform. This is the class that does the background work. This class is where you will write your business logic.We have to write our logic inside doWork() of Worker class. You see doWork() method have return type WorkerResult. We will have to return Success if everything goes well and Failure if any error occurred and one more flag is Retry.
- WorkRequest : This class actually schedules your work request and make it run. It represents an individual task. At a minimum, a workRequest object specifies which Worker class should perform the task. You can schedule your request by using two of its subclasses OneTimeWorkRequest (Things which can only be done once) and PeriodWorkRequest(For recurring work) depend upon your requirements. Every request made by using WorkRequest has an autogenerated unique ID, you can use this id to get task state or to cancel the task.
- WorkReuest.Builder : It is a helper class which is used for creating object of WorkRequest. You will have to use OneTimeWorkRequest.Builder or PeriodicWorkRequest.Builder according to your WorkRequest.
- Constraints : It specifies when the task should run for example if you want to run your task when connected to network or device is plugged-in etc. We will have to create a Constraints object using Constraints.Builder and pass it to WorkRequest.Builder before crating WorkReuest.
- WorkManager : It enqueues and manages the work requests. You pass your workRequest object to WorkManager to enqueue the task. It will schedules the task in such a way as to spread out the load on system resources, while honoring the constraints you specify.
- WorkStatus : It contains all information about a particular task. You can get the status of any using the unique request id. WorkManager provides a LiveData for each WorkRequest object and this LiveData will hold a WorkStatus for each WorkRequest so you can easily determine current status of a task by observing livedata and can also get any returned value after finishing the task.
Let us start with the code how we will implement WorkManager Api and what kind of task it deal with. First of all we will add dependency in our app gradle.
implementation "android.arch.work:work-runtime:1.0.0-alpha05"
Create your own worker class (I used MyWorker) by extending Worker class and implement doWork() method, you can write your logic in this overridden method.
public class MyWorker extends Worker {@NonNull
@Override
public Result doWork() {
//write your logic here to do the background work return Result.SUCCESS;
}
}
Here we are returning Result.SUCCESS, you can also use FAILURE and RETRY as per your requirements.
Note: Returning RETRY tells WorkManager to try this task again and FAILURE means not to try task again.
Now we will have to make work request then call the WorkManager in order to queue up the work you want to execute. First i will show you a OneTimeWorkRequest and there were no constraints specified because in this case the WorkManager runs the task immediately.
OneTimeWorkRequest workRequest = new OneTimeWorkRequest.Builder(MyWorker.class).build();WorkManager.getInstance().enqueue(workRequest);
In our activity/fragment first we create OneTimeWorkRequest object using OneTimeWorkRequest.Builder and we will bind our Worker Class (here MyWorker.class) than we use WorkManager to enqueue our request by passing OneTimeWorkRequestobject in WorkManager.enqueue().
If we wanted to, we could limit our WorkManager to run our request one time only if we have a network connection and our device is currently charging. Than we will use Constraints object, see code below how we will use constraints.
Constraints constraints = new Constraints.Builder().setRequiredNetworkType(NetworkType.CONNECTED).setRequiresCharging(true).build();OneTimeWorkRequest workRequest = new OneTimeWorkRequest.Builder(MyWorker.class).setConstraints(constraints).build();WorkManager.getInstance().enqueue(workRequest);
In this case our request will run once as soon as both Network Connected and Charging condition meet.
If want to repeat your task periodically after some specific time then You will have to change workReuest and we will use PeriodicWorkRequest object and then enqueue our task using WorkManager by passing PeriodicWorkRequest object in WorkManager.enqueue(). See the code implementation below.
PeriodicWorkRequest workRequest = new PeriodicWorkRequest.Builder(MyWorker.class, 1, TimeUnit.HOURS).build();WorkManager.getInstance().enqueue(workRequest);
Here you can specify your time and doWork() method of Worker Class (here MyWorker.class) will call in the given interval just like you used to work with alarm manager. The wonderful thing about this API is that you don’t have to write any device logic or worry about selecting the best API for running these tasks. WorkManager has been doing all of that work for you!!!
We can also add Constraints with PeriodicWorkRequest, see the code implementation below.
Constraints constraints = new Constraints.Builder().setRequiredNetworkType(NetworkType.CONNECTED).build();
PeriodicWorkRequest workRequest = new PeriodicWorkRequest.Builder(MyWorker.class, 1, TimeUnit.HOURS).setConstraints(constraints).build();
WorkManager.getInstance().enqueue(workRequest);
You can also cancel a task after you enqueue it. To cancel the task, you need its work ID, which you can get from the WorkRequest object. For example, the following code cancels the workRequest from the previous section:
UUID myWorkId = workRequest.getId();
WorkManager.getInstance().cancelWorkById(myWorkId);
Note: WorkManager makes its best effort to cancel the task, but this is inherently uncertain--the task may already be running or finished when you attempt to cancel it. WorkManager also provides methods to cancel all tasks in a uinque work sequence, or all tasks with a specified tag, also on a best-effort basis.
WorkManager don’t stop here you can avail more feature.
Like if you wanted to run multiple tasks that are doing background work, we can easily chain together those tasks by creating a sequence of OneTimeRequest with our WorkManager. See the code implementation below
WorkManager.getInstance()
.beginWith(workRequest1)
.then(workRequest2)
.then(workRequest3)
.enqueue();
here we are making a chain of background task it will start from workRequest1(An OneTimeRequest object) then will execute 2nd work request(workRequest2) and so on WorkManager will enqueue your request using WorkManager.enqueue() and they will execute one after another.
Note: One problem here is that if at any point one task fails(means If any task returns Worker.Result.Failure) then the whole sequence will end.
You can also pass multiple OneTimeRequest objects to any of the beginWith() and .then() calls. If you pass several OneTimeRequest objects to a single method call, the WorkManager runs all of those tasks (in parallel) before it runs the rest of the sequence.
For example:See the code implementation below
WorkManager.getInstance()
.beginWith(workRequest1, workRequest2, workRequest3)
.then(workRequest4)
.then(workRequest5, workRequest6)
.enqueue();
First WorkManager run workRequest1, workRequest2, workRequest3 all in parallel and when all these task will finish then it run single task workRequest4 after this workRequest5 and workRequest6 task will run in any order.
You can join multiple chain by using WorkContinuation.combine() methods to create complex sequence.
To set up this sequence, create two separate chains, then join them together into a third one. See the code implementation below
WorkContinuation chain1 = WorkManager.getInstance()
.beginWith(workReuestA)
.then(workReuestB);
WorkContinuation chain2 = WorkManager.getInstance()
.beginWith(workReuestC)
.then(workReuestD);
WorkContinuation chain3 = WorkContinuation
.combine(chain1, chain2)
.then(workReuestE);
chain3.enqueue();
Here WorkManager runs workReuestA before workReuestB and it also run workReuestC before workReuestD after finishing both workRequestB and workRequestD(means completing chain1 and chain2) WorkManager start running workReuestE.
Note: While WorkManager runs each of the subchains in order, there is no guarantee about how the tasks in chain1 might overlap with those in chain2. For example, workReuestB might run before or after workReuestC, or they might run at the same time. The only promise is that the tasks within each subchain will run in order; that is, workReuestB does not start until after workReuestB has finished.
One more feature of WorkManager is tagged work. You can group your tasks logically by assigning a tag string to any WorkRequest object. To set a tag, call WorkRequest.Builder.addTag(), see code implementation below:
OneTimeWorkRequest workRequest =
new OneTimeWorkRequest.Builder(MyWorker.class)
.setConstraints(constraints)
.addTag("myRequest")
.build();
Note: WorkManager provides several utility methods that let you operate on all tasks with a particular tag. For example,WorkManager.cancelAllWorkByTag() cancels all tasks with a particular tag, and WorkManager.getStatusesByTag() returns a list of all the WorkStatus for all tasks with that tag.
That was all about this article, give a clap if you like the article. You can refer to more article for reference.
- https://developer.android.com/topic/libraries/architecture/workmanager#unique
- https://developer.android.com/jetpack/
Happy Coding…
Thanks & Regards