Working with WorkManager in Android — An Intro

Mahima
Stasis India
Published in
6 min readSep 22, 2019

One of the deadly nightmares for an Android developer is the infamous “Application Not Responding” dialog!

Oh no! 😰

Why does this happen? You’re probably running a long task in the UI thread. How do you fix it? Simple — kick-off the tasks to the background. But how do you achieve that?

AsyncTasks? AlarmManager? JobScheduler? Having multiple options hasn’t necessarily been a good thing. Each one has its own pros but brings with it a bunch of new restrictions.

So what do you do? Which one do you choose? 🤷‍♀

In this series of articles, you will explore how background processing has evolved in Android and understand how Jetpack’s WorkManager makes it easy to handle background tasks.

Evolution of background processing in Android

It’s time to revisit some of the popular background processing APIs and libraries. A bit of history doesn’t hurt, right? 📚

Threads and Handlers

The first and foremost way of running tasks in the background is to define them in separate threads using the Thread API. When using threads you need to take care not to update the UI from the worker threads. You can use Handlers to communicate between a worker thread and the UI thread. You will also need to handle your threads’ states while your app switches between its lifecycle states as threads do not account for these changes on their own.

Services & IntentServices

Services for very long have been the go-to way to handle long-running tasks. They are not tied to your activity’s lifecycle. But when you use the Services API, it is your responsibility to explicitly define how to handle threading as they run on the UI thread by default.

IntentServices, on the other hand, provide a way to define your task on a background thread but the responsibility of communicating the results to the UI again rests upon you.

AsyncTasks

AsyncTasks is one of the most widely used APIs. It provides easy-to-use methods to both define your task in a background thread and also to update UI on the UI thread. One of the issues with AsyncTasks is that they don’t care about your device’s state. If there’s a configuration change and you’re not handling the lifecycle of your activity correctly, it might lead to a crash or memory leaks.

AlarmManager

AlarmManager lets you define tasks that run at a specific time or deferrable tasks. However, alarms don’t survive a device restart. You have to remember to use a broadcast receiver and re-configure your alarms every time the device is restarted.

JobScheduler

JobScheduler was introduced in API 21. It’s recommended to be used only on API 23 and above, as there were a number of platform bugs reported in 21 & 22. So there you go, you’d be losing a large number of users if you do not account for lower APIs. So you’ll have to use AlarmManager with Broadcast Receivers on API level 22 and below and JobScheduler for API level 23 and above.

All of this is doable, but let’s not kid ourselves, it’s a lot of work. A lot of boilerplate code, a lot of if and else's. 😨

Aren’t we forgetting something? What about the challenges due to battery optimization features released in the last couple of years? 🔋

Background restrictions

Starting with the release of Marshmallow, Android imposes a lot of restrictions on background services to help save your users’ battery. Let’s see what these are.

Doze Mode and App standby

Doze was introduced in Marshmallow (API 23). Whenever your device screen is off and has been stationary for some time, doze mode kicks in. Android stops processing your background tasks such as syncs, backups or standard alarms. It allows the tasks to run normally in what’s called a doze maintenance window every now and then.

App standby allows the system to identify apps that are not actively being used on a device and impose restrictions on them accordingly. Whenever the user resumes using the app or plugs the device to a charger, it will lift off such restrictions.

Doze-on-the-go

Nougat (API 24) takes doze a step further by imposing restrictions not only when the device is stationary but also while-on-the-go. So if your device screen is off and you’re carrying it around in your pocket, doze restrictions still apply.

Background execution limits

Further background service limitations were introduced in Oreo (API 26) which restricted access to frequent location updates and placed limitations on a number of implicit broadcasts.

App Standby buckets

Pie release (API 28) introduced App standby buckets which limit device resource access to apps by placing them in priority buckets based on how recently and how frequently they’re used. For example, an app that is used frequently such as a Messenger app would be placed in an “active” bucket with no restrictions to access system resources. On the other hand, an app that was installed but never opened would be placed in a “never” bucket imposing severe restrictions.

Now, with so many things to worry about how can you be a good Avenger who can help save the world? 😟

Not to worry, we’ve found our secret weapon.

Say Hello to WorkManager! 😻

WorkManager is meant to do deferrable but guaranteed work — these are tasks that need not happen immediately but you want them to survive app restarts and device reboots.

WorkManager, under the hood, uses the platform APIs like JobScheduler and AlarmManager based on the API level of the device.

WorkManager under the hood

Let’s briefly look at the components and features of WorkManager before diving deep into the concepts.

Components of WorkManager

  • Worker — Defines the work that needs to be done in the background
  • WorkRequest — Creates a request for the Worker class, allows you to pass input data, set constraints to a work and define retry policy in case of failures
  • WorkManager — handles handing off the work to the Android system as soon as the specified constraints are met
  • WorkResult — Published by the WorkManager when work is complete — specifies whether the work completed successfully or failed

Features of WorkManager

  • Run one-time or periodic tasks
  • Set constraints to a task
  • Easily pass data to and retrieve data from a finished task
  • Define dependent tasks
  • Run tasks in parallel

Okay, enough theory! 😴 Time to code? 👻

Well, not so soon. Stay tuned for the upcoming articles, where we will develop an app to demonstrate the powerful features of WorkManager. 😋

Psst! Here’s what we’ve named that app: Furrever. 🐶

--

--