The 12 Android Coding Habits That Drain Batteries and Overheat Phones
In a previous article, although I said technical improvements are hard to do and not easy to implement, due to performance pressure, I still racked my brains to come up with several technical improvement requirements. One of them is to solve some power consumption issues in the project. Before that, I also sorted out some common behaviors that cause power consumption problems. You can take a look and preview them first.
WakeLock Not Released in Time
This is when the app acquires PARTIAL_WAKE_LOCK but forgets to release it, causing the CPU to be unable to enter sleep mode and the system to continuously consume power.
Just like above, only acquire is called but release is not, or an exception occurs and the release method is not reached. It is equivalent to continuously executing a time-consuming task. So in this situation, we either add a timeout or add a try-catch to ensure release is definitely reached.
Unthrottled Network Requests
Some apps, to ensure real-time data, frequently initiate network requests in the background without any backoff strategy.
Just like above, although there is an interval, the requests are still too frequent. The solution is to use WorkManager to handle such periodic tasks, leaving the optimization work to the system.
Location Service Abuse
- Using
GPS_PROVIDERfor continuous high-precision positioning - Not unregistering the listener after the location callback
- Setting the
minTimeparameter too small
The code above is quite scary. When the distance is 0, it continuously requests location data. No wonder the phone gets hot. The correct approach should be to choose reasonable accuracy based on the scenario. For non-critical scenarios, the accuracy can be slightly rougher. Who would care about that, right?
Then set reasonable interval time and distance, and remember to unregister the listener when done.
If possible, prioritize using the system-level FusedLocationProviderClient (though this is rarely used domestically, you know why).
Persistent Background Service
Some apps may set up a long-running Service in the background, and then use START_STICKY so that even if the service is killed, it can restart.
This is also a very power-consuming behavior. So what should be done? For short-term tasks, it is recommended to use coroutines and actively stop the task after completion.
For deferred tasks, it is recommended to use WorkManager.
Frequent AlarmManager Wakeups
Using AlarmManager.RTC_WAKEUP to frequently wake up the device, with short wakeup intervals. If multiple apps on the device all use AlarmManager, the number of device wakeups increases, the CPU is constantly restarted, leading to fast battery drain and device heating.
For the above problem, regarding multiple apps using AlarmManager to wake up the device, the correct approach should be to merge alarms and schedule them uniformly.
In some scenarios where delayed execution is needed, use the set method instead of the setRepeating method.
For scenarios where the wakeup requirement is not very high, use RTC more often instead of RTC_WAKEUP. The difference between the two is that the former will forcibly wake up the device, making the device "angry and heated," while the latter only triggers when the device wakes up naturally, which is gentler and won't make the device "angry and heated."
Implicit/Static Broadcast Abuse
Some apps frequently listen for system-sent broadcasts, such as ACTION_SCREEN_ON/OFF, ACTION_TIME_TICK, CONNECTIVITY_CHANGE, or use static registration for high-frequency broadcasts. This also causes power consumption problems.
Such broadcasts should only be registered when needed and immediately released when not in use.
Sensor Not Unregistered
This is also a classic problem. The sensor is registered but not unregistered at the appropriate place, causing the sensor chip to keep working.
The correct approach is, of course, to unregister the sensor as soon as it is no longer needed.
The power consumption differences for different sampling rates are as follows:
| Sampling Rate | Callback Interval | Applicable Scenario |
|---|---|---|
SENSOR_DELAY_FASTEST |
0ms | Games, Gestures |
SENSOR_DELAY_GAME |
~20ms | Games |
SENSOR_DELAY_UI |
~60ms | UI Updates |
SENSOR_DELAY_NORMAL |
~200ms | Pedometer, Direction |
Frequent GC and Memory Churn
Creating and destroying a large number of objects in a short period causes ART/Dalvik to frequently trigger GC. GC itself is a CPU-intensive operation that prevents the CPU from entering a low-power state, continuously consuming power. A typical scenario is creating objects during drawing.
This kind of problem is basically solvable. If an object can be reused, reuse it; don't create it frequently.
The question here is, why does frequent GC exacerbate power consumption? Mainly because multiple GCs mean the CPU needs to switch states multiple times, switching from idle state to active state. It never rests, always working, so of course it gets hot.
Handler / Timer Loop Tasks Not Cleaned Up
This type of problem mainly exists after an Activity or Fragment is destroyed, where Handler messages are still queued or a Timer is still executing, causing the CPU to spin idle.
The approach to handling such problems is similar: clean up promptly when done, removing both messages and registered callbacks.
Unreasonable WorkManager Configuration
Although some projects use WorkManager, due to unreasonable configuration, such as setting the interval for non-urgent periodic tasks too short, or not setting any constraints, and doing a lot of useless calculations in the task, it can also lead to some power consumption problems.
The correct approach should be:
Extend the period and add constraints
Merge WorkRequests of the same type to avoid multiple Workers running independently
Bluetooth / WiFi Scanning Not Stopped
This is similar to the sensor and broadcast problems above. The stopScan method is not called after use, leading to continuous scanning of surrounding Bluetooth devices or WiFi hotspots.
The solution idea is the same: set a timeout and stop scanning promptly after use.
Additionally, you can configure ScanFilter to filter out irrelevant devices, only scanning devices that meet the conditions, preventing the app from being frequently woken up to process useless scan results.
ContentObserver Abuse
Another similar problem. Some apps register a ContentObserver to listen for changes in system databases (such as SMS, contacts, media library) but do not cancel it when not needed, causing a callback to be triggered on every change and waking up the CPU. The solution to this problem is the same: the registration and unregistration actions should correspond to each other.
Animation Not Cancelled in Time
This problem mainly manifests as:
- Property animation still running after the page exits
ViewPropertyAnimatornot settingwithEndActionfor cleanupAnimatorSetlooping infinitely
We should promptly pause or cancel animations when they are not in use or after they are finished, to prevent unnecessary battery loss.
Or use the View's built-in property animation to hide the View after the animation ends.
Summary
Roughly, you can find that most behaviors revolve around not closing and recycling in time after use. These problems are relatively easy to solve and carry low risk. However, some issues that involve changing the framework or architecture must be well-planned and coordinated with testing resources. After all, such problems are mostly centered around core business logic. If a technical improvement causes business problems, it would truly be a case of effort without reward.
Top 1 of 2 from juejin.cn, machine-translated. The original thread is authoritative.
Bro, could the next article combine trace analysis to pinpoint the issues, and then what methods to use? That would feel more practically valuable [grin]
Sure thing