DEV Community

Magda Miu
Magda Miu

Posted on • Originally published at proandroiddev.com on

Workout your tasks with WorkManager — Basics

Workout your tasks with WorkManager — Main Components

“WorkManager is a library for managing deferrable and guaranteed background work.”

In my previous post I covered details about the Android memory model, battery optimization features, current background processing solutions and what are the main advantages of WorkManage and where it should be used.

In this blog post I’ll cover the main components of WorkManager library:

  • Worker
  • WorkRequest
  • Constraints
  • Input/Output Data
  • WorkManager

In order to use WorkManager in our app first of all we should declare the dependencies :

  • Open the build.gradle file of the project and add the google() repository as shown below:
allprojects {
repositories {
google()
jcenter()
}
}
view raw build.gradle hosted with ❤ by GitHub
  • Open the build.gradle file of the module (app) and add necessary dependencies as shown below:
dependencies {
def work_version = "2.0.1"
// (Java only)
implementation "androidx.work:work-runtime:$work_version"
// Kotlin + coroutines
implementation "androidx.work:work-runtime-ktx:$work_version"
// optional - RxJava2 support
implementation "androidx.work:work-rxjava2:$work_version"
// optional - Test helpers
androidTestImplementation "androidx.work:work-testing:$work_version"
}
view raw build.gradle hosted with ❤ by GitHub

The main components of WorkManager are:

So first of all we need to create a background task by extending the Worker class. After that we configure how and when to run the task by using the WorkRequest implementations and finally we hand off the task to the system.

Q: What happens behind the scenes? What is the internal mechanism used by worked manager?

A: Actually, WorkManager is more like a wrapper on the existing background processing solutions for deferrable and guaranteed work.

1️⃣Worker

A task is defined by the Worker class. The function doWork() runs synchronously on a background thread.

Worker implementation

class SyncWorker(c: Context, wp: WorkerParameters):Worker(c, wp) {
override fun doWork(): Result {
loadData()
return Result.success()
}
}
view raw SyncWorker.kt hosted with ❤ by GitHub

2️⃣WorkRequest

When a task is defined we should specify it is a periodic one or not. If it is periodic we will use PeriodicWorkRequest class, if not we will use OneTimeWorkRequest.

OneTimeWorkRequest

  • It is used for non-repeating work
  • It could have an initial delay
  • It could be part of a chain or graph of work

PeriodicWorkRequest

  • Used for tasks that need to execute periodically
  • The minimum repeat interval that can be defined is 15 minutes (same as the JobScheduler API) and it cannot have an initial delay
  • It cannot be part of a chain or graph of work
  • Before v2.1-alpha02 it’s not possible to create a PeriodicWorkRequest with an initial delay.
  • The execution may be delayed because WorkManager is subject to OS battery optimizations, such as doze mode

val syncOnlyOnce = OneTimeWorkRequestBuilder<SyncWorker>().build()
val syncPeriodically = PeriodicWorkRequestBuilder<SyncWorker>(1, TimeUnit.HOURS).build()
val periodicRefreshRequest = PeriodicWorkRequest.Builder(
SyncWorker::class.java, // the worker class
30, // repeating interval
TimeUnit.Minutes,
15, // flex interval - worker will run somehow within this period of time, but at the end of repeating interval
TimeUnit.MINUTES
)

Happy and Unhappy paths for One Time Work and Periodic Work

Let’s say that our task should run only when some conditions are met, maybe we need a wi-fi connection and the device should be idle. This thing is possible in WorkManager by using the Constraints class and there 5 limitations that could be used:

  • network type limitations: setRequiredNetworkType(requiredNetworkType: NetworkType)
  • battery level limitations: setRequiresBatteryNotLow(requiresBatteryNotLow: Boolean)
  • charging limitations: setRequiresCharging(requiresCharging: Boolean)
  • status of the device limitations: setRequiresDeviceIdle(requiresDeviceIdle: Boolean)
  • storage level limitations: setRequiresStorageNotLow(requiresStorageNotLow: Boolean)

Related to the NetworkType options, the enum have 5 values:

  • CONNECTED — Any working network connection
  • METERED — A metered network connection
  • NOT_REQUIRED — A network is not required for this work.
  • NOT_ROAMING — A non-roaming network connection
  • UNMETERED — An unmetered network connection

We can apply the constraints like this:

val constraints = Constraints.Builder()
.setRequiresCharging(true)
.setRequiresStorageNotLow(true)
.setRequiredNetworkType(NetworkType.CONNECTED)
.build()
val syncOnlyOnce = OneTimeWorkRequestBuilder<SyncDataWorker>()
.setConstraints(constraints)
.build()

Input and output Data

  • The Data.Builder helper class lets us to provide data to our Worker.
  • Please keep in mind that this should not exceed 10KB. We will get an IllegalStateException if we pass data that exceeds this limit.
  • Think of Data as a key-value pairs store, not unlike SharedPreferences.

val userIdData = Data.Builder()
.putInt(DATA_USER_ID, userId)
.build()
val syncOnlyOnce = OneTimeWorkRequestBuilder<SyncWorker>()
.setInputData(userIdData)
.build()
val userIdInput = inputData.getInt(Constants.DATA_USER_ID, 0)
// ktx
val outputData = workDataOf(Constants.DATA_SENT to isDataSent)

3️⃣WorkManager

After the work was defined by the Worker class and we know also the type of the task (periodic or not) we can now schedule it with WorkManager using the enqueue() method.

And if we decide to cancel all the work we could use the cancelAllWork() method.

WorkManager methods

// running work
WorkManager.getInstance(context).enqueue(syncOnlyOnce)
// cancelling work
WorkManager.getInstance(context).cancelAllWork()

An important thing to mention on how to retrieve the WorkManager instance:

WorkManager v2.1 has deprecated WorkManager#getInstance() and there’s now a new WorkManager#getInstance(context: Context) that works in the same way but supports the new on-demand initialization. In this article I’m going to use this new syntax that expects that we pass a context to retrieve the WorkManager instance.

In the next article, we’ll take a closer look on features like BackoffPolicy, how to identify a task, how to get the status of a task, how to combine the tasks and the graphs of tasks (chaining the work) and how to merge the inputs and outputs. Stay tuned!

That’s it for now, hope it helps! Enjoy and feel free to leave a comment if something is not clear or if you have questions. Thank you for reading! 🙌🙏😍✌


Image of AssemblyAI tool

Transforming Interviews into Publishable Stories with AssemblyAI

Insightview is a modern web application that streamlines the interview workflow for journalists. By leveraging AssemblyAI's LeMUR and Universal-2 technology, it transforms raw interview recordings into structured, actionable content, dramatically reducing the time from recording to publication.

Key Features:
🎥 Audio/video file upload with real-time preview
🗣️ Advanced transcription with speaker identification
⭐ Automatic highlight extraction of key moments
✍️ AI-powered article draft generation
📤 Export interview's subtitles in VTT format

Read full post

Top comments (0)