6. 白名单机制(Power Whitelist):如何添加、系统预置白名单、用户可配置白名单

Doze 模式一开启,系统就像个严格的管家——大部分应用都会被强制休眠。但总有那么几个应用,需要保持后台运行,比如即时通讯、音乐播放器、导航软件。这时候,白名单机制就派上用场了。

说白了,白名单就是一张「免死金牌」。应用一旦加入白名单,Doze 模式就不会对它施加限制。它依然可以保持网络连接、使用 AlarmManager 唤醒设备、执行后台任务。

我刚开始接触 Doze 时,以为白名单只是给用户手动配置的。后来才发现,系统内部有一套完整的预置机制,而且不同厂商的定制策略差异很大。嗯,这里面的门道不少。

6.1 白名单的本质:一个系统级的权限列表

白名单在 Android 系统中,本质上是一个由 DeviceIdleController 维护的字符串列表。每个字符串对应一个应用的包名(package name)。

系统会定期检查这个列表。如果某个应用在列表中,Doze 模式的所有限制——包括网络访问、Alarm 延迟、JobScheduler 推迟——都不会生效。

你想想看,如果微信不在白名单里,别人给你发消息,你手机可能延迟半小时才收到。这显然不行。所以,即时通讯类应用几乎都在白名单里。

核心要点: 白名单只影响 Doze 模式的限制,不影响 Standby 模式(App Standby)和后台限制(Background Restrictions)。这是很多人容易混淆的地方。

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>
小技巧: 如果你想查看当前设备上的白名单列表,可以在 adb shell 中执行: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
注意: 通过 adb 添加的白名单是临时的,设备重启后会丢失。如果需要持久化,需要修改系统配置文件或通过系统应用接口添加。

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 不允许应用直接请求加入白名单——必须由用户主动操作。这是为了保护用户的电池使用体验。

避坑指南: 我曾经开发过一个音乐播放器,用户反馈在 Doze 模式下播放会卡顿。排查后发现,我们的应用没有加入白名单,导致网络请求被延迟。解决方案是:在应用首次启动时,弹出一个引导对话框,提示用户将应用加入电池优化白名单。这样既尊重了用户选择,又解决了功能问题。

6.6 白名单机制的核心逻辑流程图

下面这张图展示了白名单机制在 Doze 模式中的完整工作流程:

白名单机制核心流程 应用发起后台请求 设备是否处于Doze模式? 正常处理请求 检查白名单 在白名单中 放行,不限制 不在白名单中 应用被限制(延迟/挂起) 用户手动配置白名单 系统预置白名单(不可修改)

从这张图可以清晰地看到:应用发起请求后,系统先判断设备是否处于 Doze 模式。如果不是,一切照常。如果是,则检查白名单——在白名单中的应用不受限制,不在的则被延迟或挂起。

用户可以通过设置界面或 adb 命令动态修改白名单,而系统预置的白名单则是固定的,用户无法修改。这两者共同构成了完整的白名单机制。

6.7 总结与建议

白名单机制是 Doze 模式中最重要的「例外处理」手段。它让系统在省电和用户体验之间找到了平衡点。

我个人建议,在开发应用时:

  • 不要滥用白名单:只有真正需要后台运行的应用才应该加入白名单。比如音乐播放器、导航软件、即时通讯工具。
  • 引导用户操作:如果应用确实需要白名单权限,可以在首次启动时弹出一个友好的引导对话框,而不是直接请求系统权限。
  • 测试白名单行为:在开发过程中,使用 adb shell dumpsys deviceidle 命令模拟 Doze 模式,测试应用在不同白名单状态下的行为。

我曾经见过一个天气应用,它每隔15分钟就要刷新一次数据。开发者把它加入了白名单,结果导致设备在 Doze 模式下每小时被唤醒4次,电池续航直接缩水了20%。这其实完全没必要——天气数据延迟半小时更新,用户根本感知不到。

嗯,白名单机制就讲到这里。记住一句话:白名单是给「必须活着」的应用准备的,不是给「想要活着」的应用准备的。

公众号:蓝海资料掘金营,微信deep3321