移动设备的续航时间无疑是所有用户都非常在意的。我们都希望自己的手机一次充电可以使用更长的时间。但遗憾的是,近几年移动设备的电池元件一直没有重大的技术突破。并且,随着硬件性能的提升却带来了更多的电量消耗。
如果对比近几年的Android和ios手机,我们会发现:通常情况下,Android手机的电池要比同时期的ios电池容量大很多,但是待机方面却没有太大的优势。ios之所以续航优秀,其很大的原因在于对于后台进程的限制。在ios上,后台进程是无法长时间处于活跃状态的。而Android系统正好相反,通过监听广播、添加后台服务等方式,应用程序可以一直在后台保持活跃。太多进程的长时间活跃,显然会导致电量的快速耗尽。而反过来,想要延长电池寿命的重要措施就是尽可能减少后台应用的活跃性。因此,从Android5.0到8.0的功耗改进,一直都是围绕着“后台进程的活跃性”来展开的。
Project Volta
project volta是在Android5上引入的。要延长电池的寿命,首先就得要明确消耗电量的主要因素是什么。在移动设备上,对于电量的消耗最大的是下面三个模块:
- 应用处理器(CPU、GPU)
- 电话信号
- 屏幕
除此之外,设备的频繁唤醒也会导致电量消耗过快。系统唤醒1s所消耗的电量约等于两分钟系统待机所消耗的电量。如果系统中安装了大量的应用,每个应用都在不同的时间点将系统唤醒(例如,通过广播或者服务),那么无疑会导致电量很快耗尽。
反过来,假设系统能将应用唤醒系统的频率减低,尽可能将不同应用唤醒系统的步调合并和集中,便能减少电量的消耗。
为了改善电池使用寿命,project volta提供的机制包含以下几个方面:
-
提供了JobScheduler API
Android5新增了该api,这个api允许开发者定义一些系统在稍后或指定条件下(如设备充电时)以异步方式运行的作业,从而优化电池寿命。下列情形下,这个功能很有用:- 应用具有不面向用户并且可以推迟的作业
- 应用具有在设备插入电源时再进行的作业
- 应用具有一项需要接入网络或连接wlan的任务
- 应用具有多项希望定期以批处理方式运行的任务
参考资料: https://blog.csdn.net/FightFightFight/article/details/86240553
-
提供工具帮助开发者发现问题
为了帮助开发者分析系统的电池消耗,Android系统内置了Batterystats工具。可以通过下面的命令来使用这个工具:adb shell dumpsys batterystats
这个命令的输出内容非常长,人工阅读比较困难。所以Google提供了另一款工具,可以将上一步的输出装换成图形的方式方便解读。https://github.com/google/battery-historian
通过这两个工具的组合,可以得到一份图形化的电量信息的报表。
整个过程操作步骤如下:
- 从https://github.com/google/battery-historian下载工具。
- 解压缩刚刚下载的压缩包,并找到historian.py脚本。
- 将设备连接到pc上。
- 打开一个终端。
- 通过cd命令切换到historian.py脚本所在路径。
- 停止adb server:adb kill-server。
- 重启adb服务并通过adb devices确认设备已经连接上。
- 重置电磁使用历史数据:adb shell dumpsys batterystats–reset。
- 将设备与pc断开连接。
- 正常使用待测试的应用程序。
- 重新将设备与pc连接。
- 通过adb devices确认设备已经连接成功。
- 通过adb shell dumpsys batterystats>batterystats.txt将电池统计结果导出到文本文件中。
- 通过python historian.py batterystats.txt>batterystats.html获取图形化结果。
- 通过浏览器打开batterystats.html。
- 对结果进行分析。
- 在虚拟机层面减少电池消耗
Android5之前的版本使用的是Dalvik虚拟机,Android5及其之后的版本正式使用了新的虚拟机:ART。Dalvik虚拟机上解释执行和JIT,是在应用程序每次运行过程中将java字节码翻译为机器码的,这个翻译过程可能是反复的、多次的。而art上的aot是在应用安装的时候,一次性直接将字节码编译成了机器码(虽然说art后来的版本改进,没有一次性将所有代码编译为机器码,但总的来说,无论是安装时,还是后期运行时,只要有过一次编译成机器码,之后就不再重复编译了)。从字节码到机器码这个过程本身就非常消耗cpu,因此也是非常耗电的。而art虚拟机的引入和改进,由运行多次翻译改成一次编译。节省了cpu的执行,也节省了电量的消耗。 - 提供省电模式给用户
Android5上添加了一个新的省电模式给用户,用户可以通过系统设置主动打开省电模式,也可以设置电量过低时自动打开。
Doze模式与App StandBy
Project Volta主要是提供了一些api和工具给开发者,让开发者配合来改善电池寿命,所以这个机制的效果很难得到保证。从Android6开始,系统包含了一些自动的省电行为,这些行为对系统上的所有应用都会产生影响,不需要开发者做特殊适配。
从Android6开始,Android引入了两个新的省电功能为用户延长电池寿命。
- Doze:该模式的运行机制是系统会监测设备的活跃状态,如果设备长时间处于闲置状态且没有接入电源,那么便推迟应用的后台cpu和网络活动来减少电池消耗。
- App StandBy:该模式可推迟用户近期未与之交互的应用的后台网络活动。
Doze模式和AppStandBy会影响Android6或更高版本上运行的所有应用,无论它们是否特别设置过api level。
了解Doze模式
如果用户设备未接入电源、处于静止状态一段时间且屏幕关闭,设备便会进入Doze模式。在Doze模式下,系统会尝试通过限制应用对网络和cpu密集型服务的访问来节省电量。
系统会定期退出Doze模式一会,好让应用完成其已推迟的活动。在此维护时段内,系统会运行所有待定同步、作业和闹钟并允许应用访问网络。在每个维护时段结束后,系统会再次进入Doze模式,暂停网络访问并推迟作业、同步和闹铃。随着时间的推移,系统安排维护时段的次数越来越少,这有助于在设备未连接至充电器的情况下长期处于不活跃状态时降低电池消耗。
一旦用户通过移动设备、打开屏幕或连接到充电器唤醒设备,系统就会立即退出Doze模式,并且所有应用都将返回到正常活动状态。
Android6.0引入了Doze模式,当用户设备未插入电源、处于静止状态且屏幕关闭时,该模式会推迟cpu和网络活动,从而延长电池寿命。而Android7.0则通过在设备未插接电源且屏幕关闭状态下,但不一定要处于静止状态(例如,用户外出时把手持式设备装在口袋里)时应用部分cpu和网络限制,进一步增强了Doze模式。
当设备处于充电状态且屏幕已关闭一定时间后,设备会进入Doze模式并应用第一部分限制:关闭应用网络访问,推迟作业和同步。如果进入Doze模式后设备处于静止状态且达到一定时间,系统则会对PowerManager.WakeLock、AlarmManager闹铃、GPS和WLAN扫描应用余下的Doze模式限制。无论是应用部分还是全部Doze模式限制,系统都会唤醒设备以提供简短的维护时间窗口,在此窗口期间,应用程序可以访问网络并执行任何被推迟的作业/同步。同样,一旦激活屏幕或插接设备电源,系统将退出Doze模式并移除这些处理限制。
了解App StandBy
App StandBy允许系统判定应用在用户未主动使用它时使其处于空闲状态。当用户有一段时间未触摸应用时,系统便会做出此判定。但是对于以下情况,系统将判定应用退出App StandBy状态,包括:
- 用户显示启动应用
- 应用有一个前台进程(例如activity或前台服务,或被另一个activity或前台服务使用)
- 应用生成用户可在锁屏或通知栏中看到的通知
当用户将设备插入电源时,系统将从App StandBy状态释放应用,从而让它们可以自由访问网络并执行任何特定作业和同步。如果设备长时间处于空闲状态,系统将按每天大约一次的频率允许该应用访问网络。
通过妥善管理网络连接、闹钟、作业和同步并使用FCM高优先级消息,几乎所有应用都应该能够支持Doze模式。对于一小部分用例,这可能还不够。对于此类用例,系统为部分免除Doze模式和App StandBy优化的应用提供了一份可配置的白名单。
在Doze模式和App StandBy期间,加入白名单的应用可以使用网络并保留部分wake locks。不过,正如其他应用一样,其他限制仍然适用于加入白名单的应用。例如,加入白名单的原因的作业和同步将推迟,并且其常规alarmmanager闹铃不会触发。通过调用isIgnoringBatteryOptimizations(),应用可以检查自身当前是否位于白名单中。
用户可以在Settings->Battery->Battery optimization中手动配置该白名单。
另外,系统也为应用提供了编程接口来请求让用户将其加入白名单。
- 应用可以触发Settings.ACTION_IGNORE_BATTERY_OPTIMIZATION_SETTINGS的Intent,让用户直接进入电池优化界面,他们可以在其中添加应用。
- 具有REQUEST_IGNORE_BATTERY_OPTIMIZATIONS权限的应用可以触发系统对话框,让用户无须转到“设置”即可直接将应用添加到白名单。应用将通过触发Settings.ACTION_IGNORE_BATTERY_OPTIMIZATION_SETTINGS的Intent来触发该对话框。
- 用户可以根据需要手动从白名单中移除应用。
Android8.0上的后台限制
Android6.0和7.0两个版本提供了Project Volta、Doze模式以及App StandBy机制来降低功耗以延长电池寿命。
但实际上Android系统上最令人诟病的“后台问题‘仍然没有得到解决:应用程序很容易通过监听各种广播的方式来启动后台服务,然后长时间在后台保持活跃。这样做无疑会导致电池电量很快耗尽。Android系统的用户对此应该深有体会,通过系统设置中额运行中应用列表,总能看到一大串的服务在后台运行着。
概览
Android是一个多任务的os。例如,用户可以在一个窗口中玩游戏,同时在另一个窗口中浏览网页,并使用第三个应用播放音乐。
同时运行的应用越多,对系统造成的负担越大。如果还有应用或服务在后台运行,会对系统造成更大负担,进而可能导致用户体验下降。例如,音乐应用可能会突然关闭。
为了减低发生这些问题的概率,Android8.0对应用在用户不与其直接交互时可以执行的操作施加了限制。
应用在两个方面受到限制:
-
后台服务限制:处于空闲状态时,应用可以使用的后台服务存在限制,但这些限制不实施于前台服务,因为前台服务更容易引起用户注意。
-
广播限制:除了有限的例外情况,应用无法使用AndroidManife.xml注册隐式广播。但它们仍然可以在运行时注册这些广播,并且可以使用AndroidManifest.xml注册专门针对它们的显式广播。
默认情况下,这些限制仅适用于针对8.0的应用。不过,用户可以从Settings屏幕为任意应用启用这些限制,即使应用并不是以8.0为目标平台。
后台服务限制
在后台中运行的服务会消耗设备资源,这可能会降低用户体验。为了缓解这一问题,系统对这些服务施加了一些限制。
如果满足以下条件中的任意一个,应用都将被视为处于前台:
- 具有可见activity,不管该activity处于resume还是pause状态
- 具有前台服务
- 另一个前台应用关联到当前应用,可能是绑定到其中一个service,或者是使用其中一个contentprovider。
如果以上条件均不满足,则应用被视为处于后台。
处于前台时,应用可以自由创建和运行前台服务和后台服务。进入后台时,在一个持续数分钟的时间窗内,应用仍可以创建和使用服务。
在该时间窗结束后,应用将被视为处于空闲状态。此时,系统将停止应用的后台服务,就像应用已经调用服务的stopself()方法。
在下面这些情况下,后台应用将被置于一个临时白名单中并持续数分钟。位于白名单中时,应用可以无限制地启动服务,并且其后台服务也可以运行。
处理对用户可见地任务时,应用将被置于白名单中,例如:
- 处理一条高优先级fcm云消息。
- 接收广播,例如,短信/彩信。
- 从通知中执行pendingintent。
在很多情况下,应用都可以使用JobScheduler来替换后台服务。在Android8.0之前,创建前台服务的方式通常时先创建一个后台服务,然后将该服务推到前台。Android8.0有一项复杂功能:系统不允许后台应用创建后台服务。因此,Android8.0引入了一个权全新的方法,即Context.startForegroundService(),以在前台启动新服务。在系统创建服务后,应用有5s的时间来调用该服务的startForeground方法以显示新服务的用户可见通知。如果应用在此时间限制内未调用该方法,则会报anr的错误。
广播限制
如果应用注册为接收广播,则在每次发送广播时,应用的接收器都会消耗资源。如果多个应用注册为接收基于系统事件的广播,这会引发问题:触发广播的系统事件会导致所有应用快速地连续消耗资源,从而降低用户体验。
为了缓解这一问题,Android7.0对广播施加了一些限制,而Android8.0让这些限制更为严格。
- Android8.0的应用无法继续在其AndroidManifest.xml中为隐式广播注册广播接收器。隐式广播是一种不专门针对该应用的广播,它将发送到注册的所有侦听器中。
- 应用可以继续在它们的清单中注册显式广播。
- 应用可以在运行时使用registerReceiver动态地为任意广播(不管是隐式还是显式)注册接收器。
- 需要签名权限地广播不受此限制所限,因为这些广播只会发送到使用相同证书签名地应用,而不是发送到设备上地所有应用。
- 在Android8.0版本上,系统对应用程序的后台位置也进行了限制:为降低功耗,无论应用的目标sdk版本是什么,Android8.0都会对后台应用检索用户当前位置的频率进行限制。