Android Native - How to use WorkManager

dimitrilc 1 Tallied Votes 20 Views Share
Introduction

WorkManager is the preferred method of managing background tasks on Android. It also includes convenient extensions for RxJava3 and Kotlin Coroutines.

In this tutorial, we will learn how to use WorkManager as well as how to observe the background task with the debugging tool Background Task Inspector.

Goals

At the end of the tutorial, you would have learned:

  1. How to use WorkManager.
  2. How to use the Background Task Inspector.
Tools Required
  1. Android Studio. The version used in this tutorial is Arctic Fox 2020.3.1 Patch 4.
Prerequisite Knowledge
  1. Basic Android.
Project Setup

To follow along with the tutorial, perform the steps below:

  1. Create a new Android project with the default Empty Activity.

  2. Add the WorkManger dependency to your module build.gradle file.

     def work_version = "2.7.1"
    
     implementation "androidx.work:work-runtime-ktx:$work_version"
  3. Remove the default “Hello World!” TextView.

  4. Add a new Button inside ConstraintLayout.

  5. Constrain the Button to the center of ConstraintLayout.

  6. Extract the Button android:text value to strings.xml, using download_button as the resource name and Download File as the Resource value.

Your activity_main.xml should look similar to the code below.

<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
   xmlns:app="http://schemas.android.com/apk/res-auto"
   xmlns:tools="http://schemas.android.com/tools"
   android:layout_width="match_parent"
   android:layout_height="match_parent"
   tools:context=".MainActivity">

   <Button
       android:id="@+id/button"
       android:layout_width="wrap_content"
       android:layout_height="wrap_content"
       android:text="@string/button_download"
       app:layout_constraintBottom_toBottomOf="parent"
       app:layout_constraintEnd_toEndOf="parent"
       app:layout_constraintStart_toStartOf="parent"
       app:layout_constraintTop_toTopOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>
Primary Classes from androidx.work

Before writing any code, we will first need to know about the primary classes of WorkManager. Most of the classes related to WorkManager can be found in the androidx.work package. The most basic classes are Worker, WorkRequest, and WorkManager.

  1. WorkManager: This class is responsible for submitting WorkRequest objects to the system. It can also cancel and read Workers. By default, the WorkManager starts itself when your App is started, so in most cases, you do not have to initialize WorkManager yourself.
  2. WorkRequest: Represents a work request, which WorkManager submits to the system. You can build custom WorkRequest objects with WorkRequest.Builder or use the two existing concrete implementations OneTimeWorkRequest or PeriodicWorkRequest. You can create OneTimeWorkRequest objects by wrapping Worker objects.
  3. Worker: This class contains code to perform the actual work for your App. Worker is abstract, so you will need to override it and implement the doWork() function.
  4. ListenableWorker.Result: The object that you must return from Work#doWork(). You can create Result objects by calling Result’s static functions failure(), retry(), and success().
  5. WorkInfo.State: Your work moves through different stages in the system. There are six different stages for your work: BLOCKED, CANCELLED, ENQUEUED, FAILED, RUNNING, SUCCEEDED.
Project Overview

For this tutorial, we will have our app start a fake download WorkRequest. When the user taps Download File, the background thread that executes the work request will wait for 1 second before reporting back the ListenableWorker.Result status.

We will also use the Background Task Inspector debugging facility to observe our work progress.

Override Worker

It is finally time to write some code. Our first task is to create a concrete implementation of Worker.

  1. In the same package as the MainActivity class, create a new Kotlin class called DownloadWorker.kt.

     class DownloadWorker {
    
     }
  2. Add Worker as the parent.

     class DownloadWorker(context: Context, workerParams: WorkerParameters) : Worker(context, workerParams) {
    
     }
  3. Implement the doWork() function. This implementation will wait for 1 second and return a SUCCESS status.

     override fun doWork(): Result {
         Thread.sleep(1_000L)
    
         return Result.success()
     }
Retrieves the WorkManager

As stated previously, a default WorkManager is automatically initialized by our App on startup. Because our use case is quite simple and does not require a custom WorkManager, we can just use the default WorkManager instance using the convenient static function WorkManger#getInstance().

  1. Inside MainActivity.kt#onCreate(), append the line of code below.

     val workManager = WorkManager.getInstance(applicationContext)
  2. Create the a OneTimeWorkRequest object by appending the line of code below to onCreate(). You must pass in the Java class object to the from() function.

     val oneTimeDownloadWorkRequest = OneTimeWorkRequest.from(DownloadWorker::class.java)
  3. Append the line of code below to onCreate() retrieve the Button object.

     val button = findViewById<Button>(R.id.button)
  4. Bind the Button click to the work request in onCreate().

     button.setOnClickListener {
        workManager.enqueue(oneTimeDownloadWorkRequest)
     }
Background Task Inspector

The Background Task Inspector is a special debugging facility available when your WorkManager runs at Android API level 26 or above. To see how it works, follow the steps below:

  1. In Android Studio, go to View > Tool Windows > App Inspection.
  2. This tool also contains the Database Inspector, so ignore that tab for now. When your App is started, switch to the Background Task Inspector.
    1.png
  3. Run the App.
  4. Taps Download File button in your App and observe the background job being executed.

WorkManager.gif

Solution Code

DownloadWorker.kt

package com.example.daniwebandroidworkmanager

import android.content.Context
import androidx.work.Worker
import androidx.work.WorkerParameters

class DownloadWorker(context: Context, workerParams: WorkerParameters) : Worker(context, workerParams) {

   override fun doWork(): Result {
       Thread.sleep(1_000L)

       return Result.success()
   }

}

MainActivity.kt

package com.example.daniwebandroidworkmanager

import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import android.widget.Button
import androidx.work.OneTimeWorkRequest
import androidx.work.WorkManager

class MainActivity : AppCompatActivity() {
   override fun onCreate(savedInstanceState: Bundle?) {
       super.onCreate(savedInstanceState)
       setContentView(R.layout.activity_main)

       val workManager = WorkManager.getInstance(applicationContext)
       val oneTimeDownloadWorkRequest = OneTimeWorkRequest.from(DownloadWorker::class.java)

       val button = findViewById<Button>(R.id.button)

       button.setOnClickListener {
           val operation = workManager.enqueue(oneTimeDownloadWorkRequest)
       }
   }
}

strings.xml

<resources>
   <string name="app_name">Daniweb Android WorkManager</string>
   <string name="button_download">Download File</string>
</resources>

activity_main.xml

<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
   xmlns:app="http://schemas.android.com/apk/res-auto"
   xmlns:tools="http://schemas.android.com/tools"
   android:layout_width="match_parent"
   android:layout_height="match_parent"
   tools:context=".MainActivity">

   <Button
       android:id="@+id/button"
       android:layout_width="wrap_content"
       android:layout_height="wrap_content"
       android:text="@string/button_download"
       app:layout_constraintBottom_toBottomOf="parent"
       app:layout_constraintEnd_toEndOf="parent"
       app:layout_constraintStart_toStartOf="parent"
       app:layout_constraintTop_toTopOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>
Summary

We have learned how to use WorkManager and the Background Task Inspector. The full project code can be found at https://github.com/dmitrilc/DaniwebAndroidWorkManager

Be a part of the DaniWeb community

We're a friendly, industry-focused community of developers, IT pros, digital marketers, and technology enthusiasts meeting, learning, and sharing knowledge.