参考:
Optimizing for Doze and App Standby
Android M新特性Doze and App Standby模式详解
Android M 的 Doze 模式下第三方推送服务还能用吗?
一、Optimizing for Doze and App Standby
- 从Android6.0开始,Android提供了两种省电延长电池寿命的功能:Doze和App Standby;
- 表现形式:当设备没有连接到电源,设备进入Doze模式时,系统将通过延迟最近用户没有使用的应用程序的后台CPU运作及网络活动,让应用程序处于App Standby状态,以此来减少电池消耗。谷歌表示,在Nexus5和Nexus6上测试,当屏幕处于关闭状态,平均续航时间提高30%;
- 版本要求:Android6.0(API level 23)及其更高版本;
- 开发者影响:为了保证用户的最佳体验,开发者有必要在Doze和App Standby模式下测试应用程序,及其对代码进行相应的调整。
二、Understanding Doze
1.设备进入Doze睡眠模式时机:
- 用户不操作设备一段时间
- 屏幕关闭
- 设备未连接电源充电
2.Doze模式下应用程序有什么变化:
- 系统试图通过限制应用程序访问网络和CPU密集型服务节省电池;
- 防止应用程序访问网络,推延应用程序的工作,同步,和标准的警报;
- 系统定期提供一个短暂的时间让应用程序完成延迟的工作活动,之后再次进入Doze模式。在这个时间片里,系统将提供维持性窗口(maintenance window)应用程序可以在此时执行访问网络,同步,和报警等活动。
Doze模式的五种状态,分别如下:
- ACTIVE:手机设备处于激活活动状态
- INACTIVE:屏幕关闭进入非活动状态
- IDLE_PENDING:每隔30分钟让App进入等待空闲预备状态
- IDLE:空闲状态
- IDLE_MAINTENANCE:处理挂起任务
如下图所示,Doze期间提供间隔一小段时间(30s)供应用程序使用网络和处理挂起的活动。
从这张图我们可以看到,系统进入Doze模式后,系统会隔一段时间处理正在挂起的任务,随着时间推移,后面间隔的时间会越来越长,以此来减少电量消耗。
3.退出Doze模式:
- 移动设备
- 打开屏幕
- 设备连接电源
以上三种情况会退出Doze模式,之后apps回复正常模式。
4.Doze有哪些限制?
- 网络连接会被禁止
- Wake Lock会被屏蔽
- AlarmManager定时任务延迟到下一个maintenance window进行处理,除非使用AlarmManager提供的方法:setAndAllowWhileIdle() 或者setExactAndAllowWhileIdle()
- 系统将不扫描热点WIFI
- 同步工作将被禁止
- 不允许JobScheduler进行任务调度
5.适配Doze模式有什么方法?
- Doze影响到AlarmManager闹钟和定时器管理活动,在Android6.0引入了两个新方法:setAndAllowWhileIdle() 和setExactAndAllowWhileIdle(),调用两个方法可以在Doze模式下让系统响应定时任务。
- Doze模式下限制了网络的连接,如果应用程序依赖于实时信息,那么这个将影响App的体验。那么你需要使用Google Cloud Messaging (GCM)谷歌云消息(后面详细讲解)
三、Understanding App Standby
当用户不触摸使用应用程序一段时间时,该应用程序处于App Standby状态,系统将把该App标志为空闲状态(idle)。除非触发以下任意条件,应用程序将退出App Standby状态:
- 用户主动启动该App;
- 该App当前有一个前台进程(或包含一个活动的前台服务,或被另一个activity或前台service使用);
- App生成一个用户所能在锁屏或通知托盘看到的Notification, 而当用户设备插入电源时,系统将会释放App的待机状态,允许他们自由的连接网络及其执行未完成的工作和同步。如果设备空闲很长一段时间,系统将允许空闲App一天一次访问网络。
四、Doze和App Standby的区别:
Doze模式需要屏幕关闭(通常晚上睡觉或长时间屏幕关闭才会进入),而App Standby不需要屏幕关闭,App进入后台一段时间也会受到连接网络等限制。
五、DeviceIdleController
DeviceIdleController是Doze模式的主要驱动。接下来,我将使用device idle mode而不是doze mode来描述“Doze”,因为它更符合代码的实际情况。
5.1、deviceidle——新的系统服务
如果你已经阅读了官方文档,你可能已经注意到下面的命令,开发者可以通过这些命令得知当下设备的应用行为:
adb shell dumpsys battery unplug
adb shell dumpsys deviceidle step
对于上面的命令你可能并不熟悉,dumpsys是用来与系统服务交互的(查看它们的状态)。deviceidle是我们之前没有看到过的,它是一个新的系统服务。用来检测是否进入idle mode(Doze模式)
$ adb shell service list | grep deviceidle
59 deviceidle: [android.os.IDeviceIdleController]
我们可以使用‘-h’看到所有的deviceidle的所有选项:
$ adb shell dumpsys deviceidle -h
Device idle controller (deviceidle) dump options:
[-h] [CMD]
-h: print this help text.
Commands:
step
Immediately step to next state, without waiting for alarm.
disable
Completely disable device idle mode.
enable
Re-enable device idle mode after it had previously been disabled.
whitelist
Add (prefix with +) or remove (prefix with -) packages.
5.2、DeviceIdleController的五种状态
DeviceIdleController维持着设备包含的五种状态,和上面介绍的Doze的五种状态是一样的:
- ACTIVE – 设备在使用中,或者连接着电源。
- INACTIVE – 设备已经从ACTIVE状态中出来一段时间了(使用者关闭了屏幕或者拔掉了电源)
- IDLE_PENDING – 请留意,我们将进入idle mode.
- IDLE – 设备进入idle mode.
- IDLE_MAINTENANCE – 应用窗口已经打开去做处理.
1、当设备被唤醒和正在使用中,控制器就处于ACTIVE状态,
2、不活跃时间超时,用户关闭屏幕等等,将会使设备状态进入到INACTIVE.
3、INACTIVE状态下,DeviceIdleController将会通过AlarmManager来设置他自己的alarm来驱动进程:
一个alarm会被设置在一个预设的时刻(这个时间在M的预览中是30分钟)。
当这个alarm生效后,DeviceIdleController 会进入到IDLE_PENDING然后再次设置同样的alarm。
4、当触发下一个alarm后,控制器会进入到IDLE 状态,进入到这个状态后,应用特性会被完全限制。
5、IDLE 状态后,会在IDLE 和IDLE_MAINTENANCE两个状态之间周期性的跳转。IDLE_MAINTENANCE也就是Doze中提到的maintenance window,在这个状态下,应用程序可以在此时执行访问网络,同步,和报警等活动。
这些服务的公共API(由IDeviceIdleController 接口展现)持有全部方法访问白名单。应用(系统应用或其它第三方应用)任何情况下都不能驱动控制器状态。
5.3、DeviceIdleController 维护着一个应用白名单
正如你在上面的帮助菜单中看到的一样,DeviceIdleController 维护着一个应用白名单,不需要额外的参数,通过dump服务的状态,我们能够看到现在的这个列表:
$ adb shell dumpsys deviceidle
Whitelist system apps:
com.android.providers.downloads
com.android.vending
com.google.android.gms
Whitelist app uids:
UID=10012: true
UID=10016: true
UID=10026: true
…
这个名单分为两个部分:系统应用和第三方应用。
系统应用
系统应用会被平台制作者通过配置定义列在白名单中。下面这个是从Nexus 6中得到的一个配置定义例子,它将GMS核心(在GCM中使用),应用商店,以及一个任意的用于电源监控的app白名单化:
<?xml version="1.0" encoding="utf-8"?>
<!-- These are configurations that must exist on all GMS devices. -->
<config>
<allow-in-power-save package="com.google.android.gms" />
<allow-in-power-save package="com.android.vending" />
<allow-in-power-save package="com.google.android.volta" />
</config>
其它的系统服务可以通过SystemConfig的实例访问到这些值。DeviceIdleController使用SystemConfig.getAllowInPowerSave()将这些系统定义的元素放到白名单中。
注意:当设备处于“省电模式”时,同样也是这个配置文件决定哪个系统应用可以在后台开启服务。
第三方应用
白名单中剩下的部分是用户定义的,这些项可以通过两种方式被增加和删除。
第一种:开发者可以通过dumpsys接口使用白名单命令:
$ adb shell dumpsys deviceidle whitelist +com.example.myapplication
$ adb shell dumpsys deviceidle
Whitelist system apps:
com.android.providers.downloads
com.android.vending
com.google.android.gms
Whitelist user apps:
com.example.myapplication
Whitelist app uids:
UID=10012: true
第二种:用户可以通过设置(Settings -> Battery -> Ignore optimizations)来修改白名单。
另外:在小米手机中,神隐模式中把应用设置为无限制或者在近期任务中下拉锁定,就会出现在上述的白名单中。
六、测试Doze和App Standby模式的方法(Adb命令)
测试Doze模式
1、 首先确保你的硬件或虚拟设备是Android6.0或更高版本系统;
2、 连接设备到开发机上并安装你的app;
3、 运行app并让其运行活动;
4、 关闭设备的屏幕;
5、运行以下adb命令使系统进入Doze模式:
$ adb shell dumpsys battery unplug
$ adb shell dumpsys deviceidle step
你可能需要多次执行第二条命令,直到设备处于idle 状态。注意,第一条命令的意思是,拔去电源,即使现在正在插着usb调试,也不会充电。建议运行$ adb shell dumpsys battery reset
,否则会出现手机充不上电的情况。
6、 观察你的app表现行为是否有需优化改进的地方。
测试App Standby模式
步骤1-3同测试Doze模式
4、 运行以下adb命令迫使系统进入App Standby模式:
$ adb shell dumpsys battery unplug
$ adb shell am set-inactive <packageName> true
5、 模拟唤醒你的应用程序使用以下命令:
$ adb shell am set-inactive <packageName> false
$ adb shell am get-inactive <packageName>
6、 观察你的App,确保应用程序可以从standby mode优雅得恢复,应该检查App的通知及后台能按预期的继续工作。
七、客户端使用方法:
- App程序可发送action为ACTION_IGNORE_BATTERY_OPTIMIZATION_SETTINGS的intent引导用户进入设置界面将应用程序设置进白名单列表里。
- 应用程序还可以使用AREQUEST_IGNORE_BATTERY_OPTIMIZATIONS 权限来触发一个系统对话来让用户添加到白名单里,而无需进入设置界面去设置。
当然,官方也提供用户把你的App移除电池优化白名单的选项。这个白名单也会被Android M的另一个新特性 App Standby使用,所以用户只能简单的进行控制,也就是说设备并不会完全相信这个白名单。
这个白名单只是谷歌的一个建议,就是说在下面这些情况下,你可以使用上面两张方法,来引导用户把你的app设为白名单
官方举了一下白名单例子:
总结:
我认为Doze 模式和 APP Standby模式,限制app的权限种类都是一样的。都是进入idle状态。只是各自模式的进入和退出所需要的条件不一样、进入模式后控制APP的周期也不一样。
Doze模式的推出本身是为了减少电池的消耗,且Google希望统一使用GCM来传递消息进行通讯,而对于国内开发来讲,确实带来了很大的麻烦:
- 国内开发的一些消息推送机制(PUSH)将受到影响;
- 若使用GCM,在国内使用GCM延迟高,对于即时通讯产品来说选择还需勇气啊;
- 国内第三方手机厂商如华为、小米、三星,定制的Rom也将使用定制的推送消息机制。这让同一款App如何选择哪种推送机制才能兼容呢?
解决方法:
- 用户添加应用程序到电池优化白名单列表;
- 开发者使用Google提供的ACTION_IGNORE_BATTERY_OPTIMIZATION_SETTINGS意图和AREQUEST_IGNORE_BATTERY_OPTIMIZATIONS权限设置以此忽略(推荐);
- 使用Google提供的GCM;
- 通过so绕过Doze模式。
我使用MIUI 7.5(Android 6.0.1)测试过程中发现,自己维持长连接一进入standby模式,网络就断了(手机其它app网络正常),但是运行在后台的代码还是一直在跑,log也能正常输出到手机的文件上,就是连不上网。按照官方的描述进入standby模式,工作应该被挂起,但是为什么后台代码还是正常运行呢??请高人指点一下
- 微信的主进程可以手动设置为standby 模式,
- APP的子进程都是无法设置为standby 模式,但是如果主进程是standby 模式,子进程也会是standby 模式。即使使用命令查看子进程的standby 状态也是false。
- 在正常使用手机的情况下,微信的主进程无法进入standby 模式,感觉是使用so绕过standby模式。具体so文件是怎么实现的,还请大神指点一下。