Optimized API | |
Fused Location | The fused location provider is one of the location APIs in Google Play services which combines signals from GPS, Wi-Fi, and cell networks, as well as accelerometer, gyroscope, magnetometer and other sensors. It is officially recommended to maximize battery life. Thus, developer has to set up Google Play Service in her gradle file with a dependency to com.google.android.gms:play-services-location:x.y.z , and then to import from com.google.android.gms.location instead of the android.location package of the SDK. |
Bluetooth Low-Energy | In contrast to classic Bluetooth, Bluetooth Low Energy (BLE) is designed to provide significantly lower power consumption. Its purpose is to save energy on both paired devices but very few developers are aware of this alternative API. From the Android client side, it means append android.bluetooth.le.* imports to android.bluetooth.* imports in order to benefits from low-energy features. |
Lazy Loading | When displaying scrollable data on screen, the new Jetpack Compose API introduced lazy views instead of ListView , GridView and even RecycleView . These components use the technique of lazy loading, which consists of loading data only when it arrives at the display area. Import androidx.compose.foundation.lazy.* to benefit from objects like LazyColumn , LazyRow , LazyVerticalGrid or LazyHorizontalGrid . |
Leakage | |
Media Leak | Creation of a Media Recorder object with new MediaRecorder() is used to record audio and video, while creation of a Media Player object with new MediaPlayer() can be used to control playback of audio/video files and streams. Both classes own a release() method. In addition to unnecessary resources (such as memory and instances of codecs) being held, failure to call this method immediately if a media object is no longer needed may also lead to continuous battery consumption for mobile devices. |
Sensor Leak | Most Android-powered devices have built-in sensors that measure motion, orientation, and various environmental conditions. In addition to these are the image sensor (a.k.a Camera) and the geopositioning sensor (a.k.a GPS). The common point of all these sensors is that they are expensive while in use. Their common bug is to let the sensor unnecessarily process data when the app enters an idle state, typically when paused or stopped. Consequently, calls must be carefully pairwised: SensorManager#registerListener()/unregisterListener() for regular sensors, Camera#open()/Camera#release() for the camera and LocationManager#requestLocationUpdates()/removeUpdates() for the GPS. Failing to do so can drain the battery in just a few hours. |
Everlasting Service | If someone calls Context#startService() then the system will retrieve the service (creating it and calling its onCreate() method if needed) and then call its onStartCommand(Intent, int, int) method with the arguments supplied by the client. The service will at this point continue running until Context#stopService() or Service#stopSelf() is called. Failing to call any of these methods can lead to uncontrolled energy leakage. |
Bottleneck | |
Internet In The Loop | Opening and closing internet connection continuously is extremely battery-inefficient since HTTP exchange is the most consuming operation of the network. This bug typically occurs when one obtain a new HttpURLConnection by calling URL#openConnection() within a loop control structure (while, for, do-while, for-each). Also, this bad practice must be early prevented because it is the root of another evil that consists in polling data at regular intervals, instead of using push notifications to save a lot of battery power. |
Wifi Multicast Lock | Normally the Wifi stack filters out packets not explicitly addressed to the device. Acquiring a Multicast Lock with WifiManager.MulticastLock#acquire() will cause the stack to receive packets addressed to multicast addresses. Processing these extra packets can cause a noticeable battery drain and must be disabled when not needed with to a call to WifiManager.MulticastLock#release() . |
Uncompressed Data Transmission | Transmitting a file over a network infrastructure without compressing it consumes more energy than with compression. More precisely, energy efficiency is improved in case the data is compressed at least by 10%, transmitted and decompressed at the other network node. From the Android client side, it means making a post HTTP request using a GZIPOutputStream instead of the classical OutputStream , along with the HttpURLConnection object. |
Uncached Data Reception | Caching all of the application's HTTP responses to the filesystem (application-specific cache directory) so they may be reused, allow to save energy. To that purpose the class HttpResponseCache supports HttpURLConnection and HttpsURLConnection ; there is no platform-provided cache for further clients. Installing the cache at application startup is done with the static method HttpResponseCache.install(File directory, long maxSize) . |
Sobriety | |
Dark UI | Developers are allowed to apply native themes for their app, or derive new ones throught inheritence. This decision has a significant impact on energy consumption since displaying dark colors is particularly beneficial for mobile devices with (AM)OLED screens (under certain conditions). By default Android will set a dark style and hence switching to the light theme (parent style Theme.*.Light ) within the manifest should be avoided. Of course, custom resources like bright colors values and bitmap images with too high luminance should be avoided too. |
Day Night Mode | Dark theme is available in Android 10 (API level 29) and higher. It allows the user to switch its system to a dark mode and the apps ought to adapt accordingly. In order to support Dark theme, the app's theme must inherit from the DayNight theme with parent="Theme.*.DayNight" . In addition, developers may provide resources specific to DayNight mode in a directory res/values-night/ . |
Brightness Override | Introduced in Android 9, the adaptive brightness feature raises or lower the brightness of the screen depending on the light in the current environment. For some reasons, developpers may disable this feature programmatically, setting the field WindowManager.LayoutParams#screenBrightness with the constant BRIGHTNESS_OVERRIDE_FULL . This intelligent feature was introduced to improve battery life. Its deactivation is a very bad idea. |
Thrifty Geolocation | Location awareness is one of the most popular features used by apps. The first thing is to try to get the best possible provider (LocationManager#getBestProvider() ) based on an energy criteria thanks to Criteria#setPowerRequirement(int level) , using constant POWER_LOW instead of POWER_HIGH or POWER_MEDIUM . Next, with a call to LocationManager#requestLocationUpdates (long minTime, float minDistance, Criteria criteria, PendingIntent intent) , the provider will only send your application an update when the location has changed by at least minDistance meters, AND at least minTime milliseconds have passed. So minTime should be the primary tool to conserving battery life, and, to a lesser extent, minDistance , these two must be imperatively greater than 0. |
Thrifty BLE | With Bluetooth Low Energy technology (see BLE API smell), a Bluetooth Smart Ready device (the master) will establish a link with a Bluetooth Smart device (the slave). Most often, the slave is a GATT server and the master is a GATT client. GATT capable devices can be discovered using BLE scan process. So you should always use BluetoothLeScanner#startScan() with the scan settings set to SCAN_MODE_LOW_POWER (Default scan mode). In addition, invoke BluetoothGatt#requestConnectionPriority(int connectionPriority) with the value CONNECTION_PRIORITY_LOW_POWER . Lastly, the default and preferred advertising mode is ADVERTISE_MODE_LOW_POWER when calling AdvertiseSettings.Builder#setAdvertiseMode(int advertiseMode) . |
Thrifty Motion Sensor | The rotation vector sensor is the most frequently used sensor for motion detection and monitoring. When using SensorManager#getDefaultSensor(int type) , always prefer the constant TYPE_GEOMAGNETIC_ROTATION_VECTOR which is similar to TYPE_ROTATION_VECTOR , but using a magnetometer instead of using a gyroscope. This sensor uses lower power than the other rotation vectors, because it doesn't use the gyroscope. However, it is more noisy and will work best outdoors. |
Thrifty Notification | Giving informations to the end-user through notifications is an important aspect of a modern app. However, a notification does not necessarily need to be loud and vibrant to achieve its purpose. Default mode is enough. That is why when building a notification with NotificationCompat.Builder , there should be no extra calls to the methods setVibrate() nor setSound() on the builder object. Please consider NotificationChannel#setVibrationPattern() since API 31. |
Vibration-free | Shaking of an Android device is possible in all circumstances with a call to getSystemService(Context.VIBRATOR_SERVICE) (API 26). Behind this effect stands a specific miniature hardware component, motor or actuator, that consumes power. As a consequence, its usage must be discouraged, especially since its added value is not clear. Please consider Context.VIBRATOR_MANAGER_SERVICE since API 31. |
Torch-free | Programmatically enabling torch mode with CameraManager#setTorchMode(..., true) should be avoided as the torch will drain the battery unnecessarily. |
High Frame Rate | In Android 11 (API level 30) or higher, a call to Surface#setFrameRate(float frameRate, int compatibility) results in a change to the display refresh rate. However, a regular app displays 60 frames per second (60Hz). In order to optimize content refreshes and hence saving energy, this frequency should not be raised to 90Hz or 120Hz, despite this is now supported by many devices. This bad practice can be detected when the value of the frameRate argument is greater than 60f. |
Extraneous Animation | Avoiding extraneous animations in the UI is a good practice for saving battery power. This can be checked either in the Java code when an object is instance of Animator (sub)class, or simply through the presence of xml files in the res/animator/ resource directory. |
Extraneous Init | If possible, avoid initialising anything unnecessarily in your Application class in order to improve start-up latency. Many libraries offer on-demand initialisation, allowing you to call them only when you need them. |
Hardware acceleration | Hardware acceleration for 2D rendering is enabled by default if your Target API level is >=14. Because of the increase in resources required to activate hardware acceleration, your application will use more RAM. However, it can be explicitly disabled at the application or activity level with the specific attribute android:hardwareAccelerated="false" in the Android manifest file, or programmatically. |
Auto Completion | A special type of text field allows completion suggestions to be displayed automatically as the user types. Unless the data is provided statically, using an <AutoCompleteTextView> element in an XML layout opens the door to numerous requests to a remote server, and should be avoided wherever possible. Sometimes a simple placeholder can effectively guide the user. |
Idleness | |
Keep Screen On | To avoid draining the battery, an Android device that is left idle quickly falls asleep. Hence, keeping the CPU on should be avoided, unless it is absolutely necessary. If so, developers typically use the FLAG_KEEP_SCREEN_ON in their activity. Another way to implement this is in their application's layout XML file, by using the android:keepScreenOn attribute. |
Keep CPU On | To avoid draining the battery, an Android device that is left idle quickly falls asleep. Hence, keeping the screen on should be avoided, unless it is absolutely necessary. If so, developers typically use a Power Manager system service feature called wake locks by invoking PowerManager.WakeLock#newWakeLock(int levelAndFlags, String tag) , along with the specific permission WAKE_LOCK in their manifest. |
Durable Wake Lock | A wake lock is a mechanism to indicate that your application needs to have the device stay on. The general principle is to obtain a wake lock, acquire it and finally release it. Hence, the challenge here is to release the lock as soon as possible to avoid running down the device's battery excessively. Missing call to PowerManager#release() is a built-in check of Android lint (Wakelock check) but that does not prevent abuse of the lock over too long a period of time. This can be avoided by a call to PowerManager.WakeLock#acquire(long timeout) instead of PowerManager.WakeLock#acquire() , because the lock will be released for sure after the given timeout expires. |
Rigid Alarm | Applications are strongly discouraged from using exact alarms unnecessarily as they reduce the OS's ability to minimize battery use (i.e. Doze Mode). For most apps prior to API 19, setInexactRepeating() is preferable over setRepeating() . When you use this method, Android synchronizes multiple inexact repeating alarms and fires them at the same time, thus reducing the battery drain. Similarly, setExact() and setExactAndAllowWhileIdle() can significantly impact the power use of the device when idle, so they should be used with care. High-frequency alarms are also bad for battery life but this is already checked by Android lint (ShortAlarm built-in check). |
Continuous Rendering | For developers wishing to display OpenGL rendering, when choosing the rendering mode with GLSurfaceView#setRenderMode(int renderMode) , using RENDERMODE_WHEN_DIRTY instead of RENDERMODE_CONTINUOUSLY (By default) can improve battery life and overall system performance by allowing the GPU and CPU to idle when the view does not need to be updated. |
Keep Voice Awake | During a voice interaction session, VoiceInteractionSession#setKeepAwake(boolean keepAwake) allows to decide whether it will keep the device awake while it is running a voice activity. By default, the system holds a wake lock for it while in this state, so that it can work even if the screen is off. Setting this to false removes that wake lock, allowing the CPU to go to sleep and hence does not let this continue to drain the battery. |
Power | |
Ignore Battery Optimizations | An app holding the Manifest.permission.REQUEST_IGNORE_BATTERY_OPTIMIZATIONS ask the user to allow it to ignore battery optimizations (that is, put them on the whitelist of apps). Most applications should not use this; there are many facilities provided by the platform for applications to operate correctly in the various power saving modes. |
Companion in background | A negative effect on the device's battery is when an app is paired with a companion device (over Bluetooth, BLE, or Wi-Fi) and that it has been excluded from battery optimizations (run in the background) using the declaration Manifest.permission.REQUEST_COMPANION_RUN_IN_BACKGROUND . |
Charge Awareness | It's always good that an app has different behavior when device is connected/disconnected to a power station, or has different battery levels. One can monitor the changes in charging state with a broadcast receiver registered on the actions ACTION_POWER_CONNECTED and ACTION_POWER_DISCONNECTED , or monitor significant changes in battery level with a broadcast receiver registered on the actions BATTERY_LOW and BATTERY_OKAY . |
Save Mode Awareness | Taking into account when the device is entering or exiting the power save mode is higly desirable for the battery life. It implies the existence of a broadcast receiver registered on the action ACTION_POWER_SAVE_MODE_CHANGED , or programmaticaly with a call to PowerManager#isPowerSaveMode() . |
Battery-constrained Work | You can use the WorkManager library to perform work on an efficient schedule that considers whether specific conditions are met, such as power status. A worker can be scheduled to run, provided the device's battery isn't low for exemple. This can be checked by a call to WorkRequest.Builder#setConstraints() where the constraint object built involves Constraints.Builder#setRequiresBatteryNotLow(true) or, in its extremist form, Constraints.Builder#setRequiresCharging(true) . |
Batch | |
Service@Boot-time | Services are long-living operations, as components of the apps. However, they can be started in isolation each time the device is next started, without the user's acknowledgement. This technique should be discouraged because the accumulation of these silent services results in excessive battery depletion that remains unexplained from the end-user's point of view. In addition, end-users know how to kill applications, but more rarely how to kill services. Thus, any developer should avoid having a call to Context#startService() from a Broadcast Receiver component that has specified an intent-filter for the BOOT_COMPLETED or QUICKBOOT_POWERON action in the manifest. |
Sensor Coalesce | With SensorManager#registerListener(SensorEventListener, Sensor, int) the events are delivered as soon as possible. Instead, SensorManager#registerListener(SensorEventListener, Sensor, int, int maxReportLatencyUs) allows events to stay temporarily in the hardware FIFO (queue) before being delivered. The events can be stored in the hardware FIFO up to maxReportLatencyUs microseconds. Once one of the events in the FIFO needs to be reported, all of the events in the FIFO are reported sequentially. Setting maxReportLatencyUs to a positive value allows to reduce the number of interrupts the AP (Application Processor) receives, hence reducing power consumption, as the AP can switch to a lower power state while the sensor is capturing the data. |
Job Coalesce | The Android 5.0 Lollipop (API 21) release introduces a job scheduler API via the JobScheduler class. Compared to a custom Sync Adapter or the alarm manager, the Job Scheduler supports batch scheduling of jobs. The Android system can combine jobs so that battery consumption is reduced. It means that at least one job scheduler exists through a call to the method JobScheduler#shedule(JobInfo job) . |
Release | |
Same dependencies | This occurs when equivalent libraries are incorporated into the app. If they do quite the same things, choose only one. This can be checked in the dependencies section of build.gradle along with a human-curated list of equivalent libraries for Android (e.g., Volley≈Retrofit≈okHttp≈android-async-http≈Ktor). |
Duplicate dependencies | This occurs when several versions of the same library are incorporated into the app. The weight of the app is fatally increased without changing the features. This can be checked in the dependencies section of build.gradle . |
Convert to WebP | WebP is an image format developed by Google, presenting smaller yet more visually-pleasing pictures. Typically, WebP compresses images by an average of 30 percent more than JPEG with no loss in quality. From Android 4.0 (API level 14) and higher, using this image format instead of .gif, .png or .jpeg in the /res/drawable/ folder is a good practice since it allows to decrease the size of apks. Notice that Android Studio allows you to easily convert images to the WebP format. |
Clear cache | A good (but quite rare) practice in the long term is to delete the whole cache directory of the app onto the target device. This requires the source code of the app to contains at least one call to the method Context.cacheDir#deleteRecursively() . |
Shrink Resources | For those that are still not publishing with the Android App Bundle format, it is possible to minimize the app's size via the build.gradle file, by using the following lines. Especially useful for popular apps, it reduces the amount of downloaded data required for installation and updating, across millions of devices. Check buildTypes { release { shrinkResources true } } |
Dynamic Feature | Play Feature Delivery uses advanced app bundle functionality to enable conditional or on-demand distribution of certain features of your application. Hence, you can reduce the size of downloaded apps by using the android.dynamicFeatures property in the build.gradle file. |
Longevity | |
Aging devices | The minSdkVersion set in the build.gradle file determines which APIs are available at build time, and determines the minimum version of the OS that the code will be compatible with. The lower the better so as not to exclude owners of older devices. |
Fat app | When an app exceeds the limit of 65 536 method references, the configuration multidex must be enabled with multiDexEnabled true in the defaultConfig section of build.gradle . Fat apps rarely install on space-limited devices. |
Low Memory | Methods such as ActivityManager#isLowRamDevice() and ActivityManager#getMemoryClass() help you determine memory constraints at runtime. This information allows you to reduce memory usage. Compatibility with low-memory devices is good practice to help them last. |