Quick answer: Use platform telemetry (Android BatteryStats, iOS MetricKit) to collect per-subsystem usage, normalize to per-hour drain weighted by device model and screen brightness, and compare against a reference game from your tier. Lab numbers lie; real-world aggregated drain is the only metric that correlates with store reviews complaining about battery life.

Battery life is the one performance metric mobile players consistently notice and consistently review-bomb you for when you get it wrong. Frame rate can be a bit worse and most players forgive it; a game that drops their phone from 80% to 35% in an hour becomes “that game that kills my battery” in the store reviews forever. Measuring battery drain accurately is harder than measuring FPS, because the signal lives outside your process. Here is how to build a real-world battery measurement pipeline for a mobile game.

Why Frame-Time Proxies Are Not Enough

The temptation is to assume that lower frame time means lower battery drain. It mostly does, but with big exceptions. The GPU scales its clock depending on load, so a game that hits 60 FPS with the GPU at 40% clock draws less than a game that hits 60 FPS with the GPU at 95% clock. Radio wakeups from analytics or live-ops calls cost more than their CPU time suggests. Thermal throttling eats battery with nothing to show for it. And the screen is often the biggest single draw — a game with a mostly-white UI pulls more than a game with a dark one on OLED devices.

For these reasons, every serious battery measurement uses the platform’s accounting, not a proxy.

Android: BatteryStats and BatteryManager

On Android, BatteryManager exposes a counter called BATTERY_PROPERTY_CHARGE_COUNTER that reports the remaining battery in microampere-hours. Poll it at game start and game end; the delta divided by session length gives you the drain rate.

BatteryManager bm = (BatteryManager) getSystemService(BATTERY_SERVICE);
long chargeStart = bm.getLongProperty(BatteryManager.BATTERY_PROPERTY_CHARGE_COUNTER);
long timeStart = System.currentTimeMillis();

// ... play session ...

long chargeEnd = bm.getLongProperty(BatteryManager.BATTERY_PROPERTY_CHARGE_COUNTER);
long delta_uah = chargeStart - chargeEnd;
long duration_h = (System.currentTimeMillis() - timeStart) / 3600000;
double drain_mah_per_hour = delta_uah / 1000.0 / duration_h;

Charge-counter drain is accurate but includes everything, including background apps. For a cleaner signal, use dumpsys batterystats on your test devices and parse the per-app attribution. For production, rely on the per-app wall-time drain reported in getHistoricalProcessExitReasons combined with your session length telemetry.

Also watch BatteryManager.EXTRA_TEMPERATURE. Thermal throttling kicks in above 42°C on most devices; a game that sustains high temperature is about to look worse on batteries because the SoC will downclock and render worse-looking frames at the same drain rate.

iOS: MetricKit

On iOS, the primary source is MetricKit. Subscribe to MXMetricManager and implement didReceive:. Once per day, iOS delivers a MXMetricPayload containing aggregated MXCPUMetric, MXGPUMetric, MXDisplayMetric, and MXCellularConditionMetric.

class MetricsReceiver: NSObject, MXMetricManagerSubscriber {
    func didReceive(_ payloads: [MXMetricPayload]) {
        for p in payloads {
            let cpu = p.cpuMetrics?.cumulativeCPUTime ?? .zero
            let gpu = p.gpuMetrics?.cumulativeGPUTime ?? .zero
            let bright = p.displayMetrics?.averagePixelLuminance
            report(cpu: cpu, gpu: gpu, luminance: bright)
        }
    }
}

MetricKit does not give you a single “battery drained” number, but you can reconstruct one from the subsystem totals. CPU time plus GPU time plus cellular plus display accounts for the bulk of drain on modern iPhones. Report each separately so you can see which subsystem regressed when your drain goes up.

Normalize to Per-Hour Drain

Raw drain numbers are not comparable across devices, settings, or session lengths. Normalize to drain-percent-per-hour, and stratify by three variables:

Device tier. A Pixel 3a drains faster than a Pixel 8 at the same workload. Group devices into tiers (low, mid, high) based on the GPU class and report drain per tier.

Screen brightness. Brightness is half the variance in real-world drain. Read it at session start and bucket into thirds (low, medium, high). High-brightness drain can be triple the low-brightness number for the same gameplay.

Session length. Short sessions over-weight startup cost. Ignore sessions under two minutes, and for longer sessions compute drain rate only over the steady-state portion (after the first 30 seconds).

Compare to a Baseline

Absolute numbers are hard to reason about. “Our game drains 12% per hour” is meaningless without context. Pick a reference game in your tier and report your drain as a ratio.

For casual games, I use Candy Crush as the lightweight reference — it drains roughly 6% per hour on mid-tier Android. For action games, a Genshin Impact session drains roughly 25% per hour as the heavy reference. Your game lives between them. Report “our drain is 1.4x Candy Crush, 0.6x Genshin” and the number becomes intuitive.

To measure the baseline yourself, run a scripted session on a rooted Android device with dumpsys batterystats --reset, play the reference game for twenty minutes under controlled brightness, and record the per-hour drain. Repeat monthly so your reference stays calibrated against OS updates.

Ship the Signal, Watch the Trend

Emit a battery-drain telemetry event at the end of every session: duration, start and end charge percent, average brightness, device model, and game version. Aggregate server-side into a per-version per-tier drain percentile (P50 and P90).

Alert on P90 regressions. If a release bumps P90 drain by more than 15% in any device tier, hold the release. This single rule catches virtually every battery regression before it ships to full rollout.

What to Do When Drain Is Too High

Once you have the data, the levers are familiar: cap frame rate to 30 FPS when the player is idle, shut down analytics on low-signal networks, use a darker default UI on OLED, offload physics ticks to a lower rate when off-screen, and pre-bake anything you are currently computing at runtime. Measure the drain delta of each change — do not assume anything saves battery without verifying.

“Our first live-ops release doubled our P90 drain because we added a heartbeat ping every five seconds. MetricKit caught it the day after rollout; we shipped the hotfix the day after that.”

Related Issues

For a broader look at mobile-specific bug reporting, see automated bug reporting for mobile games. For performance telemetry adjacent to battery, read best practices for game error logging.

Battery drain is the one performance metric your players will review-bomb you for. Measure it the way they experience it, not the way your profiler sees it.