Android O之后,很多后臺啟動的行為都開始受限,比如O的時候,不能后臺啟動Service,而在Android10之后,連Activity也加到了后臺限制中。在Android O 后臺startService限制簡析中,層分析Android O之后,后臺限制啟動Service的場景,一般而言,APP退到后臺(比如按Home鍵),1分鐘之后變?yōu)楹笈_APP,雖然進程存活,但是已經(jīng)不能通過startService啟動服務(wù),但是發(fā)送通知并不受限制,可以通過通知啟動Service,這個時候,Service不會被當(dāng)做后臺啟動,同樣通過通知欄打開Activity也不受限制? 為什么,直觀來講,通知已經(jīng)屬于用戶感知的交互,本就不應(yīng)該算到后臺啟動。本文先發(fā)對比之前的Android O 后臺startService限制簡析,分析下Service,之后再看Activity在Android10中的限制
本文基于android10-release
通知借助PendingIntent啟動Service
可以模擬這樣一個場景,發(fā)送一個通知,然后將APP殺死,之后在通知欄通過PendingIntent啟動Service,看看是否會出現(xiàn)禁止后臺啟動Service的場景。
void notify() {
NotificationCompat.Builder builder = new NotificationCompat.Builder(this, NOTIFICATION_CHANNEL_ID);
builder.setContentIntent(PendingIntent.getService(this, (int) System.currentTimeMillis(),
new Intent(this,
BackGroundService.class),
PendingIntent.FLAG_UPDATE_CURRENT))
.setContentText("content")...)
NotificationManager nm = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
NotificationChannel channel = new NotificationChannel(NOTIFICATION_CHANNEL_ID,
"Channel human readable title",
NotificationManager.IMPORTANCE_DEFAULT);
if (nm != null) {
nm.createNotificationChannel(channel);
}
}
nm.notify(1, builder.build());
}
實際結(jié)果是:點擊通知后Service正常啟動。下面逐步分析下。
同普通的Intent啟動Service不同,這里的通知通過PendingIntent啟動,是不是只要PendingIntent就足夠了呢,并不是(后面分析)。通過通知啟動Service的第一步是通過PendingIntent.getService獲得一個用于啟動特定Service的PendingIntent:
public static PendingIntent getService(Context context, int requestCode,
@NonNull Intent intent, @Flags int flags) {
return buildServicePendingIntent(context, requestCode, intent, flags,
ActivityManager.INTENT_SENDER_SERVICE);
}
private static PendingIntent buildServicePendingIntent(Context context, int requestCode,
Intent intent, int flags, int serviceKind) {
String packageName = context.getPackageName();
String resolvedType = intent != null ? intent.resolveTypeIfNeeded(
context.getContentResolver()) : null;
try {
intent.prepareToLeaveProcess(context);
IIntentSender target =
ActivityManager.getService().getIntentSender(
serviceKind, packageName,
null, null, requestCode, new Intent[] { intent },
resolvedType != null ? new String[] { resolvedType } : null,
flags, null, context.getUserId());
return target != null ? new PendingIntent(target) : null;
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
}
IIntentSender在APP端其實是一個Binder代理,這里是典型的Binder雙向通信模型,AMS端會為APP構(gòu)建一個PendingIntentRecord extends IIntentSender.Stub實體, PendingIntentRecord可以看做PendingIntent在AMS端的記錄,最終形成兩者對應(yīng)的雙向通信通道。之后通知就會通過nm.notify顯示在通知欄,這一步先略過,先看最后一步,通過點擊通知啟動Service,通知點擊這不細(xì)看,只要明白最后調(diào)用的是PendingIntent的sendAndReturnResult函數(shù),
public int sendAndReturnResult(Context context, int code, @Nullable Intent intent,
@Nullable OnFinished onFinished, @Nullable Handler handler,
@Nullable String requiredPermission, @Nullable Bundle options)
throws CanceledException {
try {
String resolvedType = intent != null ?
intent.resolveTypeIfNeeded(context.getContentResolver())
: null;
return ActivityManager.getService().sendIntentSender(
mTarget, mWhitelistToken, code, intent, resolvedType,
onFinished != null
? new FinishedDispatcher(this, onFinished, handler)
: null,
requiredPermission, options);
} catch (RemoteException e) {
throw new CanceledException(e);
}
}
通過Binder最終到AMS端,查找到對應(yīng)的PendingIntentRecord,進入其sendInner函數(shù),前文buildIntent的時候,用的是 ActivityManager.INTENT_SENDER_SERVICE,進入對應(yīng)分支:
public int sendInner(int code, Intent intent, String resolvedType, IBinder whitelistToken,
IIntentReceiver finishedReceiver, String requiredPermission, IBinder resultTo,
String resultWho, int requestCode, int flagsMask, int flagsValues, Bundle options) {
if (whitelistDuration != null) {
duration = whitelistDuration.get(whitelistToken);
}
<!--是否可以啟動的一個關(guān)鍵點 ,后面分析-->
int res = START_SUCCESS;
try {
<!--duration非null才會執(zhí)行tempWhitelistForPendingIntent添加到白名單-->
if (duration != null) {
int procState = controller.mAmInternal.getUidProcessState(callingUid);
<!--u0_a16 2102 1742 4104448 174924 0 0 S com.android.systemui 通知是systemui進程 優(yōu)先級高沒后臺問題-->
if (!ActivityManager.isProcStateBackground(procState)) {
...
<!--更新臨時白名單, duration設(shè)定白名單的有效時長,這個是在發(fā)通知的時候設(shè)定的-->
controller.mAmInternal.tempWhitelistForPendingIntent(callingPid, callingUid,
uid, duration, tag.toString());
} else {
}
}
...
case ActivityManager.INTENT_SENDER_SERVICE:
case ActivityManager.INTENT_SENDER_FOREGROUND_SERVICE:
try {
controller.mAmInternal.startServiceInPackage(uid, finalIntent, resolvedType,
key.type == ActivityManager.INTENT_SENDER_FOREGROUND_SERVICE,
key.packageName, userId,
mAllowBgActivityStartsForServiceSender.contains(whitelistToken)
|| allowTrampoline);
} catch (RuntimeException e) { ...
其實最后進入controller.mAmInternal.startServiceInPackage,最后流到AMS的startServiceInPackage,接下來的流程在Android O 后臺startService限制簡析分析過,包括后臺限制的檢測,不過這里有一點是前文沒分析的,
int appServicesRestrictedInBackgroundLocked(int uid, String packageName, int packageTargetSdk) {
...
// Is this app on the battery whitelist?
if (isOnDeviceIdleWhitelistLocked(uid, /*allowExceptIdleToo=*/ false)) {
return ActivityManager.APP_START_MODE_NORMAL;
}
// None of the service-policy criteria apply, so we apply the common criteria
return appRestrictedInBackgroundLocked(uid, packageName, packageTargetSdk);
}
*/
boolean isOnDeviceIdleWhitelistLocked(int uid, boolean allowExceptIdleToo) {
final int appId = UserHandle.getAppId(uid);
final int[] whitelist = allowExceptIdleToo
? mDeviceIdleExceptIdleWhitelist
: mDeviceIdleWhitelist;
return Arrays.binarySearch(whitelist, appId) >= 0
|| Arrays.binarySearch(mDeviceIdleTempWhitelist, appId) >= 0
|| mPendingTempWhitelist.indexOfKey(uid) >= 0;
}
**那就是mPendingTempWhitelist白名單 **,這個是通知啟動Service不受限制的關(guān)鍵。
前文說過,通知發(fā)送時會設(shè)定一個臨時白名單的有效存活時間,只有設(shè)置了,才能進mPendingTempWhitelist,這是存活時間是從點擊到真正start中間所能存活的時間,如果在此間還未啟動,則判斷啟動無效。有效存活時間是什么時候設(shè)置的,是發(fā)送通知的時候,而且,這個時機只在發(fā)送通知的時候,其他沒入口:
/Users/XXX/server/notification/NotificationManagerService.java:
void enqueueNotificationInternal(final String pkg, final String opPkg, final int callingUid,
final int callingPid, final String tag, final int id, final Notification notification,
int incomingUserId) {
...
// Whitelist pending intents.
if (notification.allPendingIntents != null) {
final int intentCount = notification.allPendingIntents.size();
if (intentCount > 0) {
final ActivityManagerInternal am = LocalServices
.getService(ActivityManagerInternal.class);
final long duration = LocalServices.getService(
DeviceIdleController.LocalService.class).getNotificationWhitelistDuration();
for (int i = 0; i < intentCount; i++) {
PendingIntent pendingIntent = notification.allPendingIntents.valueAt(i);
if (pendingIntent != null) {
<!--更新白名單機制的一環(huán) ,只有通過這個檢測才能加到mPendingTempWhitelist白名單-->
am.setPendingIntentWhitelistDuration(pendingIntent.getTarget(),
WHITELIST_TOKEN, duration);
}
}
}
}
setPendingIntentWhitelistDuration會更新PendingIntentRecord的whitelistDuration列表,這個列表標(biāo)識著這個
public void setPendingIntentWhitelistDuration(IIntentSender target, IBinder whitelistToken,
long duration) {
synchronized (ActivityManagerService.this) {
((PendingIntentRecord) target).setWhitelistDurationLocked(whitelistToken, duration);
}
}
void setWhitelistDurationLocked(IBinder whitelistToken, long duration) {
if (duration > 0) {
if (whitelistDuration == null) {
whitelistDuration = new ArrayMap<>();
}
<!--設(shè)置存活時長-->
whitelistDuration.put(whitelistToken, duration);
} ...
}
存活時長設(shè)置后,通過點擊,啟動Service Intent就會被放到mPendingTempWhitelist,從而避免后臺檢測。如果不走通知,直接用PendingIntent的send呢,效果其實跟普通Intent沒太大區(qū)別,也會受后臺啟動限制,不過多分析。
Android10后臺啟動Activity限制 (android10-release源碼分支)
Android10之后,禁止后臺啟動Activity,Activity的后臺定義比Service更嚴(yán)格,延時10s,退到后臺,便可以模擬后臺啟動Activity,注意這里并沒有像Service限定到60之后,Activity的后臺限制更嚴(yán)格一些,直觀上理解:沒有可見窗口都可以算作后臺,中間的間隔最多可能就幾秒,比如我們延時10s就能看到這種效果。
void delayStartActivity() {
new Handler().postDelayed(new Runnable() {
@Override
public void run() {
Intent intent = new Intent(LabApplication.getContext(), MainActivity.class);
startActivity(intent);
}
}, 1000 * 10);
}
時間到了,在Android Q的手機上startActivity會報如下異常:
Background activity start [callingPackage: com.snail.labaffinity; callingUid: 10102;
* isCallingUidForeground: false;
* isCallingUidPersistentSystemProcess: false;
* realCallingUid: 10102;
* sRealCallingUidForeground: false;
* isRealCallingUidPersistentSystemProcess: false;
* originatingPendingIntent: null;
* isBgStartWhitelisted: false;
intent: Intent { cmp=com.snail.labaffinity/.activity.MainActivity }; callerApp: ProcessRecord{f17cc20 4896:com.snail.labaffinity/u0a102}]
未正式發(fā)行的版本上還能看到如下Toast
大概意思就是:限制后臺應(yīng)用啟動Activity。
核心邏輯在這一段 ActivityStarter
boolean shouldAbortBackgroundActivityStart(int callingUid, int callingPid,
final String callingPackage, int realCallingUid, int realCallingPid,
WindowProcessController callerApp, PendingIntentRecord originatingPendingIntent,
boolean allowBackgroundActivityStart, Intent intent) {
<!--系統(tǒng)應(yīng)用不受限制-->
// don't abort for the most important UIDs
final int callingAppId = UserHandle.getAppId(callingUid);
if (callingUid == Process.ROOT_UID || callingAppId == Process.SYSTEM_UID
|| callingAppId == Process.NFC_UID) {
return false;
}
<!--有可見窗口及系統(tǒng)進程不受限制-->
// don't abort if the callingUid has a visible window or is a persistent system process
final int callingUidProcState = mService.getUidState(callingUid);
<!--是否有可見窗口-->
final boolean callingUidHasAnyVisibleWindow =
mService.mWindowManager.mRoot.isAnyNonToastWindowVisibleForUid(callingUid);
<!--CallingUid是否前臺展示-->
final boolean isCallingUidForeground = callingUidHasAnyVisibleWindow
|| callingUidProcState == ActivityManager.PROCESS_STATE_TOP
|| callingUidProcState == ActivityManager.PROCESS_STATE_BOUND_TOP;
<!--是否PersistentSystemProcess-->
final boolean isCallingUidPersistentSystemProcess =
callingUidProcState <= ActivityManager.PROCESS_STATE_PERSISTENT_UI;
if (callingUidHasAnyVisibleWindow || isCallingUidPersistentSystemProcess) {
return false;
}
// take realCallingUid into consideration
final int realCallingUidProcState = (callingUid == realCallingUid)
? callingUidProcState
: mService.getUidState(realCallingUid);
final boolean realCallingUidHasAnyVisibleWindow = (callingUid == realCallingUid)
? callingUidHasAnyVisibleWindow
: mService.mWindowManager.mRoot.isAnyNonToastWindowVisibleForUid(realCallingUid);
final boolean isRealCallingUidForeground = (callingUid == realCallingUid)
? isCallingUidForeground
: realCallingUidHasAnyVisibleWindow
|| realCallingUidProcState == ActivityManager.PROCESS_STATE_TOP;
final int realCallingAppId = UserHandle.getAppId(realCallingUid);
final boolean isRealCallingUidPersistentSystemProcess = (callingUid == realCallingUid)
? isCallingUidPersistentSystemProcess
: (realCallingAppId == Process.SYSTEM_UID)
|| realCallingUidProcState <= ActivityManager.PROCESS_STATE_PERSISTENT_UI;
...
<!--這個權(quán)限不一定是誰都能拿到-->
// don't abort if the callingUid has START_ACTIVITIES_FROM_BACKGROUND permission
if (mService.checkPermission(START_ACTIVITIES_FROM_BACKGROUND, callingPid, callingUid)
== PERMISSION_GRANTED) {
return false;
}
// don't abort if the caller has the same uid as the recents component
if (mSupervisor.mRecentTasks.isCallerRecents(callingUid)) {
return false;
}
...一些系統(tǒng)判斷
<!--是否白名單-->
// don't abort if the callerApp or other processes of that uid are whitelisted in any way
if (callerApp != null) {
// first check the original calling process
if (callerApp.areBackgroundActivityStartsAllowed()) {
return false;
}
// only if that one wasn't whitelisted, check the other ones
final ArraySet<WindowProcessController> uidProcesses =
mService.mProcessMap.getProcesses(callerAppUid);
if (uidProcesses != null) {
for (int i = uidProcesses.size() - 1; i >= 0; i--) {
final WindowProcessController proc = uidProcesses.valueAt(i);
if (proc != callerApp && proc.areBackgroundActivityStartsAllowed()) {
return false;
}
}
}
}
<!--如果callAPP有懸浮窗權(quán)限-->
// don't abort if the callingUid has SYSTEM_ALERT_WINDOW permission
if (mService.hasSystemAlertWindowPermission(callingUid, callingPid, callingPackage)) {
Slog.w(TAG, "Background activity start for " + callingPackage
+ " allowed because SYSTEM_ALERT_WINDOW permission is granted.");
return false;
}
<!--其余全部禁止-->
// anything that has fallen through would currently be aborted
Slog.w(TAG, "Background activity start [callingPackage: " + callingPackage
+ "; callingUid: " + callingUid
+ "; isCallingUidForeground: " + isCallingUidForeground
+ "; isCallingUidPersistentSystemProcess: " + isCallingUidPersistentSystemProcess
+ "; realCallingUid: " + realCallingUid
+ "; isRealCallingUidForeground: " + isRealCallingUidForeground
+ "; isRealCallingUidPersistentSystemProcess: "
+ isRealCallingUidPersistentSystemProcess
+ "; originatingPendingIntent: " + originatingPendingIntent
+ "; isBgStartWhitelisted: " + allowBackgroundActivityStart
+ "; intent: " + intent
+ "; callerApp: " + callerApp
+ "]");
// log aborted activity start to TRON
if (mService.isActivityStartsLoggingEnabled()) {
mSupervisor.getActivityMetricsLogger().logAbortedBgActivityStart(intent, callerApp,
callingUid, callingPackage, callingUidProcState, callingUidHasAnyVisibleWindow,
realCallingUid, realCallingUidProcState, realCallingUidHasAnyVisibleWindow,
(originatingPendingIntent != null));
}
return true;
}
按照Google要求,在Android Q上運行的應(yīng)用只有在滿足以下一個或多個條件時才能啟動Activity:常見的有如下幾種
具有可見窗口,例如在前臺運行的Activity。(前臺服務(wù)不會將應(yīng)用限定為在前臺運行。)
該應(yīng)用在前臺任務(wù)的返回棧中具有一項 Activity。(必須同前臺Activity位于同一個Task返回棧,如果兩個Task棧不行。)
該應(yīng)用已獲得用戶授予的 SYSTEM_ALERT_WINDOW 權(quán)限。
-
pendingIntent臨時白名單機制,不攔截通過通知拉起的應(yīng)用。
通過通知,利用pendingIntent啟動 Activity。 通過通知,在 PendingIntent中發(fā)送廣播,接收廣播后啟動 Activity。 通過通知,在 PendingIntent中啟動 Service(一定可以啟動Service),在 Service 中啟動 Activity。 該應(yīng)用的某一項服務(wù)被其他可見應(yīng)用綁定(進程優(yōu)先級其實一致)。請注意,綁定到該服務(wù)的應(yīng)用必須在后臺對該應(yīng)用保持可見,才能成功啟動 Activity。
這里有一個比較有趣的點:如果應(yīng)用在前臺任務(wù)的返回棧中具有一項Activity,并不是說一定要自己APP的Activity在展示,而是說,當(dāng)前展示的Task棧里有自己的Activity就可以,這點判斷如下
boolean areBackgroundActivityStartsAllowed() {
<!--白名單-->
// allow if the whitelisting flag was explicitly set
if (mAllowBackgroundActivityStarts) {
return true;
}
...
<!--是否有Actvity位于前臺任務(wù)棧中-->
// allow if the caller has an activity in any foreground task
if (hasActivityInVisibleTask()) {
return true;
}
<!--被前臺APP綁定-->
// allow if the caller is bound by a UID that's currently foreground
if (isBoundByForegroundUid()) {
return true;
}
return false;
}
hasActivityInVisibleTask 判斷前臺TASK棧是否有CallAPP的Activity
private boolean hasActivityInVisibleTask() {
for (int i = mActivities.size() - 1; i >= 0; --i) {
TaskRecord task = mActivities.get(i).getTaskRecord();
if (task == null) {
continue;
}
ActivityRecord topActivity = task.getTopActivity();
if (topActivity == null) {
continue;
}
// If an activity has just been started it will not yet be visible, but
// is expected to be soon. We treat this as if it were already visible.
// This ensures a subsequent activity can be started even before this one
// becomes visible.
<!--只要是Task中的TOPActivity在展示,就判斷CallAPP可見或者即將可見,TOPActivity不一定是CallAPP的-->
if (topActivity.visible || topActivity.isState(INITIALIZING)) {
return true;
}
}
return false;
}
只要是Task中的TOPActivity在展示,就判斷CallAPP可見或者即將可見,TOPActivity不一定是CallAPP的,比如APP打開微信分享,如果直接上看APP是在后臺,但是微信分享Activity沒有單獨開一Activity Task,那么CallAPP還是被看做前臺,也就是他還可以啟動Activity,在前后臺的判斷上,更像下沉到Task維度,而不是Activity維度。同Service不同,Activity嚴(yán)重依賴CallAPP的狀態(tài),而Service更關(guān)心被啟動APP的狀態(tài)。
Android10后臺限制啟動Activity的系統(tǒng)bug
連續(xù)兩次啟動Activity,后臺啟動的限制會被打破
private boolean hasActivityInVisibleTask() {
for (int i = mActivities.size() - 1; i >= 0; --i) {
TaskRecord task = mActivities.get(i).getTaskRecord();
if (task == null) {
continue;
}
ActivityRecord topActivity = task.getTopActivity();
if (topActivity == null) {
continue;
}
<!--bug起源-->
// If an activity has just been started it will not yet be visible, but
// is expected to be soon. We treat this as if it were already visible.
// This ensures a subsequent activity can be started even before this one
// becomes visible.
if (topActivity.visible || topActivity.isState(INITIALIZING)) {
return true;
}
}
return false;
}
如果應(yīng)用位于后臺,第一次啟動Activity會被當(dāng)做后臺啟動,但是ActiivityRecord仍然會被創(chuàng)建,同時State會被設(shè)置成INITIALIZING,并且位于當(dāng)前將要啟動Task的棧頂,
ActivityRecord(ActivityTaskManagerService _service, WindowProcessController _caller,
int _launchedFromPid, int _launchedFromUid, String _launchedFromPackage, Intent _intent,
...
setState(INITIALIZING, "ActivityRecord ctor");
那么如果在后臺,再次通過startActivity啟動,當(dāng)前進程就會被認(rèn)為是在前臺,應(yīng)用就會被拉起,真是個奇葩bug。因為滿足如下條件。
topActivity.isState(INITIALIZING)
這個時候,Activity就可以在后臺被啟動。其實Android10后臺限制啟動Activity的并非完全不讓啟動,只是延遲,再次APP可見的時候,依舊可以把之前未啟動的Activity喚起。
PS :更新日期2019-12-12:谷歌的補丁似乎修復(fù)了這個bug

PendingIntent啟動Activity不受限制原理
通知的進程是系統(tǒng)進程
u0_a16 2102 1742 4104448 174924 0 0 S com.android.systemui
系統(tǒng)進程不受限制,就是這么流弊。
通知啟動Service,然后在Service中是允許啟動Activity不受后臺限制(奇葩)
對于通過PendingIntent通知啟動的APP,短時間內(nèi)不算后臺啟動Activity
從上面的注釋就能看出來,如果是通過通知啟動的,或者說如果是前臺應(yīng)用觸發(fā)的sendInner,那么短時間內(nèi)允許啟動Activity,雖然是通過Service啟動,但是如果是通知啟動的Service,那么暫且算是看做應(yīng)用位于前臺,如下:
先更新一個標(biāo)識mHasStartedWhitelistingBgActivityStarts,就是是否允許Service后臺啟動Activity的標(biāo)識,這里是設(shè)置為true,此刻進程可能還未啟動,
// is this service currently whitelisted to start activities from background by providing
// allowBackgroundActivityStarts=true to startServiceLocked()?
private boolean mHasStartedWhitelistingBgActivityStarts;
等到后面進程啟動了在attach的時候會繼續(xù)走Service的啟動流程
這里因為mHasStartedWhitelistingBgActivityStarts被設(shè)置為true,
就會走setAllowBackgroundActivityStarts 將mAllowBackgroundActivityStarts設(shè)置為true
public void setAllowBackgroundActivityStarts(boolean allowBackgroundActivityStarts) {
mAllowBackgroundActivityStarts = allowBackgroundActivityStarts;
}
這樣在啟動Activity時候,判斷是否允許后臺啟動就直接返回true
boolean areBackgroundActivityStartsAllowed() {
// allow if the whitelisting flag was explicitly set
if (mAllowBackgroundActivityStarts) {
return true;
}
這樣就構(gòu)建了允許后臺啟動Activity的場景,這個時限是10秒,10秒內(nèi)啟動Activity保證沒問題。
// For how long after a whitelisted service's start its process can start a background activity
public long SERVICE_BG_ACTIVITY_START_TIMEOUT = DEFAULT_SERVICE_BG_ACTIVITY_START_TIMEOUT;
因為之前啟動的時候,加了一個10s清理的監(jiān)聽回調(diào)
ams.mHandler.postDelayed(mStartedWhitelistingBgActivityStartsCleanUp,
ams.mConstants.SERVICE_BG_ACTIVITY_START_TIMEOUT);
到10s的時候回再次檢查一下是否需要清理掉,但是并非一定清理掉。
/**
* Called when the service is started with allowBackgroundActivityStarts set. We whitelist
* it for background activity starts, setting up a callback to remove the whitelisting after a
* timeout. Note that the whitelisting persists for the process even if the service is
* subsequently stopped.
*/
void whitelistBgActivityStartsOnServiceStart() {
setHasStartedWhitelistingBgActivityStarts(true);
if (app != null) {
mAppForStartedWhitelistingBgActivityStarts = app;
}
// This callback is stateless, so we create it once when we first need it.
if (mStartedWhitelistingBgActivityStartsCleanUp == null) {
mStartedWhitelistingBgActivityStartsCleanUp = () -> {
synchronized (ams) {
<!--如果Service進程存活,直接將start部分清理,但是bind部分需要再確認(rèn)-->
if (app == mAppForStartedWhitelistingBgActivityStarts) {
// The process we whitelisted is still running the service. We remove
// the started whitelisting, but it may still be whitelisted via bound
// connections.
setHasStartedWhitelistingBgActivityStarts(false);
} else if (mAppForStartedWhitelistingBgActivityStarts != null) {
<!--如果進程死了,10s還沒到,進程就掛了,那么直接全部干掉,不考慮ind-->
// The process we whitelisted is not running the service. It therefore
// can't be bound so we can unconditionally remove the whitelist.
mAppForStartedWhitelistingBgActivityStarts
.removeAllowBackgroundActivityStartsToken(ServiceRecord.this);
}
mAppForStartedWhitelistingBgActivityStarts = null;
}
};
}
// if there's a request pending from the past, drop it before scheduling a new one
ams.mHandler.removeCallbacks(mStartedWhitelistingBgActivityStartsCleanUp);
ams.mHandler.postDelayed(mStartedWhitelistingBgActivityStartsCleanUp,
ams.mConstants.SERVICE_BG_ACTIVITY_START_TIMEOUT);
}
總結(jié)
- 通過通知啟動Service不受后臺限制的原因是存在可更新PendingTempWhitelist白名單
- 后臺啟動Activity嚴(yán)重依賴CallAPP的狀態(tài),而Service更關(guān)心被啟動APP的狀態(tài)
- 位于后臺,連續(xù)多次startActivity就可以啟動Activity,目前看是個系統(tǒng)bug (2019-12-12實驗 新補丁似乎修復(fù)了這個功能 ,但是國內(nèi)ROM可能還有這個問題(如果不更新的話))
- Android10后臺限制啟動Activity的并非完全不讓啟動,只是延遲,再次APP可見的時候,依舊可以把之前未啟動的Activity喚起。
- 通過通知啟動Service,Service內(nèi)部不10s內(nèi)是允許后臺啟動Activity的,超過十秒就可能掛了
作者:看書的小蝸牛
AAndroid Notification、PendingIntent與后臺啟動Service、Activity淺析
僅供參考,歡迎指正