| name | battery-profiling |
| description | Attribute battery drain to CPU, radio, GPS, display, and background work using Android Battery Historian and Xcode Energy Log / Instruments Energy gauge. |
Battery Profiling
Instructions
Battery drain is always attributable — CPU, radio wakeups, GPS, screen brightness, or unreleased wakelocks. This skill shows how to capture a reliable energy trace and how to read it.
1. What to Measure
- mAh per hour in a representative workload (idle, active feed, video).
- CPU time (user + kernel).
- Radio time (Wi-Fi on, cellular on, data transfer).
- Wakelocks / assertions count and duration.
- GPS / sensor active time.
- Screen on-time at which brightness.
Budgets (rule of thumb):
| State | Foreground (%/h) | Background (%/h) |
|---|
| Idle | < 3 | < 0.5 |
| Browsing | < 8 | — |
| Video | < 15 | — |
2. Android — Battery Historian
-
adb shell dumpsys batterystats --reset
-
Unplug the device. Use the app for 10–20 minutes.
-
adb bugreport bugreport.zip
-
Open Battery Historian or run locally via Docker:
docker run -p 9999:9999 bhaavan/battery-historian
-
Upload bugreport.zip. Inspect:
- Wakelocks — per-app held wakelocks. Any partial wakelock > 30 s is suspect.
- Kernel wakeups — modem, Wi-Fi, alarm. Reduce with JobScheduler / WorkManager constraints.
- Process CPU — top processes while screen-off.
- Doze transitions — confirm the app respects Doze (no network during idle maintenance windows).
3. Android — On-Device Energy Profiler
Android Studio Profiler → Energy row on API 26+. Shows a unit-less high/medium/low indicator with a breakdown:
- CPU
- Network (radio-on cost + transfer cost)
- Location (GPS on)
Click a bar to see wakelock, alarm, and job events with stack traces — correlates code to energy cost.
4. iOS — Instruments Energy Log + Xcode Gauges
Xcode debugger gauge panel shows live Energy Impact. For reproducible measurements:
-
Product → Profile → Energy Log template (requires a physical device, ideally unplugged — use wireless debugging).
-
Record for 2–5 minutes under the scenario.
-
Track views:
- Energy Usage — high/low bars per second.
- CPU Activity — user vs kernel.
- Networking — bytes in/out and connection lifetime.
- Location — GPS/Wi-Fi scan activity.
- Display Brightness, Bluetooth, etc.
-
Generate a diagnostic .logarchive:
xcrun simctl spawn booted log collect
MetricKit in production:
class Delegate: NSObject, MXMetricManagerSubscriber {
func didReceive(_ payloads: [MXMetricPayload]) {
for p in payloads {
let cpu = p.cpuMetrics.cumulativeCPUTime
let cells = p.cellularConditionMetrics
}
}
}
5. Common Drain Sources
| Symptom | Likely cause | Fix |
|---|
| High radio time even when idle | Chatty polling, small requests every 5–30 s | Batch, lengthen interval, use silent push. |
| Sustained wakelock | PARTIAL_WAKE_LOCK held across network retry loops | Use WorkManager with NetworkType.CONNECTED; drop explicit lock. |
| High CPU while screen off | Infinite timers, MQTT reconnect storm | Exponential backoff, FCM push instead of polling. |
| GPS active for minutes | "Always" location permission, foreground tracking bugs | Use significant-change / geofence APIs; stop when not needed. |
| Bluetooth always scanning | BLE scan without filters | Use ScanFilter + SCAN_MODE_LOW_POWER. |
| iOS "high energy" from WebView | Animated GIFs, complex CSS, autoplay video | Pause WebView on disappear; freeze animations. |
6. Guardrails in Code
Kotlin — prefer coroutines with cancellation and network constraints:
val req = OneTimeWorkRequestBuilder<SyncWorker>()
.setConstraints(
Constraints.Builder()
.setRequiredNetworkType(NetworkType.UNMETERED)
.setRequiresBatteryNotLow(true)
.setRequiresCharging(false)
.build()
).build()
WorkManager.getInstance(ctx).enqueue(req)
Swift — use BGProcessingTaskRequest with requiresExternalPower/requiresNetworkConnectivity:
let req = BGProcessingTaskRequest(identifier: "com.app.sync")
req.requiresNetworkConnectivity = true
req.requiresExternalPower = false
try BGTaskScheduler.shared.submit(req)
7. Compare Before/After
Always compare two runs with the same workload, same starting battery level, same network conditions. Tools:
- Android Automator +
monkey for reproducible workloads.
- XCUITest +
XCTApplicationLaunchMetric/custom metrics for iOS.
Checklist