跪拜 Guibai
← Back to the summary

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.

image.png

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.

image.png

Unthrottled Network Requests

Some apps, to ensure real-time data, frequently initiate network requests in the background without any backoff strategy.

image.png

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.

image.png

Location Service Abuse

image.png

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?

image.png

Then set reasonable interval time and distance, and remember to unregister the listener when done.

image.png

If possible, prioritize using the system-level FusedLocationProviderClient (though this is rarely used domestically, you know why).

image.png

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.

image.png

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.

image.png

For deferred tasks, it is recommended to use WorkManager.

image.png

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.

image.png

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.

image.png

In some scenarios where delayed execution is needed, use the set method instead of the setRepeating method.

image.png

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."

image.png

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.

image.png

Such broadcasts should only be registered when needed and immediately released when not in use.

image.png

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.

image.png

The correct approach is, of course, to unregister the sensor as soon as it is no longer needed.

image.png

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.

image.png

This kind of problem is basically solvable. If an object can be reused, reuse it; don't create it frequently.

image.png

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.

image.png

The approach to handling such problems is similar: clean up promptly when done, removing both messages and registered callbacks.

image.png

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.

image.png

The correct approach should be:

Extend the period and add constraints

image.png

Merge WorkRequests of the same type to avoid multiple Workers running independently

image.png

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.

image.png

The solution idea is the same: set a timeout and stop scanning promptly after use.

image.png

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.

image.png

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.

image.png

Animation Not Cancelled in Time

This problem mainly manifests as:

image.png

We should promptly pause or cancel animations when they are not in use or after they are finished, to prevent unnecessary battery loss.

image.png

Or use the View's built-in property animation to hide the View after the animation ends.

image.png

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.

Comments

Top 1 of 2 from juejin.cn, machine-translated. The original thread is authoritative.

路人甲酱100399

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]

Coffeeee

Sure thing