Working with the WorkManager — Part 2

Vinay
IVYMobility TechBytes
4 min readMar 9, 2022

In the preceding article we looked on how to get our hands dirty with the WorkManager in real-time, paused at OneTimeWorkRequest. Lets now continue with PeriodicWorkRequest, the next big thing in WorkManager API.

PeriodicWorkRequest

It happens, the certainities where work need to be run at regular intervals or say periodically. Like when the app needs to sync at regular intervals to stay afresh of data from the server and stay updated. Thats the area where the PeriodicWorkRequest can play.

Lets say if the app need to sync the data with the server every 15 minutes. As simple as the OneTimeWorkRequest we saw prior, the PeriodicWorkRequest too goes like this.

PeriodicWorkRequest syncRequest =
new PeriodicWorkRequest.Builder(SyncWorker.class, 15, TimeUnit.MINUTES)
// Constraints
.build();

Adding Constraints to PeriodicWorkRequest

Furthermore, if it is required to do at some exact instance of time, then we can add constraints to it as done like in the OneTimeWorkRequest.

Constraints constraints = new Constraints.Builder()
.setRequiredNetworkType(NetworkType.CONNECTED) // Only when the internet connection is available
.build();
PeriodicWorkRequest syncRequest =
new PeriodicWorkRequest.Builder(SyncWorker.class, 15, TimeUnit.MINUTES)
.setConstraints(constraints)
.build();
(or)WorkRequest myWorkRequest =
new PeriodicWorkRequest.Builder(SyncWorker.class, 15, TimeUnit.MINUTES)
.setConstraints(constraints)
.build();

If multiple constraints are specified, the work will run only when all the constraints are met.

The WorkManager will stop the work if in any event while the work is running, a constraint becomes unmet. But the work will then be retried when all the constraints are met.

Now that we’ve learned the types of Work Requests. Lets move on to the advanced level.

Tag Work

Chances are there where we might have to run multiple works in a same app and needed to be operated separately depending upon the requirements. WorkManager API has features to handle that too.

The Tag Work feature available in the WorkManager API comes to aid here. Every work request has a unique identifier, which can be used to identify that work later in order to observe its progress or cancel that work itself. Tagging a work can be simply achieved by adding .addTag() method to the WorkRequest.

WorkRequest uploadWorkRequest =
new OneTimeWorkRequest.Builder(UploadWorker.class)
.addTag("uploadImages")
.build();

So, simply we can get to know the status of the work or cancel it using the mentioned tag like below.

workManager.getWorkInfosByTag("uploadImages");workManager.getStatusesByTag("uploadImages");workManager.cancelAllWorkByTag("uploadImages");

Delayed Work

There might be requirements in such a way that the work not take effect immediately. The system will run the work immediately if the work has no constraints or all the constraints mentioned are met. And if we do not want the work to be run immediately, we can specify the work to start after a minimum initial delay using the setInitialDelay() method.

WorkRequest uploadWorkRequest =
new OneTimeWorkRequest.Builder(UploadWorker.class)
.addTag("uploadImages")
.setInitialDelay(5, TimeUnit.MINUTES)
.build();

Note: One has to keep in mind an important thing if setting an initial delay for a PeriodicWorkRequest. In the case of PeriodicWorkRequest, only the first run of the periodic work would be delayed.

Retry and Backoff Policy

How good would it be to automate a worker to retry itself works! It can be achieved through Result.retry() method from the worker. The work is then rescheduled according to a backoff delay and backoff policy implemented in the worker.

The Backoff delay specifies the minimum amount of time to wait before retrying the work after the first attempt. This value cannot be lesser than 10 seconds (or MIN_BACKOFF_MILLIS).

And the Backoff policy defines how the backoff delay should increase over time for subsequent retry attempts. WorkManager supports 2 backoff policies, LINEAR and EXPONENTIAL where EXPONENTIAL is default with 10 seconds, but the work request configuration can be overridden.

WorkRequest myWorkRequest =
new OneTimeWorkRequest.Builder(UploadWorker.class)
.setBackoffCriteria(
BackoffPolicy.LINEAR,
OneTimeWorkRequest.MIN_BACKOFF_MILLIS,
TimeUnit.MILLISECONDS)
.build();

In the above example, the minimum backoff delay is set to the minimum allowed value, 10 seconds. Since the policy is LINEAR the retry interval will increase by approximately 10 seconds with each new attempt, i.e., the first run finishing with Result.retry() will be attempted again after 10 seconds, followed by 20, 30, 40, and so on, if the work continues to return Result.retry() after subsequent attempts. And if the backoff policy were set to EXPONENTIAL, the retry duration sequence would be like to 20, 40, 80, and so on.

Assign input data

In some cases, the work may require input data in order to do its work. As we mentioned in the previous examples of uploading images, might require the URI of the image to be uploaded as input.

The input values are stored as key-value pairs in a Data object and can be set on work request. WorkManager will deliver the input Data to the work when it executes the work. The input arguments can be accessed by the Worker class by calling Worker.getInputData(). The code below shows how you can create a Worker instance which requires input data and how to send it in your work request.

Below is an example code on how to create an instance of a Worker that requires sending input data.

// Define the Worker requiring input
public class UploadWork extends Worker {
public UploadWork(Context appContext, WorkerParameters workerParams) {
super(appContext, workerParams);
}
@NonNull
@Override
public Result doWork() {
String imageUriInput = getInputData().getString("IMAGE_URI");
if(imageUriInput == null) {
return Result.failure();
}
uploadFile(imageUriInput);
return Result.success();
}
...
}
// Create a WorkRequest for the Worker and sending it input
WorkRequest myUploadWork =
new OneTimeWorkRequest.Builder(UploadWork.class)
.setInputData(
new Data.Builder()
.putString("IMAGE_URI", "http://...")
.build()
)
.build();

Hope we’ve gained a good knowledge in effeciently using the WorkManager API with its several inbuilt features.

In the next article, lets look into work chaining in WorkManager API and lot more.

Until then, happy coding..!

--

--