首页 > 代码库 > Android M新特性Doze and App Standby模式详解

Android M新特性Doze and App Standby模式详解

参考:
Optimizing for Doze and App Standby
Android M新特性Doze and App Standby模式详解
深入android6.0 设备 idle状态
Android M 的 Doze 模式下第三方推送服务还能用吗?

一、Optimizing for Doze and App Standby

  1. 从Android6.0开始,Android提供了两种省电延长电池寿命的功能:Doze和App Standby;

  2. 表现形式:当设备没有连接到电源,设备进入Doze模式时,系统将通过延迟最近用户没有使用的应用程序的后台CPU运作及网络活动,让应用程序处于App Standby状态,以此来减少电池消耗。谷歌表示,在Nexus5和Nexus6上测试,当屏幕处于关闭状态,平均续航时间提高30%;

  3. 版本要求:Android6.0(API level 23)及其更高版本;

  4. 开发者影响:为了保证用户的最佳体验,开发者有必要在Doze和App Standby模式下测试应用程序,及其对代码进行相应的调整。

二、Understanding Doze

1.设备进入Doze睡眠模式时机:

  • 用户不操作设备一段时间

  • 屏幕关闭

  • 设备未连接电源充电

2.Doze模式下应用程序有什么变化:

  • 系统试图通过限制应用程序访问网络和CPU密集型服务节省电池;

  • 防止应用程序访问网络,推延应用程序的工作,同步,和标准的警报;

  • 系统定期提供一个短暂的时间让应用程序完成延迟的工作活动,之后再次进入Doze模式。在这个时间片里,系统将提供维持性窗口(maintenance window)应用程序可以在此时执行访问网络,同步,和报警等活动。

Doze模式的五种状态,分别如下:

  1. ACTIVE:手机设备处于激活活动状态

  2. INACTIVE:屏幕关闭进入非活动状态

  3. IDLE_PENDING:每隔30分钟让App进入等待空闲预备状态

  4. IDLE:空闲状态

  5. 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状态:

  1. 用户主动启动该App;

  2. 该App当前有一个前台进程(或包含一个活动的前台服务,或被另一个activity或前台service使用);

  3. 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的通知及后台能按预期的继续工作。

七、客户端使用方法:

  1. App程序可发送action为ACTION_IGNORE_BATTERY_OPTIMIZATION_SETTINGS的intent引导用户进入设置界面将应用程序设置进白名单列表里。

  2. 应用程序还可以使用AREQUEST_IGNORE_BATTERY_OPTIMIZATIONS 权限来触发一个系统对话来让用户添加到白名单里,而无需进入设置界面去设置。

当然,官方也提供用户把你的App移除电池优化白名单的选项。这个白名单也会被Android M的另一个新特性 App Standby使用,所以用户只能简单的进行控制,也就是说设备并不会完全相信这个白名单。

这个白名单只是谷歌的一个建议,就是说在下面这些情况下,你可以使用上面两张方法,来引导用户把你的app设为白名单

官方举了一下白名单例子:

技术分享

总结:

我认为Doze 模式和 APP Standby模式,限制app的权限种类都是一样的。都是进入idle状态。只是各自模式的进入和退出所需要的条件不一样、进入模式后控制APP的周期也不一样。

Doze模式的推出本身是为了减少电池的消耗,且Google希望统一使用GCM来传递消息进行通讯,而对于国内开发来讲,确实带来了很大的麻烦:

  1. 国内开发的一些消息推送机制(PUSH)将受到影响;

  2. 若使用GCM,在国内使用GCM延迟高,对于即时通讯产品来说选择还需勇气啊;

  3. 国内第三方手机厂商如华为、小米、三星,定制的Rom也将使用定制的推送消息机制。这让同一款App如何选择哪种推送机制才能兼容呢?

解决方法:

  1. 用户添加应用程序到电池优化白名单列表;

  2. 开发者使用Google提供的ACTION_IGNORE_BATTERY_OPTIMIZATION_SETTINGS意图和AREQUEST_IGNORE_BATTERY_OPTIMIZATIONS权限设置以此忽略(推荐);

  3. 使用Google提供的GCM;

  4. 通过so绕过Doze模式。


我使用MIUI 7.5(Android 6.0.1)测试过程中发现,自己维持长连接一进入standby模式,网络就断了(手机其它app网络正常),但是运行在后台的代码还是一直在跑,log也能正常输出到手机的文件上,就是连不上网。按照官方的描述进入standby模式,工作应该被挂起,但是为什么后台代码还是正常运行呢??请高人指点一下

  • 微信的主进程可以手动设置为standby 模式,
  • APP的子进程都是无法设置为standby 模式,但是如果主进程是standby 模式,子进程也会是standby 模式。即使使用命令查看子进程的standby 状态也是false。
  • 在正常使用手机的情况下,微信的主进程无法进入standby 模式,感觉是使用so绕过standby模式。具体so文件是怎么实现的,还请大神指点一下。
<script type="text/javascript"> $(function () { $(‘pre.prettyprint code‘).each(function () { var lines = $(this).text().split(‘\n‘).length; var $numbering = $(‘
    ‘).addClass(‘pre-numbering‘).hide(); $(this).addClass(‘has-numbering‘).parent().append($numbering); for (i = 1; i <= lines; i++) { $numbering.append($(‘
  • ‘).text(i)); }; $numbering.fadeIn(1700); }); }); </script>

    Android M新特性Doze and App Standby模式详解