14、电量优化:Battery Historian工具、JobScheduler与WorkManager、AlarmManager优化、传感器管理
电量优化,说白了就是让App在用户不知不觉中把活干了,别偷偷摸摸吃电。我见过太多App,功能做得挺花哨,结果用户晚上充完电,早上起来发现掉了20%——这种App基本活不过两周就被卸载了。
今天咱们聊聊Android电量优化的四个核心方向。嗯,都是我在实际项目中踩过坑、填过坑的东西。
14.1 Battery Historian:先诊断,再下药
优化电量之前,你得先知道电都去哪了。Battery Historian就是干这个的。
我个人习惯,每次接手一个新项目,第一件事就是跑一遍Battery Historian。看看系统怎么说。
怎么用?
- 手机连电脑,断开充电器
- 重置电池数据:
adb shell dumpsys batterystats --reset - 正常使用App几分钟(或者放后台跑一晚上)
- 导出数据:
adb bugreport bugreport.zip - 上传到
https://bathistorian.appspot.com/或本地Docker部署
你会看到一张时间轴图,上面标着各种系统事件:
- Top App:哪个App在前台
- JobScheduler:后台任务执行时间
- Alarm:闹钟唤醒次数
- Sensor:传感器活跃时段
- Wakelock:持锁时长
关键指标:看「Wakeup Reason」那一栏。如果某个App每30秒就唤醒一次系统,那基本可以断定它在乱用AlarmManager。
我的经验:Battery Historian的图表里,最怕看到密密麻麻的红色竖线。那代表频繁的Alarm唤醒。我曾经优化过一个项目,把Alarm频率从每分钟一次降到每15分钟一次,续航直接提升了30%。
14.2 JobScheduler vs WorkManager:后台任务怎么选?
很多开发者还在用Service做后台任务。说实话,这年头还这么干,基本等于在用户的电池上开了一个洞。
Android从5.0开始推荐用JobScheduler,后来Google又推出了WorkManager。怎么选?我直接给结论:
| 场景 | 推荐方案 | 原因 |
|---|---|---|
| 需要精确时间执行 | AlarmManager(谨慎) | JobScheduler不保证精确时间 |
| 网络变化后同步数据 | WorkManager | 自带约束条件,省电省心 |
| 定期上传日志 | WorkManager + PeriodicWorkRequest | 系统会批量合并任务 |
| 需要兼容低版本 | WorkManager | 底层自动适配JobScheduler或AlarmManager |
WorkManager的正确姿势
// 定义任务
class SyncWorker(context: Context, params: WorkerParameters) : Worker(context, params) {
override fun doWork(): Result {
// 在这里做同步操作
return Result.success()
}
}
// 设置约束条件
val constraints = Constraints.Builder()
.setRequiredNetworkType(NetworkType.CONNECTED) // 只在有网时执行
.setRequiresBatteryNotLow(true) // 电量不低时执行
.build()
val syncRequest = OneTimeWorkRequestBuilder<SyncWorker>()
.setConstraints(constraints)
.build()
WorkManager.getInstance(context).enqueue(syncRequest)
避坑指南:我曾经见过一个团队,用WorkManager每5分钟执行一次任务,结果因为约束条件设置不当,导致手机在飞行模式下也疯狂尝试联网。嗯,那款App的耗电量直接排到了用户手机的第一名。
14.3 AlarmManager优化:别做那个「闹钟狂魔」
AlarmManager是电量杀手排行榜的常客。你想想看,每次Alarm触发,系统都要从深度睡眠中醒来,CPU要启动,屏幕可能亮起——这一套下来,耗电可不是闹着玩的。
优化原则
- 能合并就合并:多个Alarm尽量对齐到同一个时间点触发
- 能用setInexactRepeating就别用setRepeating:系统可以帮你合并唤醒
- 能用setAndAllowWhileIdle就别用setExact:除非你真的需要精确到秒
- 能用JobScheduler就别用AlarmManager:后台同步类任务,WorkManager更合适
// 错误示范:精确重复闹钟,每5分钟唤醒一次
alarmManager.setRepeating(AlarmManager.RTC_WAKEUP, triggerAtMillis, 5 * 60 * 1000, pendingIntent)
// 正确示范:不精确重复,允许系统合并
alarmManager.setInexactRepeating(AlarmManager.RTC_WAKEUP, triggerAtMillis,
AlarmManager.INTERVAL_FIFTEEN_MINUTES, pendingIntent)
核心原则:能用setInexactRepeating就别用setRepeating,能用set就别用setExact。每少一次精确唤醒,用户的电池就能多撑一会儿。
14.4 传感器管理:别让陀螺仪一直转
传感器这块,我见过最离谱的案例:一个天气App,后台一直开着GPS定位,就为了「实时更新当前位置的天气」。结果用户发现手机半天就没电了。
传感器耗电的真相:
- GPS:最耗电,每秒耗电约50mA
- 加速度计:中等,每秒约5mA
- 陀螺仪:中等,每秒约8mA
- 光线传感器:低功耗,约0.5mA
优化策略
- 用完就注销:在onPause或onStop中调用
unregisterListener() - 降低采样频率:不是所有场景都需要SENSOR_DELAY_FASTEST
- 用被动定位代替GPS:能走Network定位就别开GPS
- 批量处理传感器数据:用
SensorManager.registerListener()的第三个参数设置延迟
// 错误示范:最高频率采样,还忘了注销
sensorManager.registerListener(this, accelerometer, SensorManager.SENSOR_DELAY_FASTEST)
// 正确示范:按需设置采样频率,及时注销
sensorManager.registerListener(this, accelerometer, SensorManager.SENSOR_DELAY_UI)
override fun onPause() {
super.onPause()
sensorManager.unregisterListener(this)
}
我的习惯:在开发阶段,我会在Logcat里打一个传感器注册/注销的日志。如果发现某个页面退出后传感器还在跑,那基本就是bug。我曾经用这个方法抓出过三个「幽灵传感器」问题。
14.5 知识体系总览
下面这张图,是我自己总结的电量优化知识框架。你照着这个思路走,基本不会漏掉关键点。
电量优化没有银弹。你得先拿Battery Historian诊断,再针对性地优化后台任务、闹钟和传感器。说白了,就是让App在用户看不见的地方,少干活、干巧活。
最后提醒一句:别为了省电把功能砍没了。用户要的是「省电且好用」,不是「省电但啥也干不了」。平衡,才是优化的艺术。
公众号:蓝海资料掘金营,微信deep3321