6. 白名单机制(Power Whitelist):如何添加、系统预置白名单、用户可配置白名单
Doze 模式一开启,系统就像个严格的管家——大部分应用都会被强制休眠。但总有那么几个应用,需要保持后台运行,比如即时通讯、音乐播放器、导航软件。这时候,白名单机制就派上用场了。
说白了,白名单就是一张「免死金牌」。应用一旦加入白名单,Doze 模式就不会对它施加限制。它依然可以保持网络连接、使用 AlarmManager 唤醒设备、执行后台任务。
我刚开始接触 Doze 时,以为白名单只是给用户手动配置的。后来才发现,系统内部有一套完整的预置机制,而且不同厂商的定制策略差异很大。嗯,这里面的门道不少。
6.1 白名单的本质:一个系统级的权限列表
白名单在 Android 系统中,本质上是一个由 DeviceIdleController 维护的字符串列表。每个字符串对应一个应用的包名(package name)。
系统会定期检查这个列表。如果某个应用在列表中,Doze 模式的所有限制——包括网络访问、Alarm 延迟、JobScheduler 推迟——都不会生效。
你想想看,如果微信不在白名单里,别人给你发消息,你手机可能延迟半小时才收到。这显然不行。所以,即时通讯类应用几乎都在白名单里。
6.2 系统预置白名单:谁在「出生」时就拿到了免死金牌?
Android 系统在出厂时,就已经预置了一批白名单应用。这些应用通常包括:
- 核心系统服务:如电话、短信、蓝牙、Wi-Fi 等系统级应用
- Google 服务:如 Google Play 服务、Google 联系人同步
- 厂商预装应用:如小米的 MIUI 核心服务、华为的 HMS Core
- 即时通讯类:部分厂商会预置微信、QQ 等
这些预置白名单定义在系统源码的 frameworks/base/services/core/java/com/android/server/deviceidle/DeviceIdleController.java 中。具体来说,是通过 readConfigFileLocked() 方法从 XML 配置文件中读取的。
我曾经在分析某厂商的 ROM 时,发现他们把自家的推送服务也加进了白名单。这其实是个双刃剑——虽然保证了推送及时性,但也增加了功耗。说白了,厂商在功耗和体验之间做了取舍。
预置白名单的配置文件位置
// 系统默认白名单配置文件
// 路径:/system/etc/deviceidle.xml
// 或者:/data/system/deviceidle.xml(用户修改后)
<?xml version="1.0" encoding="utf-8"?>
<config>
<whitelist>
<package name="com.android.phone" />
<package name="com.android.mms" />
<package name="com.google.android.gms" />
<package name="com.android.bluetooth" />
</whitelist>
</config>
dumpsys deviceidle whitelist。这个命令会列出所有在白名单中的应用包名。
6.3 用户可配置白名单:让用户自己决定
除了系统预置的,用户也可以手动添加应用进白名单。Android 提供了两种方式:
方式一:通过系统设置界面
在 Android 6.0 及以上版本中,用户可以在「设置 → 电池 → 电池优化」中找到白名单配置界面。这个界面实际上是一个 Settings.GridActivity,它调用的是 PowerManager.isIgnoringBatteryOptimizations() 方法。
用户可以选择「所有应用」或「未优化」来查看和修改白名单状态。我建议普通用户只把真正需要的应用加入白名单,比如微信、支付宝、导航软件。加太多反而会抵消 Doze 的省电效果。
方式二:通过 adb 命令
对于开发者来说,adb 命令更高效。你可以用以下命令将应用加入白名单:
# 将应用加入白名单
adb shell dumpsys deviceidle whitelist +com.example.app
# 将应用移出白名单
adb shell dumpsys deviceidle whitelist -com.example.app
# 查看当前白名单
adb shell dumpsys deviceidle whitelist
6.4 白名单的层级结构:系统级 vs 用户级
Android 的白名单其实分为两个层级:
| 层级 | 存储位置 | 修改方式 | 持久性 |
|---|---|---|---|
| 系统级白名单 | /system/etc/deviceidle.xml |
需要 root 权限或系统签名 | 永久(除非刷机) |
| 用户级白名单 | /data/system/deviceidle.xml |
系统设置界面或 adb | 持久化(重启不丢失) |
系统级白名单在设备启动时加载,用户无法修改。用户级白名单则是在系统运行过程中动态维护的。两者合并后,才是最终生效的白名单列表。
我记得有一次,客户反馈某个系统应用在 Doze 模式下无法接收推送。排查后发现,这个应用虽然声明了 RECEIVE_BOOT_COMPLETED 权限,但并没有被加入系统级白名单。最后我们通过修改 deviceidle.xml 解决了问题。
6.5 如何判断一个应用是否在白名单中?
在应用开发中,你可以通过以下代码判断当前应用是否在白名单中:
// 检查应用是否在白名单中
String packageName = getPackageName();
PowerManager pm = (PowerManager) getSystemService(Context.POWER_SERVICE);
boolean isWhitelisted = pm.isIgnoringBatteryOptimizations(packageName);
if (isWhitelisted) {
// 应用在白名单中,可以正常使用后台服务
} else {
// 应用不在白名单中,需要引导用户手动添加
}
如果应用不在白名单中,你可以引导用户去设置界面手动添加。但要注意,Android 不允许应用直接请求加入白名单——必须由用户主动操作。这是为了保护用户的电池使用体验。
6.6 白名单机制的核心逻辑流程图
下面这张图展示了白名单机制在 Doze 模式中的完整工作流程:
从这张图可以清晰地看到:应用发起请求后,系统先判断设备是否处于 Doze 模式。如果不是,一切照常。如果是,则检查白名单——在白名单中的应用不受限制,不在的则被延迟或挂起。
用户可以通过设置界面或 adb 命令动态修改白名单,而系统预置的白名单则是固定的,用户无法修改。这两者共同构成了完整的白名单机制。
6.7 总结与建议
白名单机制是 Doze 模式中最重要的「例外处理」手段。它让系统在省电和用户体验之间找到了平衡点。
我个人建议,在开发应用时:
- 不要滥用白名单:只有真正需要后台运行的应用才应该加入白名单。比如音乐播放器、导航软件、即时通讯工具。
- 引导用户操作:如果应用确实需要白名单权限,可以在首次启动时弹出一个友好的引导对话框,而不是直接请求系统权限。
- 测试白名单行为:在开发过程中,使用
adb shell dumpsys deviceidle命令模拟 Doze 模式,测试应用在不同白名单状态下的行为。
我曾经见过一个天气应用,它每隔15分钟就要刷新一次数据。开发者把它加入了白名单,结果导致设备在 Doze 模式下每小时被唤醒4次,电池续航直接缩水了20%。这其实完全没必要——天气数据延迟半小时更新,用户根本感知不到。
嗯,白名单机制就讲到这里。记住一句话:白名单是给「必须活着」的应用准备的,不是给「想要活着」的应用准备的。