30 May 2018
Save Your Android Service From Doze Mode
   
Pranav J.Dev
#Technology | 8 Min Read
This article will explain how we can run a service on Android 6 and above without getting killed by the OS. From Marshmallow onwards, Android has introduced two new feature called Doze Mode and Standby Mode. This will give some more life to your phone i.e. extend its battery.
Doze Mode
“Doze mode is exactly like its name suggests. If the user leaves the device unplugged for a period of time and the screen is off, then the device will enter into Doze Mode. In Doze mode, the system attempts to conserve battery by restricting apps from accessing the network and intensive CPU utilization services. It also prevents the app from accessing the networks and defer their jobs, syncs, and alarms.
Standby Mode
The system will enter into standby mode if the user is not actively using the app for a period of time and also there is no process for the app which is currently in the foreground(either as an activity or foreground service). In both Doze and Standby modes, the system will prevent apps from accessing the network and CPU Utilization. Once we connect the device to the charger then the system will exit from these two modes. Android will allow 10 secs of time for all the app to run their tasks in Doze mode, which is called maintenance window. At the end of each maintenance window, the system will enter into Doze mode again. This process will continue until the device is connected to a charger or the user is interacting with the application. In the process to save the battery the maintenance window won’t happen as frequently as one would like. The first window occurs an hour after last activity, the next after two, the next after four and so on. As the phone enters Doze mode, it brings about a few changes which are not in favour of the app that needs any one of the below
  • Network Access will be suspended
  • Wake locks will be ignored
  • Stops the sync adapters
  • Job Schedules will not be allowed
How to solve Doze Mode
Now Doze mode helps us in many ways but it comes at a cost; it renders a major part of few the application useless. Now we need to figure out a way around it. Here we will see how can we create an application and will send some pieces of information to the backend API in every 2 minutes regardless of whether the app is running or not. There are some actions that the Android OS will do to extend battery life, which in turn means Android from time to time needs to kill some processes to reclaim the memory for more important processes. A process with high memory usage is likely to be killed by Android when it is backgrounded. So think about it if you are keeping your background component and UI in the same process, then it will use more memory and it is more likely to get killed when the app goes in background. Now, we know what happens next; all the components (Service, Content Provider, Activities, Broadcast Receiver) are killed when Android kills the process. We have identified the issue, the solution presents itself i.e. decouple the background components from the UI; it is better to keep them in a different process.
How to Do it
To run an Android component in the different process, you have to specify the process name while you are defining that component in the manifest. By default, all the components will run in the same process. We can create a service in a different process by mentioning different process name. By default, if you want to run a service in the background forever, then you can return START_STICKY in the onStartCommand of service. The service will get recreated by the Android OS if this was destroyed for many reasons. But this will not work in devices which are running in Android version 6 and above due to Standby and Doze mode restrictions
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
super.onStartCommand(intent, flags, startId);
return START_STICKY;
}
These battery optimizations will take place automatically if the user is not interacting with the phone for some amount of time and a charger is not connected to the device, or if the screen is off. To get rid of these modes we have to create a foreground service which will save our process from StandBy mode, because the foreground service is giving an option for the user to interact with the service (Music player is an example for foreground service, which is running in the background but still the user can switch between the songs, play, pause and other operations can be performed )
Other Options
In Android Doze mode documentation they specify the use of FirebaseJobDispatcher, which will work even in Doze mode. But this will work only once in every 15 minutes. If we want to do some API calls in less than 15 minutes interval then FirebaseJobDispatcher is not a good option. As of my knowledge, AlarmManager is the only option to do this. As the name suggests this can be used for setting some alarms and which will get invoked at the desired interval and we can do some operations when the alarm fires. But the default function which we were using in older versions setExact() will not work in Android Versions above 6. Android has introduced two new methods from Marshmellow onwards, setAndAllowWhileIdle(), setExactAndAllowWhileIdle()) which will fire even in Doze mode (more than once per 9 minutes per app). But if our apps requirement is to call some API in every 1 or 2 minutes then we should swift off the doze mode.
Switch off Doze Mode
As you are aware by now there are no settings to disable Doze mode in Android. There are some ways to turn off the doze mode, those required some user interactions as well as connecting charger to the device or interacting with the app continuously. Switching the screen ON for a while is not a difficult task but at any time the user can Switch OFF the screen by pressing the power key in Android device. Hence few steps need to be taken so that our service will continue even after the user turns off the screen by pressing the hardware button. For that lets walks through the steps mentioned below
Step 1 Create a foreground service with different process name.
<service android:name=".backgroundmanager.BackgroundOperationsManagerService" android:enabled="true" android:process="com.hashedin.processname" />

Step 2 Create an alarm which will run every 30 secs, by checking the Android version
AlarmManager alarmManager = (AlarmManager)getSystemService(Context.ALARM_SERVICE);
Intent intent = new Intent(this, AlarmBroadCastReceiver.class);
PendingIntent pendingIntent = PendingIntent.getBroadcast(this,
ALARM_REQUEST_CODE, intent, PendingIntent.FLAG_UPDATE_CURRENT);
if (Build.VERSION.SDK_INT >= 23)
{
alarmManager.setExactAndAllowWhileIdle(AlarmManager.RTC_WAKEUP, 30000, pendingIntent);
}
else if (Build.VERSION.SDK_INT >= 19)
{
alarmManager.setExact(AlarmManager.RTC_WAKEUP, 30000, pendingIntent);
}
else
{
alarmManager.set(AlarmManager.RTC_WAKEUP, 30000, pendingIntent);
}

Step 3 Create a broadcast receiver for catching the Alarm, and it should be in the same process where the foreground service is running
<receiver android:name=".backgroundmanager.alarmmanager.AlarmBroadCastReceiver"
android:enabled="true"
android:process="com.hashedin.processname"/>
This Broadcast receiver should extend from WakeFulBroadCastReceiver class AlarmBroadCastReceiver extends WakefulBroadcastReceiver
@Override
public void onReceive(Context context, Intent intent)
{
if (screenWakeLock == null)
{
PowerManager pm = (PowerManager) context.getSystemService(Context.POWER_SERVICE);
screenWakeLock = pm.newWakeLock(PowerManager.SCREEN_DIM_WAKE_LOCK | PowerManager.ACQUIRE_CAUSES_WAKEUP,
 "ScreenLock tag from AlarmListener");
screenWakeLock.acquire();
}
Intent service = new Intent(context, WakefulService.class);
startWakefulService(context, service);
if (screenWakeLock != null)
screenWakeLock.release();
}
This will request a partial wakelock and start a service which is declared under the same process. This intent service will then start the foreground service again and which will do the desired operation as the device is safe from Doze and StandBy modes.
Step 4 (Optional) Create a firebasejobdispatcher which will be scheduled for every 15 minutes, in case the device goes to idle mode (this is an exceptional case) private void schedule
FirebasePeriodicTask() {
FirebaseJobDispatcher dispatcher = new FirebaseJobDispatcher(new GooglePlayDriver(this));
Job periodicJob = dispatcher.newJobBuilder()
// the JobService that will be called
.setService(PeriodicFirebaseJobService.class)
// uniquely identifies the job
.setTag(PERIODIC_TASK_TAG)
// Recurring job
.setRecurring(true)
// persist past a device reboot
.setLifetime(Lifetime.FOREVER)
// start between 0 and 120 seconds from now
.setTrigger(Trigger.executionWindow(0, 900))
// don't overwrite an existing job with the same tag
.setReplaceCurrent(true)
// retry with exponential backoff
.setRetryStrategy(RetryStrategy.DEFAULT_EXPONENTIAL)
// constraints that need to be satisfied for the job to run
.setConstraints(
// only run on an unmetered network
Constraint.DEVICE_IDLE
)
.build();
dispatcher.mustSchedule(periodicJob);
}
Which will also start the service every 15 minutes if the device is idle so that we can assure that the service will not get killed for many reasons.
Conclusion
This article discussed how we can prevent the android app from entering in to doze mode. As there are no settings to enable or disable Doze mode, you can simply enjoy the extra battery life. Happy coding!