• Don Peter

How to avoid energy bugs caused by wake lock in Android apps.

One of the most crucial aspects that a buyer looks for while buying a smartphone would be its battery life.


Bugs in apps that can cause battery drain are called energy bugs. Unlike typical software bugs, energy bugs are not fatal or do not disrupt the normal working of an app.


Energy bugs will create a need for users to frequently recharge their Android devices.


When an Android device is in idle state, it quickly falls asleep in order to conserve battery. Android OS works in such a way that when the device turns inactive, the screen turns off and the CPU gradually sleeps.


But there may be situations when developers need to keep the CPU and/or screen active for completing long running tasks. In certain use cases like movie streaming, turn by turn navigation apps, or for keeping a background service alive developers tend to use wake locks in apps to keep the device awake.


PowerManager powerManager = (PowerManager) getSystemService(POWER_SERVICE);
WakeLock wakeLock = powerManager.newWakeLock(
                            PowerManager.PARTIAL_WAKE_LOCK, 
                                   "AppTag::TagForWakelock");
wakeLock.acquire();

Code snippet - Wake Lock


Wake locks determine whether the CPU or screen should stay awake or go to sleep. Android OS mainly has four types of wake locks. They are partial wake lock, full wake lock, screen dim wake lock and screen bright wake lock.


In partial wake lock and full wake lock, the CPU is kept awake. But in screen dim wake lock and screen bright wake lock, both the CPU and screen are kept awake.


Types of Energy Bugs


No sleep bugs

No sleep bugs prevents devices from going idle, resulting in prolonged battery drain in the Android device. No sleep bugs are created when an app acquires a wake lock for a task but is not released once the task is completed because of programming errors, race conditions or even due to exceptions triggered when releasing a wake lock.


Loop bugs

Loop bug occurs when an app enters a looping state, performing trivial tasks, which drains the device’s battery. Developers tend to write a conditional loop that could potentially run for a long time in order to keep a service alive even after the app goes into background state. This should be avoided at all costs.


Apart from no sleep bugs and loop bugs, hardware bugs and OS level bugs can also cause battery drain. However, these bugs are beyond the control of us developers to fix.


Ways to avoid Energy Bugs

There are alternatives that developers can consider in order to avoid no-sleep bugs and loop bugs to keep the device awake.


Using Screen-On flag

In case the developer wants to keep the screen turned on even without user interaction, consider using flag_keep_screen_on in the concerned activity. The advantage of using flag_keep_screen_on is that the flag will only function during the lifetime of an activity.


Screen-On flag is ideal for use cases like navigation apps, gaming apps, video player apps etc.



public class MovieCastActivity extends Activity {
  @Override
  protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_movie_cast);
    getWindow().addFlags(WindowManager.LayoutParams.
                                       FLAG_KEEP_SCREEN_ON);
  }
}

Code snippet - Screen-On flag


Using Download Manager

If the app needs to handle long running HTTP downloads, developers should consider using Download Manager.



// Create request object with all necessary parameters
DownloadManager.Request downloadRequest = new DownloadManager.
                Request(Uri.parse(urlEndPoint))
            .setNotificationVisibility
                (DownloadManager.Request.VISIBILITY_HIDDEN) 
            .setDestinationUri
                (Uri.fromFile(destinationFile))			
            .setTitle("Name of file")			
            .setDescription("file details")			
            .setAllowedOverMetered(true);		
            
// Get Download Manager object which will enqueue the request for download
DownloadManager manager = (DownloadManager) 
            getSystemService(Context.DOWNLOAD_SERVICE);
manager.enqueue(downloadRequest); //This will trigger the download.
            

Code snippet - Download Manager


Some of the use cases are cloud based file manager apps, game apps that requires users to download large files or even video streaming apps with the capability to download large video files for offline viewing or otherwise.


Using Sync Adapters

If the app is periodically synchronizing data from a remote server, developers can consider using sync adapters. Framework helps to centralize data transfer, thereby improving battery performance.


Sync Adapters are ideal for situations where your app level data store (local database) needs to be in sync with server data store (remote database).


Please find detailed information on how to implement Sync Adapters - https://developer.android.com/training/sync-adapters/creating-sync-adapter


Using Job Scheduler

Job Scheduler is an apt tool to schedule and background tasks. For instance for an online quiz app, daily challenges should start at a particular time. Using Job Scheduler, developers can make sure that all the required data is downloaded by the time user logs in for the challenge.


// Create Component from QuizChallengeSyncJobService class.
ComponentName quizServiceComponent = new ComponentName(context, 
                QuizChallengeSyncJobService.class);
                
// Create JobInfo Builder from the quizServiceComponent with necessary parameters for the Job Scheduler to execute
JobInfo.Builder jobBuilder = new JobInfo.Builder(0,
                quizServiceComponent);
jobBuilder.setOverrideDeadline(3 * 1000); //maximum allowed delay

// Create Job Scheduler object using the JobInfo Builder and call schedule function.
JobScheduler quizChallengeJobScheduler =
                context.getSystemService(JobScheduler.class);
quizChallengeJobScheduler.schedule(jobBuilder.build());
        

Code snippet - Job Scheduler


Developers should consider using wake lock support only in unavoidable situations, though it is generally discouraged.