2015 年 9 月 Android 6.0的时候doze机制诞生初期,带来一阵子社交应用消息延迟、灭屏无通知消息的问题。不过现在doze带来的消息收不到的解决方案很统一了加名单解决。
一、Doze和standby简介:
1.doze:非充电,熄屏、手机静止一段时间后,系统会进入doze模式后限制应用使用网络; 充电,亮屏解锁,手机晃动时会退出doze

2.standby:应用冻结后,如果是用户不可感知的后台应用,限制应用使用网络; 原生48小时standby机制:原生机制对于超过48小时未使用过的应用也会限制。其中standby的白名单会使用doze的白名单。但是一般大厂还是在这一基础上自建一个standby自己的白名单便于精细化管控
闲置时间 | 所属存储桶 (Bucket) | 限制程度 |
0 - 12 小时 | ACTIVE / WORKING_SET | 无限制 |
12 - 24 小时 | FREQUENT | 作业/闹钟延迟 |
24 - 48 小时 | RARE | 网络受限 |
> 48 小时 | RESTRICTED | 严格限制,可能无法收通知 |
二、解决方案
1.Settings的Doze白名单手动设置入口:
不过这个原生的入口很多手机都找不到了,部分手机还是可以看到桌面长按App->应用信息->电池

如果不配置白名单,新安装的应用默认是归为doze的自动管控规则,即优化选项
2.Doze机制新增白名单
添加Doze白名单的命令:adb shell dumpsys deviceidle whitelist +com.test
机制 | 配置路径 | 配置案例 | 说明 | 影响机制 | 调试命令 |
Doze | /platform/frameworks/base/data/etc/platform.xml | 【系统应用Doze白名单配置】例如App安装路径:/system/app 或 /system/priv-app 目录下 | Doze\Job\AppStandby\Alarm\WakeLock\Sync | 查看Doze白名单:adb shell dumpsys deviceidle添加Doze白名单:adb shell dumpsys deviceidle whitelist +com.test删除Doze白名单:adb shell dumpsys deviceidle whitelist -com.test进入Doze模式:灭屏adb shell dumpsys battery unplugadb shell dumpsys deviceidle stepStepped to deep: IDLE_PENDINGadb shell dumpsys deviceidle stepStepped to deep: SENSINGadb shell dumpsys deviceidle stepStepped to deep: LOCATINGadb shell dumpsys deviceidle stepStepped to deep: IDLE【设备处于Doze】 | |
系统应用Doze白名单省电模式下也能豁免,一般少配置 | Doze\Job\AppStandby\Alarm\WakeLock\Sync\省电机制 | ||||
自定义三方应用doze名单.xml | doze_pkg_white_list | 【第三方应用Doze白名单】可云配推送例如App安装路径:/data/app/实现原理:DeviceIdleController.addPowerSaveWhitelistApp | Doze\Job\AppStandby\Alarm\WakeLock\Sync | ||
temp_pkg_white_list | Doze tempWhiteList白名单 |

3.普通应用不是Doze白名单怎么办?
普通应用能做到就是提高本身进程重要度
目标场景 | 系统判定允许的进程状态 | 第三方应用可做的“提权”行为(接口 / 方式) | 对应 capability / 效果 |
Idle / Doze / Power Save Mode 下仍保活 & 联网 | procState ≤ FOREGROUND_THRESHOLD_STATE | 启动前台 Service(Foreground Service) startForegroundService()+ 显示通知 | 提升到 FGS,不被 idle 限制 |
(capability & POWER_RESTRICTED_NETWORK) != 0 | 申请 REQUEST_IGNORE_BATTERY_OPTIMIZATIONS PowerManager.isIgnoringBatteryOptimizations() | 获得 POWER_RESTRICTED_NETWORK 能力 | |
Low Power Standby(LPS)下仍运行 | procState ≤ TOP_THRESHOLD_STATE | 成为 TOP 应用(前台 Activity) 用户正在操作的界面 | 唯一合法方式 |
第三方无法绕过不能在 LPS 下后台运行系统强制限制 | |||
后台仍允许网络访问 | procState < BACKGROUND_THRESHOLD_STATE | 短时任务 / 前台 Service | 避免落入 BACKGROUND |
(capability & POWER_RESTRICTED_NETWORK) != 0 | 加入电池白名单 / 关闭应用待机 | 获得后台联网能力 | |
Restrict Background(数据节省)下仍联网 | procState ≤ IMPORTANT_FOREGROUND | 播放音频 / 导航 / 下载 (系统认定的 IMPORTANT_FOREGROUND) | 用户感知型任务 |
(capability & USER_RESTRICTED_NETWORK) != 0 | 用户手动授予“不受限制的后台数据” | 用户侧提权 |
以下是源码根据不同的进程重要度,限制后台应用的联网行为,后台应用联网行为会影响对应消息通知的接收
/** @hide */public static boolean isProcStateAllowedWhileIdleOrPowerSaveMode(int procState, @ProcessCapability int capability) {if (procState == PROCESS_STATE_UNKNOWN) {return false;}if (NetPolicyStub.getInstance().isProcStateRestrictWhileIdleOrPowerSaveMode(uid,uidState.procState)) {return false;}return procState <= FOREGROUND_THRESHOLD_STATE|| (capability & ActivityManager.PROCESS_CAPABILITY_POWER_RESTRICTED_NETWORK) != 0;}/** @hide */public static boolean isProcStateAllowedWhileInLowPowerStandby(@Nullable UidState uidState) {if (uidState == null) {return false;}if (NetPolicyStub.getInstance().isProcStateRestrictWhileInLowPowerStandby(uid, uidState.procState)) {return false;}return uidState.procState <= TOP_THRESHOLD_STATE;}/*** This is currently only used as an implementation detail for* {@link com.android.server.net.NetworkPolicyManagerService}.* Only put here to be together with other isProcStateAllowed* methods.** @hide*/public static boolean isProcStateAllowedNetworkWhileBackground(@Nullable UidState uidState) {if (uidState == null) {return false;}if (NetPolicyStub.getInstance().isProcStateRestrictNetworkWhileBackground(uid,uidState.procState)) {return false;}return uidState.procState < BACKGROUND_THRESHOLD_STATE|| (uidState.capability & PROCESS_CAPABILITY_POWER_RESTRICTED_NETWORK) != 0;}/** @hide */public static boolean isProcStateAllowedWhileOnRestrictBackground(int procState,@ProcessCapability int capabilities) {if (procState == PROCESS_STATE_UNKNOWN) {return false;}if (NetPolicyStub.getInstance().isProcStateRestrictWhileOnRestrictBackground(uid,uidState.procState)) {return false;}return procState <= FOREGROUND_THRESHOLD_STATE// This is meant to be a user-initiated job, and therefore gets similar network// access to FGS.|| (procState <= ActivityManager.PROCESS_STATE_IMPORTANT_FOREGROUND&& (capabilities& ActivityManager.PROCESS_CAPABILITY_USER_RESTRICTED_NETWORK) != 0);}

夜雨聆风