文章目錄
前言
在Activity啓動過程中,ActivityManagerService會與APP進程進行交互,調度APP進程上的Activity的生成和初始化,以及生命週期的逐步切換和回調。
在這期間的調度交互中ActivityManagerService需要知道APP進程端的執行結果來進行下一步操作,因此ActivityManagerService需要一個超時判定機制,來應對APP進程端執行過久或無響應時的情況。
例如,當目標Activity所屬進程未啓動時,ActivityManagerService需要先請求Zygote創建進程,然後等待進程創建完畢後繼續啓動目標Activity,那麼AMS如何監聽進程啓動是否太久呢?還有當啓動目標Activity前,會先pause當前正顯示的Activity,之後才顯示目標Activity,那麼AMS是否會無限期等待?
源碼探究
文中源碼基於Android 9.0
ActivityManagerService在調度Activity的各個階段都需要超時處理,這裏按照《Activity啓動流程總結-生命週期》中的過程來分階段看。
Activity pause階段
Activity啓動流程中,首先需要暫停當前正在顯示的Activity。
超時的設置
[ActivityStack.java]
final boolean startPausingLocked(boolean userLeaving, boolean uiSleeping,
ActivityRecord resuming, boolean pauseImmediately) {
// ···
ActivityRecord prev = mResumedActivity;
// ···
mPausingActivity = prev;
// ···
if (prev.app != null && prev.app.thread != null) {
try {
// 調度APP進程執行對應Activity的pause行爲
mService.getLifecycleManager().scheduleTransaction(prev.app.thread, prev.appToken,
PauseActivityItem.obtain(prev.finishing, userLeaving,
prev.configChangeFlags, pauseImmediately));
} catch (Exception e) {
// Ignore exception, if process died other code will cleanup.
Slog.w(TAG, "Exception thrown during pause", e);
mPausingActivity = null;
mLastPausedActivity = null;
mLastNoHistoryActivity = null;
}
} else {
mPausingActivity = null;
mLastPausedActivity = null;
mLastNoHistoryActivity = null;
}
// ···
// If we are not going to sleep, we want to ensure the device is
// awake until the next activity is started.
if (!uiSleeping && !mService.isSleepingOrShuttingDownLocked()) {
// 獲取Wakelock並設置超時監聽
mStackSupervisor.acquireLaunchWakelock();
}
if (mPausingActivity != null) {
// ···
// pauseImmediately默認爲false
if (pauseImmediately) {
// If the caller said they don't want to wait for the pause, then complete
// the pause now.
completePauseLocked(false, resuming);
return false;
} else {
// 設置超時監聽
schedulePauseTimeout(prev);
return true;
}
} else {
// This activity failed to schedule the
// pause, so just treat it as being paused now.
if (DEBUG_PAUSE) Slog.v(TAG_PAUSE, "Activity not running, resuming next.");
if (resuming == null) {
mStackSupervisor.resumeFocusedStackTopActivityLocked();
}
return false;
}
}
該方法中會設置兩個超時,在通過scheduleTransaction方法調度Activity進行pause後,就調用acquireLaunchWakelock和schedulePauseTimeout方法進行超時設置。
LAUNCH_TIMEOUT_MSG
[ActivityStackSupervisor.java]
void acquireLaunchWakelock() {
if (VALIDATE_WAKE_LOCK_CALLER && Binder.getCallingUid() != Process.myUid()) {
throw new IllegalStateException("Calling must be system uid");
}
mLaunchingActivity.acquire();
if (!mHandler.hasMessages(LAUNCH_TIMEOUT_MSG)) {
// To be safe, don't allow the wake lock to be held for too long.
mHandler.sendEmptyMessageDelayed(LAUNCH_TIMEOUT_MSG, LAUNCH_TIMEOUT);
}
}
可以看出這裏是通過mHandler發送延遲消息的方式來進行超時設置,what爲LAUNCH_TIMEOUT_MSG,LAUNCH_TIMEOUT爲10s。
PAUSE_TIMEOUT_MSG
[ActivityStack.java]
private void schedulePauseTimeout(ActivityRecord r) {
final Message msg = mHandler.obtainMessage(PAUSE_TIMEOUT_MSG);
msg.obj = r;
r.pauseTime = SystemClock.uptimeMillis();
mHandler.sendMessageDelayed(msg, PAUSE_TIMEOUT);
if (DEBUG_PAUSE) Slog.v(TAG_PAUSE, "Waiting for pause to complete...");
}
通過mHandler發送延遲消息的方式來進行超時設置,Message的what賦值爲PAUSE_TIMEOUT_MSG,obj爲需要pause的ActivityRecord,pauseTime爲系統啓動以來時間。
[ActivityStack.java]
private static final int PAUSE_TIMEOUT = 500;
PAUSE_TIMEOUT表示500ms,留給Activity pause的時間很短,在500ms後就會觸發超時。
mHandler的由來
- ActivityStack的mHandler成員在ActivityStack的構造函數中實例化:
[ActivityStack.java]
ActivityStack(ActivityDisplay display, int stackId, ActivityStackSupervisor supervisor,
int windowingMode, int activityType, boolean onTop) {
mHandler = new ActivityStackHandler(mService.mHandler.getLooper());
}
mHandler實例的類型爲ActivityStackHandler(繼承自Handler),通過ActivityManagerService的mHandler的成員的Looper創建。
- ActivityStackSupervisor的mHandler成員在構造函數中創建,其實例爲ActivityStackSupervisorHandler:
[ActivityStackSupervisor.java]
public ActivityStackSupervisor(ActivityManagerService service, Looper looper) {
mService = service;
mLooper = looper;
mHandler = new ActivityStackSupervisorHandler(looper);
}
- ActivityManagerService的mHandler成員在構造函數中實例化,其實例爲創建MainHandler。
構造函數中創建ServiceThread(繼承自HandlerThread)並啓動,之後將Looper設置給mHandler。通過mHandler發送的消息將運行在ServiceThread中。
[ActivityManagerService.java]
public ActivityManagerService(Context systemContext) {
mHandlerThread = new ServiceThread(TAG,
THREAD_PRIORITY_FOREGROUND, false /*allowIo*/);
mHandlerThread.start();
mHandler = new MainHandler(mHandlerThread.getLooper());
}
超時的觸發
LAUNCH_TIMEOUT_MSG
進入ActivityStackSupervisorHandler的handleMessage方法的LAUNCH_TIMEOUT_MSG case:
[ActivityStack.java]
public void handleMessage(Message msg) {
switch (msg.what) {
case LAUNCH_TIMEOUT_MSG: {
synchronized (mService) {
if (mLaunchingActivity.isHeld()) {
Slog.w(TAG, "Launch timeout has expired, giving up wake lock!");
if (VALIDATE_WAKE_LOCK_CALLER
&& Binder.getCallingUid() != Process.myUid()) {
throw new IllegalStateException("Calling must be system uid");
}
// 釋放Wakelock
mLaunchingActivity.release();
}
}
} break;
// ···
}
}
PAUSE_TIMEOUT_MSG
進入ActivityStackHandler的handleMessage方法的PAUSE_TIMEOUT_MSG case:
[ActivityStack.java]
public void handleMessage(Message msg) {
switch (msg.what) {
case PAUSE_TIMEOUT_MSG: {
// 獲取pausing的Activity對應的ActivityRecord
ActivityRecord r = (ActivityRecord)msg.obj;
// We don't at this point know if the activity is fullscreen,
// so we need to be conservative and assume it isn't.
Slog.w(TAG, "Activity pause timeout for " + r);
synchronized (mService) {
if (r.app != null) {
mService.logAppTooSlow(r.app, r.pauseTime, "pausing " + r);
}
// 傳入token和true
activityPausedLocked(r.appToken, true);
}
} break;
// ···
}
}
當過了500ms的延遲時間後,若觸發了PAUSE_TIMEOUT_MSG的消息,將執行activityPausedLocked方法,傳入Activity對應的窗口token和true(表示來自超時)。
activityPausedLocked方法在正常的Activity pause後也會調用。
接着進入activityPausedLocked方法:
[ActivityStack.java]
// timeout在超時觸發情況下爲true,正常pause情況下爲false
final void activityPausedLocked(IBinder token, boolean timeout) {
if (DEBUG_PAUSE) Slog.v(TAG_PAUSE,
"Activity paused: token=" + token + ", timeout=" + timeout);
final ActivityRecord r = isInStackLocked(token);
if (r != null) {
// 移除PAUSE_TIMEOUT_MSG消息
mHandler.removeMessages(PAUSE_TIMEOUT_MSG, r);
if (mPausingActivity == r) {
if (DEBUG_STATES) Slog.v(TAG_STATES, "Moving to PAUSED: " + r
+ (timeout ? " (due to timeout)" : " (pause complete)"));
mService.mWindowManager.deferSurfaceLayout();
try {
completePauseLocked(true /* resumeNext */, null /* resumingActivity */);
} finally {
mService.mWindowManager.continueSurfaceLayout();
}
return;
} else {
// ···
}
}
mStackSupervisor.ensureActivitiesVisibleLocked(null, 0, !PRESERVE_WINDOWS);
}
可以看出該方法中是否timeout的區別僅在日誌不同上,即使超時也會繼續進行後續的啓動Activity的流程。
超時的取消
當APP進程端完成Activity的pause操作後,會調用ActivityManagerService的activityPaused方法,而在該方法中又會通過token查找對應的ActivityStack,調用其activityPausedLocked方法,此時傳入的timeout爲false,在activityPausedLocked方法中就會移除PAUSE_TIMEOUT_MSG消息。
LAUNCH_TIMEOUT_MSG消息將會在目標Activity啓動完成後取消。
Application launch階段
若待啓動的目標Activity所屬進程未啓動,則AMS會先請求創建進程。
超時的設置
ActivityManagerService中會調用startProcessLocked方法進行請求創建進程:
[ActivityManagerService.java]
private boolean startProcessLocked(String hostingType, String hostingNameStr, String entryPoint,
ProcessRecord app, int uid, int[] gids, int runtimeFlags, int mountExternal,
String seInfo, String requiredAbi, String instructionSet, String invokeWith,
long startTime) {
// ···
// 進一步請求創建進程
final ProcessStartResult startResult = startProcess(app.hostingType, entryPoint,
app, app.startUid, gids, runtimeFlags, mountExternal, app.seInfo,
requiredAbi, instructionSet, invokeWith, app.startTime);
synchronized (ActivityManagerService.this) {
// 這個方法中進行超時設置
handleProcessStartedLocked(app, startResult, startSeq);
}
// ···
}
handleProcessStartedLocked方法中又會調用另一個重載方法:
[ActivityManagerService.java]
private boolean handleProcessStartedLocked(ProcessRecord app, int pid, boolean usingWrapper,
long expectedStartSeq, boolean procAttached) {
synchronized (mPidsSelfLocked) {
this.mPidsSelfLocked.put(pid, app);
// procAttached傳入爲false
if (!procAttached) {
// what爲PROC_START_TIMEOUT_MSG
Message msg = mHandler.obtainMessage(PROC_START_TIMEOUT_MSG);
// obj爲待啓動進程對應的ProcessRecord
msg.obj = app;
// 發送延遲消息
mHandler.sendMessageDelayed(msg, usingWrapper
? PROC_START_TIMEOUT_WITH_WRAPPER : PROC_START_TIMEOUT);
}
}
checkTime(app.startTime, "startProcess: done updating pids map");
return true;
}
這裏可以看出是通過ActivityManagerService的mHandler成員發送一個what=PROC_START_TIMEOUT_MSG的10s延遲消息。
[ActivityManagerService.java]
// 20min
static final int PROC_START_TIMEOUT_WITH_WRAPPER = 1200*1000;
// 10s
static final int PROC_START_TIMEOUT = 10*1000;
當有對啓動進程過程附加監聽時,將設置延遲時間爲1200s,例如設置了Valgrind內存監測工具。通常情況下延遲時間爲10s。
超時的觸發
ActivityManagerService的mHandler是在ActivityManagerService構造函數中初始化,其實例爲MainHandler,進入它的handleMessage方法:
[ActivityManagerService.java]
public void handleMessage(Message msg) {
switch (msg.what) {
// ···
case PROC_START_TIMEOUT_MSG: {
ProcessRecord app = (ProcessRecord)msg.obj;
synchronized (ActivityManagerService.this) {
processStartTimedOutLocked(app);
}
} break;
// ···
}
}
在PROC_START_TIMEOUT_MSG case中調用了processStartTimedOutLocked方法:
[ActivityManagerService.java]
private final void processStartTimedOutLocked(ProcessRecord app) {
final int pid = app.pid;
// 標記是否進行清理工作
boolean gone = false;
synchronized (mPidsSelfLocked) {
// mPidsSelfLocked中保存着所有在運行的APP進程信息
ProcessRecord knownApp = mPidsSelfLocked.get(pid);
if (knownApp != null && knownApp.thread == null) {
// 若存在APP進程信息,但是IApplicationThread爲空,則需要移除,並標記需要清理
mPidsSelfLocked.remove(pid);
gone = true;
}
}
if (gone) {
Slog.w(TAG, "Process " + app + " failed to attach");
EventLog.writeEvent(EventLogTags.AM_PROCESS_START_TIMEOUT, app.userId,
pid, app.uid, app.processName);
removeProcessNameLocked(app.processName, app.uid);
if (mHeavyWeightProcess == app) {
mHandler.sendMessage(mHandler.obtainMessage(CANCEL_HEAVY_NOTIFICATION_MSG,
mHeavyWeightProcess.userId, 0));
mHeavyWeightProcess = null;
}
mBatteryStatsService.noteProcessFinish(app.processName, app.info.uid);
// Take care of any launching providers waiting for this process.
// 處理等待該進程啓動的ContentProvider部分
cleanupAppInLaunchingProvidersLocked(app, true);
// Take care of any services that are waiting for the process.
// 處理等待該進程啓動的Service部分
mServices.processStartTimedOutLocked(app);
app.kill("start timeout", true);
if (app.isolated) {
mBatteryStatsService.removeIsolatedUid(app.uid, app.info.uid);
}
removeLruProcessLocked(app);
if (mBackupTarget != null && mBackupTarget.app.pid == pid) {
Slog.w(TAG, "Unattached app died before backup, skipping");
mHandler.post(new Runnable() {
@Override
public void run(){
try {
IBackupManager bm = IBackupManager.Stub.asInterface(
ServiceManager.getService(Context.BACKUP_SERVICE));
bm.agentDisconnected(app.info.packageName);
} catch (RemoteException e) {
// Can't happen; the backup manager is local
}
}
});
}
if (isPendingBroadcastProcessLocked(pid)) {
Slog.w(TAG, "Unattached app died before broadcast acknowledged, skipping");
// 跳過等待該進程啓動的廣播部分
skipPendingBroadcastLocked(pid);
}
} else {
// 否則僅打印一段日誌
Slog.w(TAG, "Spurious process start timeout - pid not known for " + app);
}
}
該方法中會對AMS的mPidsSelfLocked成員中保存的ProcessRecord進行檢查,若存在對應的ProcessRecord卻不存在IApplicationThread,則需要進行清理工作。否則打印一段日誌。
APP進程正常啓動情況下,會將對應的ProcessRecord保存在mPidsSelfLocked中。
超時的取消
APP進程正常啓動後,會通知AMS:
[ActivityThread.java]
private void attach(boolean system, long startSeq) {
final IActivityManager mgr = ActivityManager.getService();
try {
// 通過IActivityManager binder通信接口通知到AMS,最終調用到AMS的attachApplication方法。
// 這裏將IApplicationThread和啓動序列號傳給AMS,用於AMS向APP進程通信和AMS匹配啓動的ProcessRecord。
mgr.attachApplication(mAppThread, startSeq);
} catch (RemoteException ex) {
throw ex.rethrowFromSystemServer();
}
}
在AMS的attachApplication方法中又調用attachApplicationLocked方法:
[ActivityManagerService.java]
private final boolean attachApplicationLocked(IApplicationThread thread,
int pid, int callingUid, long startSeq) {
// ···
mHandler.removeMessages(PROC_START_TIMEOUT_MSG, app);
// ···
}
在AMS的attachApplicationLocked方法中將移除PROC_START_TIMEOUT_MSG消息。
Activity launch階段
超時的設置
在ActivityStack的resumeTopActivityInnerLocked方法中,當判斷APP進程存在時,將會調度Activity resume事務,併發送超時消息:
[ActivityStack.java]
private boolean resumeTopActivityInnerLocked(ActivityRecord prev, ActivityOptions options) {
// ···
if (next.app != null && next.app.thread != null) {
// next爲將要顯示的Activity對應的ActivityRecord
next.completeResumeLocked();
} else {
// ···
}
// ···
}
在ActivityRecord的completeResumeLocked方法中將調用ActivityStackSupervisor的scheduleIdleTimeoutLocked方法:
[ActivityStackSupervisor.java]
void scheduleIdleTimeoutLocked(ActivityRecord next) {
if (DEBUG_IDLE) Slog.d(TAG_IDLE,
"scheduleIdleTimeoutLocked: Callers=" + Debug.getCallers(4));
// 發送IDLE_TIMEOUT_MSG消息
Message msg = mHandler.obtainMessage(IDLE_TIMEOUT_MSG, next);
// IDLE_TIMEOUT值爲10s
mHandler.sendMessageDelayed(msg, IDLE_TIMEOUT);
}
通過mHandler(爲ActivityStackSupervisorHandler)發送一個10s的IDLE_TIMEOUT_MSG消息消息。
超時的觸發
進入ActivityStackSupervisorHandler的handleMessage方法,看對應的IDLE_TIMEOUT_MSG case:
[ActivityStackSupervisor.java]
public void handleMessage(Message msg) {
switch (msg.what) {
case IDLE_TIMEOUT_MSG: {
if (DEBUG_IDLE) Slog.d(TAG_IDLE,
"handleMessage: IDLE_TIMEOUT_MSG: r=" + msg.obj);
// We don't at this point know if the activity is fullscreen,
// so we need to be conservative and assume it isn't.
// 調用ActivityStackSupervisorHandler的activityIdleInternal方法
activityIdleInternal((ActivityRecord) msg.obj,
true /* processPausingActivities */);
} break;
}
}
最終將調用ActivityStackSupervisor的activityIdleInternalLocked方法。
超時的取消
Activity啓動完成後,將調用AMS的activityIdle方法通知啓動完成,在activityIdle中又會調用ActivityStackSupervisor的activityIdleInternalLocked方法:
[ActivityStackSupervisor.java]
final ActivityRecord activityIdleInternalLocked(final IBinder token, boolean fromTimeout,
boolean processPausingActivities, Configuration config) {
ActivityRecord r = ActivityRecord.forTokenLocked(token);
if (r != null) {
// ···
// 移除IDLE_TIMEOUT_MSG消息
mHandler.removeMessages(IDLE_TIMEOUT_MSG, r);
// ···
}
if (allResumedActivitiesIdle()) {
// ···
if (mLaunchingActivity.isHeld()) {
// 移除IDLE_TIMEOUT_MSG消息
mHandler.removeMessages(LAUNCH_TIMEOUT_MSG);
if (VALIDATE_WAKE_LOCK_CALLER &&
Binder.getCallingUid() != Process.myUid()) {
throw new IllegalStateException("Calling must be system uid");
}
// 釋放WakeLock
mLaunchingActivity.release();
}
ensureActivitiesVisibleLocked(null, 0, !PRESERVE_WINDOWS);
}
}
AMS在收到Activity啓動完成通知調用後,就會移除IDLE_TIMEOUT_MSG和LAUNCH_TIMEOUT_MSG消息。
Activity stop階段
當目標Activity顯示後,將處理待執行stop的那些Activity。
超時的設置
停止Activity會調用ActivityStack的stopActivityLocked方法:
[ActivityStack.java]
final void stopActivityLocked(ActivityRecord r) {
// ···
Message msg = mHandler.obtainMessage(STOP_TIMEOUT_MSG, r);
// STOP_TIMEOUT爲 11 * 1000
mHandler.sendMessageDelayed(msg, STOP_TIMEOUT);
// ···
}
發送11s延遲的STOP_TIMEOUT_MSG消息。
超時的觸發
進入ActivityStackHandler的handleMessage方法:
[ActivityStack.java]
public void handleMessage(Message msg) {
switch (msg.what) {
case STOP_TIMEOUT_MSG: {
ActivityRecord r = (ActivityRecord)msg.obj;
// We don't at this point know if the activity is fullscreen,
// so we need to be conservative and assume it isn't.
Slog.w(TAG, "Activity stop timeout for " + r);
synchronized (mService) {
if (r.isInHistory()) {
r.activityStoppedLocked(null /* icicle */,
null /* persistentState */, null /* description */);
}
}
} break;
}
}
即使APP進程端stop Activity超時,AMS也會進行後續的操作。
超時的取消
Activity在stop完成後,會調用AMS的activityStopped方法,在該方法中會查找對應ActivityRecord,調用其activityStoppedLocked方法:
[ActivityRecord.java]
final void activityStoppedLocked(Bundle newIcicle, PersistableBundle newPersistentState,
CharSequence description) {
// ···
stack.mHandler.removeMessages(STOP_TIMEOUT_MSG, this);
// ···
}
在該方法中移除STOP_TIMEOUT_MSG超時消息。
Activity destroy階段
超時的設置
銷燬APP進程中的Activity時會調用ActivityStack的destroyActivityLocked方法:
[ActivityStack.java]
final boolean destroyActivityLocked(ActivityRecord r, boolean removeFromApp, String reason) {
r.setState(DESTROYING,
"destroyActivityLocked. finishing and not skipping destroy");
Message msg = mHandler.obtainMessage(DESTROY_TIMEOUT_MSG, r);
// DESTROY_TIMEOUT爲 10 * 1000
mHandler.sendMessageDelayed(msg, DESTROY_TIMEOUT);
}
發送10s延遲的DESTROY_TIMEOUT_MSG消息。
超時的觸發
進入ActivityStackHandler的handleMessage方法:
[ActivityStack.java]
public void handleMessage(Message msg) {
switch (msg.what) {
case DESTROY_TIMEOUT_MSG: {
ActivityRecord r = (ActivityRecord)msg.obj;
// We don't at this point know if the activity is fullscreen,
// so we need to be conservative and assume it isn't.
Slog.w(TAG, "Activity destroy timeout for " + r);
synchronized (mService) {
// 進行清理和移除相關操作
activityDestroyedLocked(r != null ? r.appToken : null, "destroyTimeout");
}
} break;
}
}
APP進程端destroy Activity如果超時,AMS會進行清理相關操作。
超時的取消
APP進程端destroy Activity完成後會調用AMS的activityDestroyed方法,該方法中最終會調用ActivityStack的activityDestroyedLocked方法:
[ActivityStack.java]
final void activityDestroyedLocked(ActivityRecord record, String reason) {
if (record != null) {
// 移除DESTROY_TIMEOUT_MSG消息
mHandler.removeMessages(DESTROY_TIMEOUT_MSG, record);
}
// 進行清理和移除相關操作 ···
}
AMS收到通知後即會移除DESTROY_TIMEOUT_MSG消息。
總結
AMS在pause Activity時會通過Handler發送LAUNCH_TIMEOUT_MSG(10s)和PAUSE_TIMEOUT_MSG(500ms)超時消息;在launch Application時發送**PROC_START_TIMEOUT_MSG(10s)超時消息;launch Activity時發送IDLE_TIMEOUT_MSG(10s)超時消息;stop Activity時發送STOP_TIMEOUT_MSG(11s)超時消息;destory Activity時發送DESTROY_TIMEOUT_MSG(10s)**超時消息。
當觸發超時後,AMS將自動進行後續階段的操作和清理操作。即使APP進程端處理耗時過久或異常或無響應,AMS也能保證調度過程不會因此出現停滯和混亂。