Android WorkManager: The Ultimate Guide

You may have heard great things about WorkManager. Maybe you’re curious about WorkManager, and want to know how Android Developers can leverage the Google library to build better apps. Whatever the case, this is the complete, up-to-date, definitive guide to WorkManager. Let’s begin our journey.

What is WorkManager?

With WorkManager, developers can schedule tasks in the background that are guaranteed to run either once or periodically. Oftentimes, developers need to download lots of data. With WorkManager, developers can schedule the data to be downloaded, but downloaded only when the end user is on WiFi (free) and if the device is charging. With cellular data, the user might be charged a lot for the data. You can schedule the data to only be downloaded when the user is on a “unmetered” network. (Metered data is cellular, and costs money. Think of unmetered data as a household WiFi or the local coffee shop.)

Is WorkManager free?

Yes, WorkManager is a free library from Google. It’s currently in alpha, but I expect it to become stable — and thus ready for production — some time in the next few months. As mentioned, this library is created by Google, and will likely receive support for many years to come. Don’t expect this to become a paid-only library; use WorkManager for anything, including commercial projects.

How do I use WorkManager?

Let’s build a news app that download new stories, images and videos while the user is sleeping at night. When morning comes, all stories, images and videos are already downloaded on the user’s device, ready to be read or watched. We will ensure that this content will only be downloaded when the device is on a non-metered network, as well as on a power source (charging). We’ll use Kotlin for this project.

First, let’s add the android.arch.work library to our build.gradle.


dependencies {
    implementation fileTree(dir: 'libs', include: ['*.jar'])
    implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
    implementation 'com.android.support:appcompat-v7:28.0.0-rc02'
    implementation 'com.android.support.constraint:constraint-layout:1.1.3'
    implementation 'com.android.support:design:28.0.0-rc02'
    testImplementation 'junit:junit:4.12'
    androidTestImplementation 'com.android.support.test:runner:1.0.2'
    androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.2'

    implementation "android.arch.work:work-runtime-ktx:1.0.0-alpha08"
}

Next, let’s create the Kotlin code that will download the latest news from a web server (such as Google Cloud Platform, Amazon Web Services, Firebase, etc.). To keep things simple, let’s omit the actual background task and just log something to the console:

package com.example.workmanagereveryday

import android.util.Log.d
import androidx.work.Worker

class StoryDownloadWork : Worker() {

    override fun doWork(): Result {
        d("daniel", "performing work now!")
        return Result.SUCCESS
    }
}

With above code displays a message in logcat. Later, we can replace d(“daniel”, “performing work now!”) with our actual script that will fetch and download the news in the background.

Next, let’s look at MainActivity. Our MainActivity currently looks like this:

package com.example.workmanagereveryday

import android.os.Bundle
import android.support.v7.app.AppCompatActivity
import kotlinx.android.synthetic.main.activity_main.*

class MainActivity : AppCompatActivity() {

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        setSupportActionBar(toolbar)
    }
}

Let’s run our work! Change MainActivity to this:

package com.example.workmanagereveryday

import android.os.Bundle
import android.support.v7.app.AppCompatActivity
import androidx.work.OneTimeWorkRequestBuilder
import androidx.work.WorkManager
import kotlinx.android.synthetic.main.activity_main.*

class MainActivity : AppCompatActivity() {

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        setSupportActionBar(toolbar)

        val downloadWork = OneTimeWorkRequestBuilder<StoryDownloadWork>().build()
        WorkManager.getInstance().enqueue(downloadWork)
    }
}

In the above code, first we create the downloadWork variable and call OneTimeWorkRequestBuilder, passing in the StoryDownloadWork class we previously created. We then call build(). Next, we get an instance of WorkManager, then enqueue the downloadWork.

Run the app in an emulator or on a physical device and examine the logcat. You should see the string “performing work now!” That is because MainActivity calls StoryDownloadWork, and StoryDownloadWork contains a log statement.

You may have noticed that in the above code we use OneTimeWorkRequestBuilder. As you might guess, OneTime means that the StoryDownloadWork will only happen once. Later, we’ll change that to PeriodicWorkRequest, a class that can be executed daily, for example.

The above code will now run, but what if we want to be notified when StoryDownloadWork is finished? We can use the Observable pattern. In MainActivity, type the following code:

  WorkManager.getInstance().getStatusById(downloadWork.id).observe(this, Observer {
            d("daniel", "value? ${it?.state}")
        })

In the above code, we first get an instance of WorkManager, then call the method getStatusById, passing in the downloadWork’s id. Run the app again, open logcat, and you should see something like the following:

09-15 08:41:54.217  6918  6982 D daniel  : performing work now!
09-15 08:41:55.190  6918  6918 D daniel  : value? ENQUEUED
09-15 08:41:55.303  6918  6918 D daniel  : value? SUCCEEDED

As you can tell logcat, first we see the “daniel” log statement “performing work now!” followed by the status (value) of the the performed work. First, we see ENQUEUED followed by SUCCEEDED. This means that the work was performed successfully!

WorkManager Constraints

So we have the WorkManager functional, but what about the constraints? What if we want to only perform StoryDownloadWork when the devices is plugged in and charging as well as only when the device is on an unmetered network? (Unmetered means that the device is connect to WiFi and not cellular data that costs money, for example.) We can do this using Constraints.

Let’s add the following to MainActivity:

        val constraints = Constraints.Builder()
                .setRequiresCharging(true)
                .setRequiredNetworkType(NetworkType.UNMETERED)
                .build()

The above code sets the Constraints, and specifies that the device must be charging and and on an unmetered network.

Next, in MainActivity, directly below the variable constraints, update downloadWork to the following:

        val downloadWork = OneTimeWorkRequestBuilder<StoryDownloadWork>().setConstraints(constraints).build()
        WorkManager.getInstance().enqueue(downloadWork)

Next, run the app, and you should notice that the Constraints are in place. In an emulator, turn “Battery status” to “Not charging” as well as “Charging connection” to “None” and run the app again. You should notice that nothing happens. As soon as you re-enable charging (or plug your physical device into AC power), you should see StoryDownloadWork run.

Periodic Scheduling

Running WorkManager when the user opens MainActivity is great, but what if we want to schedule a repeating task? With only a slight modification, we can schedule a task to run every x amount of time. Open MainActivity, and modify downloadWork as follows:

        val downloadWork = PeriodicWorkRequestBuilder<StoryDownloadWork>(12, TimeUnit.HOURS)
                .setConstraints(constraints)
                .build()

In the above example, the task downloadWork will run every 12 hours, given the Constraint requirements are met.

To be continued…

About the author

Daniel Malone
Bringing more than a decade of software engineering experience, Daniel Malone is Editor at androidEveryday. An Austin native, Daniel is often found reading technical books, blogging and creating YouTube tutorials. When not working, he likes to listen to pop hits on Google Play Music.

One thought on “Android WorkManager: The Ultimate Guide

Comments are closed.

Android RecyclerView Tutorial (Part 1)

  • Most apps contain lists of data using RecyclerView.
  • In this tutorial, use ConstraintLayout and Kotlin to display a list of data.

3 weeks ago

findViewById() in Kotlin

  • As you may have discovered, findViewByid() is no longer needed.
  • Kotlin adds the ability to directly access Views.

1 month ago

Android Architecture Components Tutorial: ViewModel + LiveData

  • Use Kotlin to build a basic Android app using Android Architecture Components.
  • As part of Jetpack, LiveData and ViewModel support a MVVM app architecture.

1 month ago