Into Depth: Dose Mode and App Standby

Mehmet Emin Ergenç
Appcent
Published in
3 min readJul 1, 2024

Doze Mode and App Standby

Doze Mode and App Standby are some of the power-saving capabilities by Android with the view of optimizing battery life concerning the management of background activities and network access.

Doze Mode

Overview

Doze Mode aims to save power by closing all apps and limiting their activity, especially if the device is not used for a long duration. The overall idea came up in Android 6.0, Marshmallow, but it was improved in later releases.

How It Works

Initial Idle Mode: The device first enters an initial idle mode after some time being unplugged and stationery. Network access and background tasks are deferred.
Maintenance Windows: It periodically exits Doze for a short time to maintenance window allows apps to finish pending activities and sync data.
Deep Idle Mode: If it continues staying idle, then it goes to deeper idle states where it accesses the network and background activities are further restricted.

Impact of Doze Mode on Apps

Background Services: These are put in pause, thus delaying tasks such as synchronization and update of data.
Network Access: Network access is disallowed and thus apps can’t send or receive data outside maintenance windows.
Alarms and Jobs: Alarms set using AlarmManager and jobs scheduled using JobScheduler are deferred until the maintenance window comes.

Developer Considerations

GCM/FCM: Google Cloud Messaging or Firebase Cloud Messaging may help to skip these restrictions, as it delivers high-priority messages, waking up the device from Doze for an urgent task.
JobScheduler: Schedule jobs with JobScheduler, thereby enabling Android to group network and CPU-intensive tasks together to minimize instances of wake-ups.
Optimized Battery Usage: Ensure the app’s background activities are optimized only when it is necessary to run.

App Standby

Overview

Standby puts applications into standby mode after a certain period of inactivity and restricts the system resources they can use. In standby mode, access to the network and CPU is specifically restricted. App Standby was introduced in Android 6.0 (Marshmallow).

How It Works

Long-time unused apps are considered inactive. For example, an app may be assumed to be inactive if it was never launched or does not have any visible activities.
Restrictions: The background tasks of an inactive app are executed less often than the ones of active apps. It also has limited access to network operations and the AlarmManager.

Impact on Apps

Background Jobs: The execution of background jobs and alarms may be delayed or prevented.
Network Access: Access to the network is restricted; thus, the app cannot transfer data in the background.

Developer Considerations

Usage Statistics: The usage statistics of apps can be tracked. It gives the idea of when the app may go into standby.
Optimize Background Work: Use WorkManager or other libraries that are Doze and App Standby aware to perform background work effectively.

Handling Doze Mode and App Standby

Best Practices

1. High-Priority FCM: High-priority Firebase Cloud Messaging may be used in case of urgent messages to wake up the device from Doze.
2. Defer Background Work: Delay undue usage of JobScheduler, WorkManager, or AlarmManager with exact timing requirements as long as you can allow it.
3. Optimize Network Requests: Such requests should be put in batches so as to reduce as much as possible the impact of the network restrictions.
4. Testing: Test your app for different conditions under Doze and App Standby to see if everything is working as expected.

dozing cat
Photo by Echo on Unsplash

Code Examples

Using JobScheduler

val jobScheduler = getSystemService(Context.JOB_SCHEDULER_SERVICE) as JobScheduler

val jobInfo = JobInfo.Builder(1, ComponentName(this, MyJobService::class.java))
.setRequiredNetworkType(JobInfo.NETWORK_TYPE_ANY)
.setRequiresCharging(true)
.setPeriodic(15 * 60 * 1000) // 15 minutes
.build()

jobScheduler.schedule(jobInfo)

Using WorkManager

val constraints = Constraints.Builder()
.setRequiresCharging(true)
.setRequiredNetworkType(NetworkType.CONNECTED)
.build()

val periodicWorkRequest = PeriodicWorkRequest.Builder(MyWorker::class.java, 15, TimeUnit.MINUTES)
.setConstraints(constraints)
.build()

WorkManager.getInstance(context).enqueue(periodicWorkRequest)

In these examples:

  • For JobScheduler, you schedule a job that requires network connectivity and charging, and it runs periodically every 15 minutes.
  • For WorkManager, you create a PeriodicWorkRequest with similar constraints and schedule it to run every 15 minutes.

--

--