Mallow's Blog

WorkManager – Schedule background tasks in Android

In Android development, to schedule a work we are using job scheduler and other background processing types. Now we can use work manager to run the background processes. WorkManager is one of the Android Architecture Components and  part of Android jetpack,a new and preferable approach to take on how to build modern Android application.

Adding WorkManager dependency:

To use the work manager, we have to add the below code snippet to build.gradle file and we must add the Google Maven Repository to your project.

dependencies {
def work_version = “2.2.0”
// (Java only)
implementation “android.work:work-runtime:$work_version”
// (Kotlin + coroutines)
implementation “android.work:work-runtime-ktx:$work_version”
}


Cases for using WorkManager:

i) Should the task be completed now?

Should the task be completed now even the application is killed?
For example, in a notes taking application the data needs to be synced with server once user completes and this must be done even if the application is killed.

ii) Should the task be completed later?

Should the task be completed later even after the application killed?

In both of the above cases, we can go with work manager. It will ensure to complete the task.

Implementation:

We need to create a custom class for work manager to define our work.Here the UploadWorker class overrides the worker class and doWork() does the job.

        class UploadWorker(appContext: Context, workerParams: WorkerParameters) 
                            : Worker(appContext, workerParams) {
                 override fun doWork(): Result {
                     try {
                             // Get the input
                             val imageUriInput = inputData.getString(Constants.KEY_IMAGE_URI)
                             // Do the work
                             val response = upload(imageUriInput)
                             // Create the output of the work
                             val imageResponse = response.body()
                             val imgLink = imageResponse.data.link
                             // workDataOf (part of KTX) converts a list of pairs to a [Data] object.
                             val outputData = workDataOf(Constants.KEY_IMAGE_URI to imgLink)
                             return Result.success(outputData)
                     } catch (e: Exception) {
                     return Result.failure()
                     }
             }
                fun upload(imageUri: String): Response {
                     // Webservice request code here; note this would need to be run
                     // synchronously for reasons explained below.
                 }
         }


The input and outputs passes as Data class. In data object, we can pass the limited size of data. This is set by MAX_DATA_BYTES.If the data exists we can use database or something else to pass data.

In WorkManager we can return the result in two ways – Result.success() and Result.failure(). 

There is also an option for Result.retry() option to retry your work on later time. 

Creating WorkRequest:

There are two kinds of works that can be called to make a work request. We can use OneTimeWorkRequest and  PeriodicWorkRequest.

        // workDataOf converts a list of pairs to a [Data] object.
        val imageData = workDataOf(Constants.KEY_IMAGE_URI to imageUriString)
        val uploadWorkRequest = OneTimeWorkRequestBuilder<UploadWorker>()
              .setInputData(imageData)
                .build() 


We can also add the constraints for work request, for example this work should start when the device has network connection.

        val constraints = Constraints.Builder()
                .setRequiredNetworkType(NetworkType.CONNECTED)
                .build() 


There are some other constraints are available to use with work request. 

val constraints = Constraints.Builder()
                .setRequiredNetworkType(NetworkType.CONNECTED)
                .setRequiresCharging(true)
                .setRequiresBatteryNotLow(true)
                .setRequiresDeviceIdle(true)
                .build() 


After adding the constraints we can enqueue our work.

        WorkManager.getInstance().enqueue(uploadWorkRequest) 


Chain our work:

If multiple works has to be finished one by one, we can chain the tasks in work manager.

        WorkManager.getInstance(myContext) 
            // Candidates to run in parallel
            .beginWith(Arrays.asList(filter1, filter2, filter3))
            // Dependent work (only runs after all previous work in chain)
            .then(compress)
            .then(upload)
            // Don't forget to enqueue()
            .enqueue();
 Sequence and parallel 


Chaining work status:

When multiple works are chained together and enqueued in OneTimeWorkRequest, and any one of the work has failed then all works will be marked as FAILED.

Also if any parent work request is cancelled, then the child work requests will also be marked as cancelled.

Types of chaining:

  • Sequencial chaining:

Its a simple chaining, when one worker completes the work then next worker starts the work. First worker’s output may be passed as input for second worker.

  • Parallel chaining:

In this type of chaining we can run some parallel task.When this parallel task completes its execution, then the next one starts execution.

  • Multiple parallel chaining:

WorkManager gives us the ability to execute multiple chains parallel with the use of WorkContinuation class.

Observing work status:

After enqueuing work, we can observe the status of work. This work status is available in WorkInfo class. We can retrieve the work info by work request id.

WorkManager.getInstance(myContext).getWorkInfoByIdLiveData(uploadWorkRequest.id)
                .observe(lifecycleOwner, Observer { workInfo ->
            if (workInfo != null &amp;&amp; workInfo.state == WorkInfo.State.SUCCEEDED) {
                displayMessage("Work finished!")
            }
        }) 


Cancelling WorkRequest:

If  any work isn’t needed anymore we can cancel it by its id.

        WorkManager.cancelWorkById(workRequest.id) 


Conclusion:

In this blog, we have seen how to implement the WorkManager and different cases in it. WorkManager is now in stable release and available in Android jetpack components.

– Selvaraj V,
Android Team,
Mallow technologies.

1 Comment

  1. ExoRank

    Awesome post! Keep up the great work! 🙂

Leave a Reply

%d bloggers like this: