跪拜 Guibai
← All articles
Android · Kotlin · Architecture

The 12 Android Coding Habits That Drain Batteries and Overheat Phones

By Coffeeee ·
Read original on juejin.cn ↗ Google Translate ↗ Alt translation

Battery drain and overheating are the top one-star review drivers on Google Play, yet the root causes are rarely exotic — they are missing release calls, zero-interval polling loops, and listeners that outlive their screens. A systematic sweep through these twelve patterns can recover hours of screen-on time without a single architecture change.

Summary

A catalog of twelve Android power bugs walks through the exact code that keeps the CPU from sleeping. Unreleased WakeLocks, background services with START_STICKY, and location requests with a zero-meter minimum distance all force the processor to stay active. Each pattern gets a before-and-after code sample showing the fix — usually a timeout, a try-catch around release, or a switch to WorkManager.

Several of the problems share the same root cause: registering a listener, sensor, scanner, or observer and never unregistering it. Sensors keep sampling at game-rate frequencies, Bluetooth scans run indefinitely, and ContentObservers fire on every system database change long after the screen that needed them is gone. The fixes are mechanical — pair every register with an unregister in the right lifecycle callback — but the post makes clear these are the bugs that survive code review because the drain is invisible until a user complains about heat.

The deeper takeaway is that power bugs fall into two buckets. The first bucket — missing cleanup calls — is cheap to fix and low-risk. The second bucket involves architectural choices like alarm scheduling, WorkManager configuration, and GC pressure from per-frame allocations. Those require planning and test coordination because they touch core business flows, and a botched optimization that breaks a feature costs more than the battery life it saves.

Takeaways
Acquiring a PARTIAL_WAKE_LOCK without a corresponding release — or without a try-catch around it — keeps the CPU awake indefinitely; adding a timeout or a finally block fixes it.
Background network polling with no backoff strategy burns radio and CPU; WorkManager hands scheduling to the OS and coalesces wake windows across apps.
Requesting GPS_PROVIDER updates with minTime=0 and minDistance=0 turns location into a continuous high-power drain; pick accuracy and interval appropriate to the feature.
Services flagged with START_STICKY resurrect after being killed and run forever; short tasks belong in coroutines that self-stop, deferred work in WorkManager.
AlarmManager.RTC_WAKEUP forcibly wakes the device from deep sleep; prefer RTC for non-urgent work so the alarm fires only during natural wake windows, and use set() over setRepeating() to avoid fixed-interval storms.
Static registration for high-frequency system broadcasts like ACTION_SCREEN_ON or CONNECTIVITY_CHANGE wakes the app on every event; register dynamically and unregister immediately when the relevant screen goes away.
Sensors left registered after a screen exits keep the sensor chip active at the last sampling rate; SENSOR_DELAY_FASTEST (0ms) draws far more power than SENSOR_DELAY_NORMAL (200ms).
Per-frame object allocation in onDraw or animation loops triggers repeated GC, which is CPU-intensive and blocks low-power state entry; reuse Paint, Rect, and Path objects instead.
Handler messages and Timer tasks queued in a destroyed Activity or Fragment spin the CPU with no visible effect; clear the message queue and cancel the timer in onDestroy.
WorkManager periodic requests with no constraints and short intervals defeat the point of deferred execution; add network and charging constraints and merge same-type workers into a single chain.
Bluetooth and WiFi scanning that never calls stopScan keeps the radio active; set a scan timeout and apply ScanFilter to ignore irrelevant devices so the app isn't woken for every nearby SSID.
Property animations and AnimatorSets that loop or outlive their parent view consume CPU cycles for invisible frames; cancel or pause them in onStop or onDetachedFromWindow.
Conclusions

The post's own summary is the real insight: most power bugs are just missing cleanup calls — cheap, low-risk fixes that survive review because the damage is invisible until a user feels the heat.

Several patterns (sensors, broadcasts, ContentObservers, Bluetooth/WiFi scanning) are structurally identical — register-and-forget — which suggests a single lint rule or lifecycle wrapper could catch them all.

The distinction between easy fixes and architectural changes matters operationally: a dev can ship a WakeLock timeout in a point release, but rescheduling alarms or merging WorkManager chains touches core flows and needs QA bandwidth the team may not have.

Frequent GC as a power drain is underappreciated in Android performance guides; the mechanism is straightforward — GC keeps the CPU out of idle states — but the fix (object pooling in draw paths) is often skipped because the allocation site looks harmless.

Concepts & terms
PARTIAL_WAKE_LOCK
An Android PowerManager lock that keeps the CPU running while allowing the screen and keyboard backlight to turn off. If not released, the CPU never enters deep sleep, draining battery continuously.
START_STICKY
A Service return flag that tells the OS to restart the service if it is killed after onStartCommand returns. The service restarts with a null Intent, which often leads to a permanent background process.
RTC_WAKEUP vs RTC
Two AlarmManager clock types. RTC_WAKEUP forcibly wakes the device from sleep to fire the alarm; RTC fires only when the device is already awake. Using RTC for non-urgent work avoids unnecessary wakeups.
FusedLocationProviderClient
Google Play Services' location API that automatically chooses the best provider (GPS, WiFi, cell) and batches requests across apps to reduce power. Rarely used in Chinese Android ecosystems due to GMS availability.
SENSOR_DELAY_FASTEST
A sensor sampling rate constant that delivers data as fast as the hardware allows (0ms interval). It draws maximum power and is only appropriate for real-time gestures or games.
Memory churn / GC pressure
Rapid allocation and deallocation of short-lived objects that forces the garbage collector to run frequently. Each GC cycle is CPU-intensive and prevents the processor from entering a low-power idle state.
From the discussion
Featured comments
路人甲酱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

See top comments, translated →
Source: juejin.cn ↗ Google Translate ↗ Backup ↗