Android background processing – a decision guide

by
Tags: , , ,
Category: , ,

If you’ve ever done anything in Android that takes more than 16 milliseconds to process then you’ve probably heard of Android services and background processing in general.
Why 16 milliseconds?
At Android’s refresh rate of 60 frames per second, the main thread can not be delayed by more than 16 milliseconds to avoid frame skipping and, therefore, UI lagging.

The problem is – Google is constantly changing the way background processes are allowed to run. This is mostly done for performance improvements and to combat malicious actors.
This guide is the best practices as of Jan 2021.
Proceed with caution.

Your background task will fall into one of the categories of background tasks:
1. Immediate
2. Exact
3. Deferred

To figure out your particular case follow the diagram below:

 

Immediate

For immediate tasks that don’t need to run when user closes the app it’s best to use Kotlin coroutines. If you don’t like the power and simplicity of Kotlin coroutines you should rethink your life and read up on thread pools and executors here: https://developer.android.com/guide/background/threading

Exact

For exact tasks you’ll need to use AlarmManager and familiarize yourself with this: https://developer.android.com/training/scheduling/alarms

Deferred

The most interesting case is the deferred tasks or long running immediate tasks. This is the most juicy and exciting type of background processing. Here are the mechanisms available to you:

1. Background Services.

These are application components, but they are becoming useless and will probably be killed off by Google soon. It seems like with every API update Google imposes more and more restrictions on background services. If you decide to use background service you must keep in mind that:
a) extending Service() does not guarantee that your work is happening on a separate thread. You have to manually make sure you’re not doing anything on the UI thread.
b) service will be killed once the app is moved to the background. So yeah, useless.

2. Bound Services.

These are more involved and work as long as other components are bound to them by calling bindService(), but are always killed when the app is moved to the background.

3. Foreground services.

These are just like background services, but waaaay cooler. They are started with context.startForegroundService(...) command AND they have to show a notification to the user. This way the service is allowed to run even after the application is stopped, or it can be used to prevent OS from killing the application. Foreground services are used to play music or collect sensor data like I did in GestUR app

You can also employ a simple trick of starting a service from a BroadcastReceiver’s onReceive method. This way you can start the service right after the phone is booted up by registering receiver’s intent filter to react to action android.intent.action.BOOT_COMPLETED

Read more about foreground services here: https://developer.android.com/guide/components/foreground-services

4. Now the best of the bunch – WorkerManager and its Workers.

These are awesome and they have a future in Google’s eyes, because they are part of Android Jetpack. Here’s from Android development documentation: “For tasks that should be executed immediately and need continued processing, even if the user puts the application in background or the device restarts, we recommend using WorkManager and its support for long-running tasks.”

YouTube recently moved over to WorkManager for their background scheduling needs and has reported improvements in app startup time as well as an 8% drop in crash rates.

This is serious people. WorkManager and Workers is what you should be using for long running background tasks.

How does it work?

1. Create a worker class or classes that extend Worker(context, workerParameters)
2. These classes will overwrite doWork() that returns a Result
3. Create a work request with either PeriodicWorkRequestBuilder or OneTimeWorkRequestBuilder and set constraints for your worker. For example, if your worker needs a network connection and a particular state of phone charge, then you add those constraints to the request builder.
4. Using an instance of WorkManager for your context, call .enqueueUniquePeriodicWork(...) or .enqueueUniqueWork(...) or .enqueue(...) depending on your needs
5. Wait for and react to Result returned from the Worker

Keep in mind – there is a couple of specialized Workers: ListenableWorker and CoroutineWorker. Kotlin developers should use CoroutineWorker

I hope this guide helps you decide how to do stuff in a background in Android
– Joe Berger

 

Sources:
1. https://developer.android.com/guide/background
2. https://developer.android.com/topic/libraries/architecture/workmanager