WorkManager, Does it always Manage to Work?

Rotem Matityahu
5 min readMar 2, 2021

--

Intro

While WorkManager is a convenient tool for scheduling background tasks, it doesn’t ensure that your task is actually going to run at the desired time. To understand why that is, we first need to understand how the OS manages background operations.

A little history

Android 6

Android 6 introduced two performance optimization strategies that greatly affect the ability of apps to continuously run background operations: Doze mode and App Standby.

Doze affects the entire device for all applications. If the device is left unplugged and stationary, with the screen off, the device enters Doze mode. In Doze mode, the system attempts to conserve battery by restricting apps’ access to network and CPU-intensive services. It also prevents apps from accessing the network and defers their tasks, syncs, and Alarms.

App Standby is a state determined by the OS, based on the app’s usage, in which an app might have restricted network access. This one, unlike Doze, hates you personally!

Android 7

A more lightweight version of Doze was added in Android 7, called Light-Doze. The main differences are that Light Doze gets activated more shortly, has fewer preconditions to get activated and maintenance windows are more frequent.

Android 8

Then, in Android 8, the system applied background limitations on all applications. This means that your app is no longer allowed to start Services when in the background.

Android 9

On top of that, Android 9 extended the concept of App Standby with App Standby Buckets, now powered by a shiny new algorithm that dynamically assigns each app to a priority bucket, and updates its bucket as needed.
Along with network restrictions, an app that is in a low priority bucket will be affected in the frequency of running Jobs, triggering Alarms, and receiving FCM messages. Basically, everything needed in order to run smoothly in the background.

User vs Developer

Now, don’t get me wrong, As an Android user, I love the idea that my battery life is optimized. The system is protecting it by preventing long CPU tasks and preventing unnecessary network tasks.
But (and it’s a big but) As an Android developer, sometimes I need my code to run at a specific time (specific-ish will also do).

The use case

My team encountered issues with the Standby buckets right from the start, when our app was put in the ‘rare’ bucket. Apps in these buckets have limited access to the internet, as well as a daily cap on background tasks. As a result, some of the scheduled Jobs didn’t get triggered.

We ran some tests and changed the background Service to a foreground Service, which serves the same purpose as a background Service but also displays a Notification in the tray.
According to the documentation, one of the reasons that an app is considered foreground is that it has an active foreground Service. If the user interacts with the Notification tray, the app is put in the ‘active’ bucket. What is not written in the documentation is that while the notification is visible, the restrictions are lifted, even if the user doesn’t click or even sees it.

Making your Service a foreground Service also keeps the process alive if possible while its work is executing (up to 10 minutes).
How is that possible? because when you’re using this strategy, you explicitly notify the user that your app is currently doing something.

Foreground Service in the wild

It seems that WhatsApp uses a foreground Service when it fetches new messages from the server if the internet connection is slow and the app requires more time without being killed by the OS. Now that the application has a running foreground Service, the restrictions are lifted and the app can acquire an internet connection to perform its business logic.

WhatsApp’s Checking for new messages notification

Wait, what about scheduling a foreground task?

What if I want my code to run with an internet connection at a deferred time? Wouldn’t that be lovely?

Let’s put it all together and create a task at a deferred time, and make it foreground so that we have an internet connection.

Is the task really going to run? Due to the fact that the notification will appear only when the task gets triggered, we’re facing the same problem again - the system doesn’t know that you’re going to start a foreground Service. Therefore, the same restrictions will apply.

In order to resolve all the challenges, I’ve developed an internal library that is now open-source.

Introducing alleviate — will ease your pain

alleviate is a lightweight library that wraps your tasks and ensures that they will run both in restricted bucket and light Doze mode! Just sit back and relax.

First, create a Class that inherits from ForegroundTaskService base Class. Then, just override getNotification(), doWork(), and onStop() as following:

Creating and scheduling a foreground task is easy:

For more information and to get started, check out the repo.

Disclaimer

Last week, the Android 12 Developer Preview was released. One of the main changes in this version is the Foreground service launch restrictions.
Basically, apps that target and run on Android 12 devices can no longer start foreground Services while running in the background.
I’m fully aware that on the Android 12 Developer Preview the library won’t work, and I’ll try to find a solution that will keep it relevant.

It’s a wrap

Scheduling deferred tasks in the Android ecosystem can be tricky, as the OS can restrict your task in order to optimize device performance. One way to work with Android and ensure that your task will run on time (or close to it) is to back up your task with a foreground service. While this approach has pros and cons, it has the added benefit of fulfilling our “contract” with the end-users to reflect the app’s usage in all states.

That’s all for today. I hope you enjoyed reading. Feel free to share and clap :)

--

--