19. 系统级功耗策略:Doze模式与相机、App Standby影响、电池优化白名单
说到系统级功耗策略,很多做相机开发的同事容易忽略一个事实——你的相机App并不是在真空中运行的。它活在Android系统的大生态里,而系统为了省电,会时不时地给App“使绊子”。
Doze模式、App Standby、还有那个让人又爱又恨的电池优化白名单,这三样东西,说白了就是系统用来管理App后台行为的三大法宝。但对我们相机App来说,它们有时候是帮手,有时候却是拦路虎。
核心观点:相机App必须主动适配系统级省电策略,否则用户可能遇到“打开相机黑屏3秒”、“扫码一直转圈”、“预览卡顿”等体验问题。这不是Bug,是系统在帮你省电。
19.1 Doze模式:相机App的“休眠杀手”
Doze模式从Android 6.0开始引入,到Android 9之后越来越严格。它的逻辑很简单:手机静止不动、屏幕关闭一段时间后,系统会限制App的网络访问、延迟Job和Alarm。
我遇到过最典型的场景:用户把手机放在桌上充电,屏幕关了,然后通过语音助手说“拍照”。结果相机App被Doze模式按住了,启动花了5秒多。用户骂的是相机卡,其实冤枉的是Doze。
Doze对相机的影响点
- 网络访问受限:云相册上传、实时滤镜下载、地理位置获取都会被延迟
- AlarmManager延迟:定时拍照、延时摄影的触发可能不准
- JobScheduler暂停:后台处理RAW转JPEG的任务会被挂起
- WakeLock失效:系统可能忽略部分WakeLock请求
我的经验:在Doze模式下,Camera2 API的CameraDevice仍然可以打开,但open()的耗时可能从正常的200ms飙升到1-2秒。这是因为系统在进入Doze时回收了部分Camera HAL资源。我个人习惯在onResume()之前先检查PowerManager.isDeviceIdleMode(),如果处于Doze状态,提前做一次“预热”操作。
如何绕过Doze限制(合法方式)
系统提供了两种机制让App在Doze模式下仍能工作:
- 使用高优先级FCM消息:通过Firebase Cloud Messaging的“高优先级”消息可以临时唤醒App,但相机场景下不太实用
- 申请临时白名单:调用
PowerManager.requestPowerSaveMode()相关API,但需要用户授权 - 利用前台服务:相机App如果正在录制视频,系统不会进入深度Doze
// 检查当前是否处于Doze模式
PowerManager pm = (PowerManager) getSystemService(Context.POWER_SERVICE);
if (pm.isDeviceIdleMode()) {
// 提示用户:相机可能启动较慢
// 或者主动请求临时退出Doze
Intent intent = new Intent(Settings.ACTION_REQUEST_IGNORE_BATTERY_OPTIMIZATIONS);
intent.setData(Uri.parse("package:" + getPackageName()));
startActivity(intent);
}
警告:不要滥用REQUEST_IGNORE_BATTERY_OPTIMIZATIONS权限。Google Play审核会严格检查,如果你的App不是核心功能需要(比如相机、地图导航、健康监测),大概率会被拒。我曾经有个项目因为加了这行代码被拒了两次,最后改成了引导用户手动添加白名单。
19.2 App Standby:后台相机的“隐形枷锁”
App Standby是Android 6.0引入的另一套机制。系统会跟踪App的使用频率,把不常用的App标记为“Standby Bucket”中的低优先级。相机App如果被用户冷落几天,就会被扔到“Rare”桶里。
你想想看,一个被标记为“Rare”的相机App,后台能做什么?几乎什么都做不了。网络访问被限制,Job被推迟,连广播接收器都可能被延迟。
Standby Bucket等级
| Bucket等级 | 网络限制 | Job/Alarm延迟 | 典型场景 |
|---|---|---|---|
| ACTIVE | 无限制 | 无延迟 | 用户正在使用相机 |
| WORKING_SET | 10分钟内10次 | 2小时内 | 用户偶尔打开相机 |
| FREQUENT | 1小时内10次 | 8小时内 | 用户每周用几次 |
| RARE | 8小时内10次 | 24小时内 | 用户很久没用相机 |
| NEVER | 完全限制 | 无限延迟 | 用户主动限制后台 |
这里有个坑:如果相机App被标记为RARE,即使你通过CameraManager.openCamera()成功打开了摄像头,后续的CaptureRequest回调也可能因为网络限制而失败——比如你需要在拍照后上传缩略图到云端。
避坑指南:我曾经在开发一款行车记录仪App时,发现用户反馈“录像文件上传失败”。查了半天,原来是App被系统标记为FREQUENT,后台网络请求被限制了。解决方案是:在录像开始时启动前台服务,并绑定通知,这样系统会把App提升到ACTIVE桶。前台服务是相机App对抗App Standby最有效的武器。
19.3 电池优化白名单:相机App的“免死金牌”
电池优化白名单,就是系统设置里那个“忽略电池优化”的开关。用户可以在设置 > 应用 > 特殊访问权限 > 电池优化中找到它。
对于相机App来说,加入白名单意味着:
- Doze模式下网络访问不受限
- App Standby不会降级到RARE
- AlarmManager和JobScheduler正常执行
- WakeLock不会被系统忽略
哪些相机App应该申请白名单?
不是所有相机App都需要。我个人建议以下场景必须引导用户加入:
- 安防/监控类相机:需要后台持续运行,检测运动事件
- 行车记录仪:需要后台录制并上传视频
- 远程相机/婴儿监视器:需要保持网络连接
- 延时摄影App:需要定时触发拍照
普通拍照App其实不需要。你想想看,用户拍完照就退出了,后台没什么事要做,强行申请白名单反而会让用户觉得你的App“流氓”。
如何优雅地引导用户加入白名单?
// 检查是否已加入白名单
PowerManager pm = (PowerManager) getSystemService(Context.POWER_SERVICE);
if (!pm.isIgnoringBatteryOptimizations(getPackageName())) {
// 弹出一个友好的对话框,解释为什么需要
new AlertDialog.Builder(context)
.setTitle("需要关闭电池优化")
.setMessage("为了确保相机在后台能正常录制和上传,建议关闭电池优化。\n\n" +
"我们不会在后台做任何额外耗电操作,请放心。")
.setPositiveButton("去设置", (dialog, which) -> {
Intent intent = new Intent(Settings.ACTION_REQUEST_IGNORE_BATTERY_OPTIMIZATIONS);
intent.setData(Uri.parse("package:" + getPackageName()));
startActivity(intent);
})
.setNegativeButton("不了", null)
.show();
}
重要提醒:从Android 11开始,ACTION_REQUEST_IGNORE_BATTERY_OPTIMIZATIONS的弹窗只能触发一次。如果用户拒绝了,后续再调用不会弹出任何界面。所以第一次弹窗时一定要把话说明白,别浪费机会。我见过有的App连续弹了3次,结果用户直接卸载了。
19.4 系统级策略的联动影响
这三个机制不是独立工作的。它们会互相影响,形成一套组合拳:
- Doze模式 + App Standby = 双重限制,相机App几乎无法后台工作
- 白名单可以同时绕过Doze和Standby的限制
- 前台服务可以临时提升Standby等级,但不能完全绕过Doze
- FCM高优先级消息可以唤醒Doze,但无法改变Standby等级
我画了一张图,帮你理清它们之间的关系:
19.5 实战建议:如何设计相机App的省电策略
说了这么多理论,最后给几条实战建议:
- 区分场景:普通拍照App不需要白名单,监控类App必须引导用户加入
- 使用前台服务:录制视频或后台处理时,启动前台服务并绑定通知,这是最稳妥的方式
- 监听系统状态:注册
ACTION_POWER_SAVE_MODE_CHANGED和ACTION_DEVICE_IDLE_MODE_CHANGED广播,动态调整行为 - 合理使用JobScheduler:把非紧急任务(如缩略图生成、日志上传)放到JobScheduler中,让系统决定何时执行
- 测试要全面:在Doze模式和App Standby的各种桶等级下测试相机功能,别只在ACTIVE状态下测
我的习惯:每次发布新版本前,我会用adb shell dumpsys deviceidle force-idle强制进入Doze,再用adb shell am set-standby-bucket把App设成RARE,然后测试相机启动、拍照、录像、上传全流程。这个测试流程帮我发现过至少5个线上问题。
系统级功耗策略,说白了就是系统在帮你省电,但有时候省过头了。作为相机App开发者,我们要做的不是对抗系统,而是理解它的规则,然后优雅地适应它。嗯,这就是我这些年踩坑踩出来的经验。
公众号:蓝海资料掘金营,微信deep3321