Background Processing Challenges in Android

Introduction


When we create a new app, every app has a main thread responsible for user interface experience. The main thread coordinates user interaction or inputs with others running in the application. If we do too much work on main thread then it will slow the application or be hung up for a while. This will result in a bad user experience. Any long-running computations and operations such as decoding a bitmap, accessing the disk, or performing network requests should be done on a separate background thread. 

So, if we have a task that takes few seconds or more than a second, then it must be delegated to a different thread or more precisely, the background thread.

There are some types of tasks to be run when the user is not using their phone such as syncing with the server periodically. Applications may also require services to run immediately to completion even after the user has completed interacting with the app. So, for these types of background work, we will see how to deal with different situations.

Challenges in background processing


Background tasks consume the resources of the phone, like RAM and battery, which are limited for handheld devices. If these background tasks are not handled properly, it will drain the battery, resulting in a bad user experience. So, our continous effort is to make efficient apps and use the limited resources wisely.

In order to maximize battery and enforce good app behavior, Android restricts background work when the app (or a foreground service notification) is not visible to the user. Here are some restriction applied on various api levels over the time.
  • Android 6.0 (API level 23) introduced Doze mode and app standby. Doze mode restricts app behavior when the screen is off and the device is stationary. App standby puts unused applications into a special state that restricts their network access, jobs, and syncs.
  • Android 7.0 (API level 24) limited implicit broadcasts and introduced Doze-on-the-Go.
  • Android 8.0 (API level 26) further limited background behavior, such as getting location in the background and releasing cached wakelocks.
  • Android 9 (API level 28) introduced App Standby Buckets, in which app requests for resources are dynamically prioritized based on app usage patterns.

Understanding App Standby Buckets

 
App Standby Buckets is a device battery management feature. According to Docs, "App Standby Buckets help the system prioritize apps' requests for resources based on how recently and how frequently the apps are used. Based on app usage patterns, each app is placed in one of five priority buckets. The system limits the device resources available to each app based on which bucket the app is in".

Priority Buckets


These are based on app usage, like how frequently the app is used and how the app is used over the network or how offline buckets are formed. There are some preloaded apps from the manufacturer or system inbuilt apps. These apps uses a machine learning based algorithm that decides the usage and type of app and priortizes into buckets.
Most active applications are assigned to buckets that give the apps higher priority, making more system resources available to the app. These bucket recognize the most active apps and keeps track of how frequently jobs are running, how frequently does it trigger the alarm service, how frequently the app receives cloud messages or pushes notifications from FCM.

However these restrictions are applicable when an app is not connected to power supply. If connected, then the system does not impose any restrictions.
 
The buckets are as follows:
  • Active - The app is currently being used or was very recently used
  • Working set - The app is in regular use
  • Frequent - The app is often used, but not every day
  • Rare -The app is not frequently used
In addition, there's a special never bucket for apps that have been installed but have never been run. The system imposes severe restrictions on these apps.
 

Let's understand the Active bucket


An app is in the active bucket if the user is currently using the app or has very recently used the app. For example:
  • The app has launched an activity
  • The app is running a foreground service
  • The app has a sync adapter associated with a content provider used by a foreground app
  • The user clicks on a notification from the app
If an app is in the active bucket, the system does not place any restrictions on the app's jobs, alarms, or FCM messages.
 
Working Sets

This is type of bucket consists of app which is used often but not active. Like social media application that used everyday. The system uses mild restrictions over the ability to run jobs and trigger alarm on these apps.
 
Frequent

Apps in this bucket list are those which are used regularly, but not neccessarily everyday, such as gym workout applications. Apps in this bucket have high restriction over the ability to run jobs and trigger the alarm. The system also imposes high restriction over cloud messages (FCM).
 
Rare

Apps in this bucket list are those which are used very rarely, like a hotel app when the user is staying in a certain hotel, then the app will be used. Otherwise, it will not. Apps in this bucket have strict restriction over the ability to run jobs and trigger alarm. The system also imposes high restriction over cloud messages (FCM). The system limits ability to connect to internet as well sometimes.
 

Limited background behavior (Api level 26) - Android 8.0


When the app is in the background, certain apps do heavy tasks in the background and consume device resources like RAM and battery. Therefore, apps which are resource intensive like games or playing videos user is using while device is idle then it becomes slow or result in bad user experience. To improve the user experience Android imposes restriction over background resources use.
  • Background Service Limitation
    While an app is idle, there are limits to its use of background services. This does not apply to foreground services, which are more noticeable to the user.

  • Broadcast Limitations
    From now, the app cannot use manifest to register implicit broadcasts. These broadcasts can be registered only at run time. However, an explicit broadcast can use manifest for registering the broadcast targeted for the app itself.

  • Intent Service
    Intent services no longer work properly in apps that target Oreo (Api 26 or higher). Instead, it uses the JobIntentService class that provides work similar to intent service, but uses job instead.
Optimize for Doze and App Standby

What is doze mode? Doze mode is a power saving feature that keeps track of how the apps are behaving in the background when the device is not charging. Doze mode gets activated when the device is not in use for some time. This mode deferres some CPU intensive activities, thus saving battery consumption, while App Standby reduces network intensive tasks when the user is not interacting with them.
 
Periodically, the system exits Doze for a brief time to let apps complete their deferred activities. During this maintenance window, the system runs all pending syncs, jobs, and alarms, and lets apps access the network. 

Doze restrictions

The following restrictions apply to your apps while in Doze:
  • Network access is suspended.
  • The system ignores wake locks.
  • Standard AlarmManager alarms (including setExact() and setWindow()) are deferred to the next maintenance window.

    • If you need to set alarms that fire while in Doze, use setAndAllowWhileIdle() or setExactAndAllowWhileIdle().
    • Alarms set with setAlarmClock() continue to fire normally — the system exits Doze shortly before those alarms fire.

  • In this mode, the system does not perform Wi-Fi scans.
  • In this mode, the system does not allow sync adapters to run.
  • In this mode, the Android system does not allow JobScheduler to run.

Choosing the right solution for your work

  • Can the work be deferred, or does it need to happen right away?
    If the work has to be done right away, when the user click the button in UI, or we have to fetch some details from the server. Another case might be a user want to upload some logs or some data to the server, then this work may be deferred and upload will happen after some time.

  • Is the work dependent on system conditions?
    As the name suggests, with system conditions, some work is condition dependent, like tasks have to be done when connected to power only. The system invokes some broadcast or wifi available. So these tasks or work can be done based on system conditions without affecting user experience.

  • Does the work involve the collection or use of sensitive user data?
    If you want to show direction in navigation app, then one must show a foreground notification and can use a foreground service in this case.

  • Does the job need to run at a precise time?
    Some jobs are deffereable and must execute at some specific time, like a calender application where the user sets a reminder at a particular date and expects a notification or alarm or something that let him know his reminders.
Lets see a diagram to understand which solutions best fits our problem, as mentioned above. 

Background Processing Challenges In Android
Image Courtesy of Official Android Docs.

WorkManager

WorkManager is an Android library that has the ability to process deferrable tasks. Tasks are deferred until system requirements are fullfiled, like internet connectivity comes back. WorkManager can process work even if the device restarts.
 
ForegroundServices

For tasks that are needed instantly and must execute completely, in this case, ForegroundService is recommended. Using a foreground service tells the system that the app is doing something important and it shouldn’t be killed. Foreground services are visible to users via a non-dismissible notification in the notification tray.

AlarmManager

When we need to run jobs at a particular or precise time, this would be a good option to use. If the task is deferrable and doesn't need to be run at precise time, then workmanager would be better choice because it balances the system resources.
 
DownloadManager

If we want long running HTTP downloads, then this will be a good choice. DownloadManager will keep retrying even connectivity lost and begins to download when connectivity comes or system reboots.