Integrating Work Manager - Android Tutorial

Integrating Work Manager - Android Tutorial

Running background task was a very tough task in Android. Working with Alarm Manager, Firebase JobDispatcher, or Job Scheduler didn't guarantee the execution of the task in Android. This was the major problem, right?

Work Manager solved this issue for us.

What is Work Manager?

Work Manager is a library part of Android Jetpack which makes it easy to schedule deferrable, asynchronous tasks that are expected to run even if the app exits or device restarts i.e. even your app restarts due to any issue Work Manager makes sure the scheduled task executes again. Isn't that great?

In this tutorial, we will talk about how to integrate the work manager in your project and much more advanced features and customization which makes your life easy in scheduling tasks.

So, Let's start.

To Integrate work manager in your project,

dependencies {
  def work_version = "2.2.0"
    implementation "androidx.work:work-runtime:$work_version"
  }

Now, as a next step, we will create a Worker class. Worker class is responsible to perform work synchronously on a background thread provided by the work manager.

class YourWorkerClass(appContext: Context, workerParams: WorkerParameters): Worker(appContext, workerParams) {

    override fun doWork(): Result {
        // Your work here.

        // Your task result
        return Result.success()
    }
}

In this above class,

  • doWork() method is responsible to execute your task on the background thread. Whatever task you want to perform has to be written here.
  • Result returns the status of the work done in doWork() method. If it returns Result.success() it means the task was successful if the status is Result.failure() , the task was not-successful and lastly, if it returns Result.retry() it means the task will execute again after some time.
Now, let's configure how to execute the task

We need to create a WorkRequest which defines how and when work should be run. It has two types,

  • OneTimeWorkRequest - Runs the task only once
  • PeriodicWorkTimeRequest - Runs the task after a certain time interval.
val yourWorkRequest = OneTimeWorkRequestBuilder<YourWorkerClass>().build()

and once we have defined our work request, we can just schedule the task using,

WorkManager.getInstance(context).enqueue(yourWorkRequest)

That is it, this is how you can schedule your task using Work Manager. This is simple right?

Now, let's talk about the customization we can do in our task execution.

We can add specific constraints in our WorkRequest to customize it. To add constraints, we use

val myConstraints = Constraints.Builder()
    .setRequiresDeviceIdle(true) //checks whether device should be idle for the WorkRequest to run
    .setRequiresCharging(true) //checks whether device should be charging for the WorkRequest to run
    .setRequiredNetworkType(NetworkType.CONNECTED) //checks whether device should have Network Connection
    .setRequiresBatteryNotLow(true) // checks whether device battery should have a specific level to run the work request
    .setRequiresStorageNotLow(true) // checks whether device storage should have a specific level to run the work request
    .build()

and to add it in our work request, we use

val yourWorkRequest = OneTimeWorkRequestBuilder<YourWorkerClass>()
    .setConstraints(myConstraints)
    .build()

Here, we set the above-defined constraints to the previously defined workRequest. Now, this work request will only run if all the constraints are satisfied.

We can also set the Periodic Task which will run after a certain time interval. To run a workRequest which runs periodically we use,

val yourPeriodicWorkRequest =
PeriodicWorkRequestBuilder<YourPeriodicWorkerClass>(1, TimeUnit.HOURS)
    .setConstraints(myConstraints)
    .build()

This will run every 1 hour periodically as we have set the period to be 1 hour.

Minimum time interval to run a periodic task is 15mins
  • If you do not want the task to be run immediately, you can specify your work to start after a minimum initial delay using,
val yourWorkRequest = OneTimeWorkRequestBuilder<YourWorkerClass>()
        .setInitialDelay(10, TimeUnit.MINUTES)
        .build()

This will run after an initial delay of 10minutes.

Now, let's check some of the amazing features we have in Work Manager

Check Status of your WorkManager

If we want to do some task when WorkManager executes the task successfully like showing a Toast or something else we need to check the status of the task. To check the status we use,

WorkManager.getInstance(context).getWorkInfoByIdLiveData(yourWorkRequest.id)
        .observe(lifecycleOwner, Observer { workInfo ->
            if (workInfo != null && workInfo.state == WorkInfo.State.SUCCEEDED) {
                //Toast
            }
        })

Here, we observe the status by the LiveData to update the UI.

Chaining of Tasks

We can chain multiple tasks in two ways.

  • Chaining in Series
  • Parallel Chaining

Let's talk about them one by one.

Chaining in Series

Let's say we have two workRequest,

val yourWorkRequestOne = ...
val yourWorkRequestTwo = ...

and we need to chain them in series. To do this we will use

WorkManager.getInstance(context).beginWith(yourWorkRequestOne)
.then(yourWorkRequestTwo)
.enqueue()

Here, first, the execution will start with yourWorkRequestOne WorkRequest and then it will execute the second one. This is called a series chaining of the tasks.

Parallel Chaining

In this, we can chain the task in parallel using the following

WorkManager.getInstance(myContext)
    .beginWith(listOf(work1, work2, work3))
    .then(work4)
    .then(work5)
    .enqueue()

Here, work1,work2, and work3 will execute parallel, and then when all of them are executed then only work4 will execute and work5 sequentially.

Note : If the first task fails the subsequent task following it will also give failure response and if the first task is cancelled all the following task will also be cancelled.

To cancel a task we use,

WorkManager.cancelWorkById(workRequest.id)

Input/output for your task

While running the work/task, we might need some input for the worker to run. Like for example, fetching the current user detail will require the user-id of the user. To pass that as input we use,

val userId = workDataOf(Constants.USER_ID to String)

val yourWorkRequest = OneTimeWorkRequestBuilder<YourWorkerClass>()
        .setInputData(userId)
        .build()

and to get the input in worker class we have,

class YourWorkerClass(appContext: Context, workerParams: WorkerParameters)
    : Worker(appContext, workerParams) {
    override fun doWork(): Result {
        val userId = getInputData().getString(Constants.USER_ID)
        val userDetail = getDetail(userId)
        // Create the output of the work
        val firstName = workDataOf(Constants.DETAIL to userDetail.firstName)
        // Return the output
        return Result.success(firstName)

    }
}

Here, we get the input using getInputData().getString() and we pass it to getDetail() as parameter to get the user detail.

Now, to pass the User's first name as output we pass the output in Result.Success(/**Your Output**/).

This is how we can integrate the work manager in our Android project.

Happy learning

Team MindOrks :)