Android原生限制后台活动,头号作家详解系统行为管控机制与影响
栏目介绍
“头号作家”是由OPPO工程师们解读时下热门技术的专题栏目。在这里,你不仅能看到最新最火的动态趋势,更能与OPPO的优秀工程师们共同学习技术干货。
头号作家
■Dustin
■Android框架层工程师,热爱技术研究,热爱技术分享,目前专注于后台管控相关技术的研究。
前言
Android系统是可以支持多个应用、多个进程同时在后台运行的,而应用每次运行,必然会消耗一定的设备资源。随着用户的使用,大量应用在后台运行,对系统功耗、性能的影响将更加显著,进而导致用户在前台使用占资源较多应用的时候,更容易出现卡顿、发热、甚至前台应用被杀死等直接影响用户体验的情况。在设备资源有限的情况下,保证前台应用的正常运行才是更符合用户体验的。
出于以上原因的考虑,Google大版本更新迭代的时候,也会逐步增加对系统资源的管控,而在Android O版本中,谷歌的做法是推出了“Background Execution Limits”,旨在限制应用的service、broadcast在后台的行为, 通过约束这种非用户主动操作且用户无法感知的应用行为,提升用户体验。
android developer:https://developer.android.google.cn/about/versions/oreo/background
本文将详细介绍这个“Background Execution Limits”这个后台行为限制。首先,讲解限制的应用范围,介绍应用处于哪些状态的时候,会受到这个限制;第二部分是讲解对service的限制的实现;第三部分是讲解对broadcast 的限制。
限制应用范围
android developer告诉我们,前台应用将不会受到限制,而以下三种情况将被视为前台应用:
.具有可见的Activity
.具有前台service
.另一个前台应用已关联到该应用
实际上,除了前台应用,还有一些情况下的应用也不会受到限制。我们直接从代码上来看吧,关键就是AMS的getAppStartModeLocked方法。
1.1getAppStartModeLocked--应用启动模式
在AMS.getAppStartModeLocked这个方法里,有四个返回值,而返回
ActivityManager.APP_START_MODE_NORMAL的,就表示允许后台行为。此方法简易的流程图如下:
接下来详细介绍代码流程:
ActivityManagerService.getAppStartModeLocked
总结主要逻辑是:
1.uid在mPendingStartActivityUids中,uid即将启动一个activity或者就是一个前台显示,允许后台行为
2. uid为null或不是idle状态时,允许后台行为,1.1.2 介绍什么时候应用会是idle状态。
3.uid是idle状态时,主要看appRestrictedInBackgroundLocked和appServicesRestrictedInBackgroundLocked方法。
1.1.1appServicesRestrictedInBackgroundLocked
appServicesRestrictedInBackgroundLocked方法主要是检查service启动是否有权限。
ActivityManagerService.appServicesRestrictedInBackgroundLocked
这里主要逻辑判断几个条件:
1. Persistent属性的app,放行。
2. 应用的uid在uid白名单中(这里只有蓝牙uid),放行。
3. 应用在doze白名单中,放行。看1.1.1.2
4.继续判断appRestrictedInBackgroundLocked方法,看1.1.2
1.1.1.2 isOnDeviceIdleWhitelistLocked
isOnDeviceIdleWhitelistLocked方法是判断各个doze的白名单:
1. mDeviceIdleExceptIdleWhitelist:doze的ExceptIdle白名单,用于豁免除了idle之外的省电功能。
2. mDeviceIdleWhitelist:doze的全白名单,用于豁免所有的省电功能。
3. mDeviceIdleTempWhitelist:doze的临时白名单。
4. mPendingTempWhitelist:pending的临时白名单。
ActivityManagerService.isOnDeviceIdleWhitelistLocked
1.1.2appRestrictedInBackgroundLocked
appRestrictedInBackgroundLocked主要是考虑到O以下版本sdk的兼容。
ActivityManagerService.appRestrictedInBackgroundLocked
1.2uid什么时候会进入idle
1.1在getAppStartModeLocked方法中提到,uid不是idle状态时,回返回true。那uid什么时候会进入idle呢?简单来说就是应用处于后台超过1分钟,就会被设置进入到idle状态,流程如下:
在AMS进行进程优先级(adj)计算的时候,updateOomAdjLocked和updateOomAdjLockedInner方法会调用一次updateUidsLocked,用于更新uid的状态。
ActivityManagerService.updateUidsLocked
这个方法主要就是在uid的进程状态发生改变的时候,调用isProcStateBackground方法判断是否需要发送延迟
msg处理uid idle,接着看isProcStateBackground是做什么事情的。
1.2.1isProcStateBackground
isProcStateBackground这个方法很简单,就是看procState是否是大于PROCESS_STATE_TRANSIENT_BACKGROUND的。
ActivityManager.isProcStateBackground
官方描述中提到的前台应用的范畴,procState都会是小于PROCESS_STATE_TRANSIENT_BACKGROUND(8)的,包括以下procState。
ActivityManager.PROCESS_STATE_UNKNOWN
1.2.2idleUidsLocked
对于ProcState是“Background”的应用,将会在idleUidsLocked中进行逻辑处理。
OomAdjuster.idleUidsLocked
idleUidsLocked方法就是将所有active的uid的上次处于后台的时间(uidRec.lastBackgroundTime)拿出来计算,如果lastBackgroundTime是0(procState小于8)或未超过60s,则等到下一次计算idle再处理,否则将idle置为true。
1.3限制应用范围-小结
通过以上对代码的分析走读,可以得知后台限制service主要就是看重uid、UidRecord,即整个包/应用的状态,而不是某个进程的状态,同时也得知了有什么应用不会被限制(大于O的版本):
top或即将是top的应用
进程procState小于8的应用
Persistent属性的应用uid在uid白名单的应用
uid在doze白名单中的应用
应用开发者也可以从以上几点考虑是否能绕过限制。
但如果getAppStartModelocked方法中传入的alwaysRestrict为true,如果静态广播的情况,则无法通过上述条件被放行。
限制后台Service
对于后台service的限制行为主要有两个,startService限制和 stopInBackground。
2.1startService限制
可能不少开发者在编写自己的应用的时候,想通过startService启动一个service,都会遇到过一下这行提示:
这就是Android对后台startService的限制。
2.1.1startServiceLocked
了解startService流程的同学都知道,调用Context.startService或Context.startForegroundService之后都会走到
ActiveServices.startServiceLocked,而Android对于后台startservice限制就是在这里做的。
ActiveServices.startServiceLocked
可见startServiceLocked方法中,如果应用并非起前台服务或被用户限制后台行为:
1. 如果允许getAppStartModeLocked应用启动,继续startService流程。
2. 如果不允许应用启动,是用户主动加进限制名单,返回null,不会抛出异常。
3. 如果不允许应用启动,且不是用户主动加进限制名单,返回一个ComponentName,将会抛出异常。
2.1.2startService抛出异常
在ActiveServices.startServiceLocked中返回的ComponentName最终回到应用侧的
ContextImpl.startServiceCommon中进行处理,最终抛出一个异常。
ContextImpl.startServiceCommon
2.2stopInBackground
可能不少开发者在编写自己的应用的时候,启动了service,但是过了一段时间,发现这个service被stop了,查看log发现了以下这行提示:
这个就涉及到我们前文讲的uid的idle计算过程,将某个uid置为idle之后,会调用ActiveServices.stopInBackgroundLocked。
2.2.1stopInBackgroundLocked
如果应用的uid变为idle,或者应用被限制后台的时候,会调用stopInBackgroundLocked,将此uid下的后台的service都stop了。
ActiveServices.stopInBackgroundLocked
stopInBackgroundLocked方法就是通过getAppStartModeLocked方法筛选出需要进行限制的service,调用stopServiceLocked进行逻辑处理,这后面的逻辑与应用直接调用Context.stopService方法流程一致,不在这篇文章里做过多阐述。
广播限制
介绍广播限制之前先介绍几个广播基本的概念:
静态注册广播接收器:在应用的AndroidManifest清单中声明注册静态的广播接收器
动态注册广播接收器:运行时使用 Context.registerReceiver()注册动态的广播接收器
显式广播:指定特定的广播接收者发送的广播
隐式广播:不指定特定的广播接收者发送的广播
而这里Android对广播的限制就是,发送隐式广播时,静态注册的广播接收器无法接收到广播,这是怎么实现的呢?我们看看代码。
3.1广播分发流程
首先先看看广播分发流程,我这里大概分为四个部分:
1.处理并行广播并进行分发
2.分发有序广播前的准备工作
3.处理动态注册的receiver
4.处理静态注册的receiver(广播限制的重点在于这部分)
代码解析这里省略很多步骤,重点关注与广播限制相关的地方。
BroadcastQueue.processNextBroadcastLocked
3.2广播限制小结
可见后台对广播的限制有几个条件,满足以下条件的广播会被限制:
1. 广播接受者类型:广播接受者是静态注册的广播接收器。
2. 广播接收者应用状态:应用不允许后台启动,即getAppStartModeLocked中返回的不是APP_START_MODE_NORMAL。
3. 广播类型:广播有FLAG_RECEIVER_EXCLUDE_BACKGROUND的flag,限制启动;或者广播是隐式广播
(隐式广播,即在intent中没有指定接受者的包名、组件名且没有
FLAG_RECEIVER_INCLUDE_BACKGROUND的广播)且接受者权限不是Signature的广播,限制启动。
推荐阅读
声明:本文由"百度是最好的搜索"发布,不代表"破站"立场,转载联系作者并注明出处:https://www.848586.cn/301799.html
