这次出现问题的堆栈是处理activity的pausetimeout的堆栈,在分析问题之前我们先了解下pausetimeout。下其大致流程如下(Android P代码)
在我们需要对当前resumed的activity做pause操作的时候,我们会调用startPausingLocked(ActivityStack.java),部分代码逻辑如下
final boolean startPausingLocked(boolean userLeaving, boolean uiSleeping,
ActivityRecord resuming, boolean pauseImmediately) {
//1.首先完成正在pausing的activity的pause操作,做到有序
if (mPausingActivity != null) {
Slog.wtf(TAG, "Going to pause when pause is already pending for " + mPausingActivity
+ " state=" + mPausingActivity.getState());
if (!shouldSleepActivities()) {
// Avoid recursion among check for sleep and complete pause during sleeping.
// Because activity will be paused immediately after resume, just let pause
// be completed by the order of activity paused from clients.
completePauseLocked(false, resuming);
}
}
//2. 将当前ResumedActivity设置为mPausingActivity
ActivityRecord prev = mResumedActivity;
…
mPausingActivity = prev;
mLastPausedActivity = prev;
…
if (mPausingActivity != null) {
….
if (pauseImmediately) {
// If the caller said they don't want to wait for the pause, then complete
// the pause now.
completePauseLocked(false, resuming); //调用completePauseLocked立刻对activity做Pause操作
return false;
} else {
schedulePauseTimeout(prev);//否则,调用schedulePauseTimeout
eturn true;
}
…..
}
如上代码可见,startPausingLocked方法首先对当前Stack中的mPausingActivity进行pause操作,然后将当前状态为resumed的activity的状态置为pausing并用此activity替换mPausingActivity变量,然后根据参数pauseImmediately的真假,调用completePauseLocked对mPausingActivity立刻做Pause操作或调用schedulePauseTimeout在500ms内完成Pause操作。
schedulePauseTimeout(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); //500ms
if (DEBUG_PAUSE) Slog.v(TAG_PAUSE, "Waiting for pause to complete...");
}
可以看到schedulePauseTimeout会发送一个延迟消息PAUSE_TIMEOUT_MSG给mHandler(ActivityStackHandler类型) 进行处理,延迟时间为PAUSE_TIMEOUT(500ms),此消息在relaunchActivityLocked,activityPausedLocked或removeTimeoutsForActivityLocked方法中移除。
在mHandler接收到PAUSE_TIMEOUT_MSG消息后会调用activityPausedLocked(r.appToken, true)对此activity进行处理,activityPausedLocked(ActivityStack.java)代码部分代码实现如下:
1)activityPausedLocked方法实现
final void activityPausedLocked(IBinder token, boolean timeout) {
final ActivityRecord r = isInStackLocked(token);
if (r != null) {
mHandler.removeMessages(PAUSE_TIMEOUT_MSG, r);
if (mPausingActivity == r) {
mService.mWindowManager.deferSurfaceLayout();
try {
completePauseLocked(true /* resumeNext */, null /* resumingActivity */);
} finally {
mService.mWindowManager.continueSurfaceLayout();
}
return;
}else {
if (r.isState(PAUSING)) {
r.setState(PAUSED, "activityPausedLocked");
if (r.finishing) {
finishCurrentActivityLocked(r, FINISH_AFTER_VISIBLE, false,"activityPausedLocked");
}}}}
mStackSupervisor.ensureActivitiesVisibleLocked(null, 0, !PRESERVE_WINDOWS);
}
activityPausedLocked方法首先将超时消息PAUSE_TIMEOUT_MSG移除,如果此时活动仍为mPausingActivity,则调用completePauseLocked(true,null)立刻对其进行pause操作,否则将活动的状态置为PAUSED,如果活动状态已经为finishing还需要调用finishCurrentActivityLocked方法来对其进行finish操作。最终都会调用ensureActivitiesVisibleLocked校正所有Task中的Activity的状态。
2)completePauseLocked( ActivityStack.java)方法实现
private void completePauseLocked(boolean resumeNext, ActivityRecord resuming) {
ActivityRecord prev = mPausingActivity;
if (prev != null) {
……
if (prev.finishing) {
prev = finishCurrentActivityLocked(prev, FINISH_AFTER_VISIBLE, false,
"completedPausedLocked");//状态已经为finish,完成finish操作
} else if (prev.app != null) {
if (mStackSupervisor.mActivitiesWaitingForVisibleActivity.remove(prev)) {//不再等待显示
}
if (prev.deferRelaunchUntilPaused) {
// Complete the deferred relaunch that was waiting for pause to complete.如注释
prev.relaunchActivityLocked(false /* andResume */,prev.preserveWindowOnDeferredRelaunch);
}
……
}
……
if (resumeNext) {
…….
mStackSupervisor.resumeFocusedStackTopActivityLocked(topStack, prev, null);//有两种不同的方式调用此方法,此处先不做关心
……
}
}
completePauseLocked方法顾名思义是用来完成对活动的pause操作和下个活动的resume操作。首先对mPausingActivity进行pause操作,将此活动的状态置为PAUSED,然后根据活动的finishing已经为true,则调用finishCurrentActivityLocked用来完成finish操作。然后,调用resumeFocusedStackTopActivityLocked函数来resume下一个activity。最后还是调用ensureActivitiesVisibleLocked校正所有Task中的Activity的状态。
3)finishCurrentActivityLocked(ActivityStack.java)方法实现
final ActivityRecord finishCurrentActivityLocked(ActivityRecord r, int mode, boolean oomAdj,String reason) {
final ActivityRecord next = mStackSupervisor.topRunningActivityLocked(
true /* considerKeyguardState */);
if (mode == FINISH_AFTER_VISIBLE && (r.visible || r.nowVisible)
&& next != null && !next.nowVisible) {
if (!mStackSupervisor.mStoppingActivities.contains(r)) {
addToStopping(r, false /* scheduleIdle */, false /* idleDelayed */);
}
r.setState(STOPPING, "finishCurrentActivityLocked");
if (oomAdj) {
mService.updateOomAdjLocked();
}
return r;
}
// make sure the record is cleaned out of other places.将此活动从个数据结构中移除
mStackSupervisor.mStoppingActivities.remove(r);
mStackSupervisor.mGoingToSleepActivities.remove(r);
mStackSupervisor.mActivitiesWaitingForVisibleActivity.remove(r);
final ActivityState prevState = r.getState();
……
if (mode == FINISH_IMMEDIATELY || (prevState == PAUSED && (mode == FINISH_AFTER_PAUSE || inPinnedWindowingMode()))
|| finishingActivityInNonFocusedStack || prevState == STOPPING || prevState == STOPPED
|| prevState == ActivityState.INITIALIZING) {
r.makeFinishingLocked();
boolean activityRemoved = destroyActivityLocked(r, true, "finish-imm:" + reason);
if (finishingActivityInNonFocusedStack) {
if(next != null){
mStackSupervisor.ensureVisibilityAndConfig(next, next.getDisplayId(),//mDisplayId,
false /* markFrozenIfConfigChanged */, true /* deferResume */);
}
}
if (activityRemoved) {
mStackSupervisor.resumeFocusedStackTopActivityLocked();
}
……
mStackSupervisor.resumeFocusedStackTopActivityLocked();
return r;
}
finishCurrentActivityLocked方法用来finish当前活动。首先要确保下一个要resumed的活动是否已经是可见的,如果不是,则需要推迟finishing当前活动。如果确认此时就要finish则调用destroyActivityLocked将当前活动remove掉;需要注意的是,如果Finnish的activity不在当前fouced的stack中,我们需要ensureVisibilityAndConfig方法来设置显示相关的参数。成功remove掉此活动后需要调用resumeFocusedStackTopActivityLocked来resume下一个活动。
4)destroyActivityLocked(ActivityStack.java)方法实现
cleanUpActivityLocked,此方法关键是调用removeActivityFromHistoryLocked方法来将activity从history中移除,有两种方式,一种是立即移除,一种是发送延时消息,在1s内移除。
5)removeActivityFromHistoryLocked方法实现
此方法用来将其从task中移除,设置acitivityrecord中的各个属性值,移除activity的windowcontainer等操作。需要注意的是如果最后一个活动的话,还需要将其所在的task也移除。
再贴代码的话这个就太长了,此处就先省略这两个方法实现的具体代码,如果后续再遇到其他问题,在详细了解。
本体出现问题的堆栈信息打印如下
java.lang.IllegalArgumentException: No display found with id: -1
at com.android.server.am.ActivityStackSupervisor.getDisplayOverrideConfiguration(ActivityStackSupervisor.java:470)
at com.android.server.am.ActivityStackSupervisor.ensureVisibilityAndConfig(ActivityStackSupervisor.java:1650)有改动
at com.android.server.am.ActivityStack.finishCurrentActivityLocked(ActivityStack.java:3877) //
at com.android.server.am.ActivityStack.completePauseLocked(ActivityStack.java:1568)
at com.android.server.am.ActivityStack.activityPausedLocked(ActivityStack.java:1534)
at com.android.server.am.ActivityStack$ActivityStackHandler.handleMessage(ActivityStack.java:398)
at android.os.Handler.dispatchMessage(Handler.java:106)
at android.os.Looper.loop(Looper.java:193)
at android.os.HandlerThread.run(HandlerThread.java:65)
at com.android.server.ServiceThread.run(ServiceThread.java:44)
No display found with id: -1此异常为系统主动抛出的异常,对应的代码为
Configuration getDisplayOverrideConfiguration(int displayId) {
final ActivityDisplay activityDisplay = getActivityDisplayOrCreateLocked(displayId);
if (activityDisplay == null) {
throw new IllegalArgumentException("No display found with id: " + displayId);
}
return activityDisplay.getOverrideConfiguration();
}
此函数用来查询参数displayId对应的Display相关参数信息,getActivityDisplayOrCreateLocked方法用来获取对应的ActivityDisplay实例。根据异常打印的信息可以看到此时的要查询的displayId的值为-1。在Display类中,定义为-1的值为INVALID_DISPLAY,即非法的DISPLAY,故此时通过getActivityDisplayOrCreateLocked函数无法查询到对应的ActivityDisplay实例,导致此异常的抛出。
那么此处我们需要找到将displayid置为-1的位置,在我们上述分析的pausetimeout流程中,调用finishCurrentActivityLocked函数来finish当前活动并resume下一个活动。而此异常出现的地方在ensureVisibilityAndConfig,在调用此方法之前我们先调用了destroyActivityLocked方法,具体代码如下
final ActivityRecord finishCurrentActivityLocked(ActivityRecord r, int mode, boolean oomAdj, String reason) {
……….
boolean activityRemoved = destroyActivityLocked(r, true, "finish-imm:" + reason);
if (finishingActivityInNonFocusedStack) {
// Finishing activity that was in paused state and it was in not currently focused
// stack, need to make something visible in its place.
mStackSupervisor.ensureVisibilityAndConfig(next, mDisplayId,
false /* markFrozenIfConfigChanged */, true /* deferResume */);
}
if (activityRemoved) {
mStackSupervisor.resumeFocusedStackTopActivityLocked();
}
……
}
在destroyActivityLocked方法中,通过removeActivityFromHistoryLocked-> removeTask-> remove-> removeFromDisplay等一系列调用,将displayid置为INVALID_DISPLAY,如下
private void removeFromDisplay() {
final ActivityDisplay display = getDisplay();
if (display != null) { display.removeChild(this);}
mDisplayId = INVALID_DISPLAY;
}
具体有以下log证明此流程是这样走的
09-18 05:57:38.779 912 929 W ActivityManager: Activity pause timeout for ActivityRecord{31811a3 u0 caller.id.phone.number.block/com.android.blue.DialtactsActivity t2261 f}
09-18 05:57:38.866 912 929 I am_destroy_activity: [0,51909027,2261,caller.id.phone.number.block/com.android.blue.DialtactsActivity,finish-imm:completedPausedLocked] //对应destroyActivityLocked方法的调用
09-1805:57:38.880 912 929 V WindowManager: Removing focused app token:AppWindowToken{27d6059 token=Token{db15ca0 ActivityRecord
{31811a3 u0 caller.id.phone.number.block/com.android.blue.DialtactsActivity t2261}}}
09-18 05:57:38.899 391 997 W SurfaceFlinger: Attempting to destroy on removed layer: AppWindowToken{27d6059 token=Token{db15ca0 ActivityRecord {31811a3 u0 caller.id.phone.number.block/com.android.blue.DialtactsActivity t2261}}}#0
09-18 05:57:38.903 912 929 E AndroidRuntime: *** FATAL EXCEPTION IN SYSTEM PROCESS: ActivityManager //此异常发生
相应的activity及其所在的task的流程
09-18 05:57:34.615 912 5435 I am_create_activity: [0,51909027,2261,caller.id.phone.number.block/com.android.blue.DialtactsActivity,android.intent.action.MAIN,NULL,NULL,270532608]
09-18 05:57:34.632 912 5435 I wm_task_moved: [2261,0,2147483647]
09-18 05:57:34.948 912 924 I am_restart_activity: [0,51909027,2261,caller.id.phone.number.block/com.android.blue.DialtactsActivity]
09-18 05:57:38.271 912 1648 I am_finish_activity: [0,51909027,2261,caller.id.phone.number.block/com.android.blue.DialtactsActivity,force-crash]
09-18 05:57:38.866 912 929 I am_destroy_activity: [0,51909027,2261,caller.id.phone.number.block/com.android.blue.DialtactsActivity,finish-imm:completedPausedLocked]
09-18 05:57:38.880 912 929 I wm_task_removed: [2261,removeAppToken: last token]
09-18 05:57:38.882 912 929 I am_remove_task: [2261,2252]
09-18 05:57:38.883 912 929 I wm_task_removed: [2261,removeTask]
如何修改此题
ensureVisibilityAndConfig方法的具体实现为
boolean ensureVisibilityAndConfig(ActivityRecord starting, int displayId,
boolean markFrozenIfConfigChanged, boolean deferResume) {
ensureActivitiesVisibleLocked(null /* starting */, 0 /* configChanges */,
false /* preserveWindows */, false /* notifyClients */);
final Configuration config = mWindowManager.updateOrientationFromAppTokens(
getDisplayOverrideConfiguration(displayId),
starting != null && starting.mayFreezeScreenLocked(starting.app)
? starting.appToken : null,
displayId, true /* forceUpdate */);
if (starting != null && markFrozenIfConfigChanged && config != null) {
starting.frozenBeforeDestroy = true;
}
// Update the configuration of the activities on the display.
return mService.updateDisplayOverrideConfigurationLocked(config, starting, deferResume,
displayId);
}
此方法为P上新增的方法,作用是通过ensureActivitiesVisibleLocked方法确保activity可见,调用updateDisplayOverrideConfigurationLocked来设置activity的显示相关参数。O上原先在此处只调用了ensureActivitiesVisibleLocked方法。实际上我们看问题发生时的调用
if (finishingActivityInNonFocusedStack) {//此stack已经不是当前的焦点所在了,DisplayID为INVALID_DISPLAY也是合理的
mStackSupervisor.ensureVisibilityAndConfig(next, mDisplayId,
false /* markFrozenIfConfigChanged */, true /* deferResume */);
}
从ensureActivitiesVisibleLocked方法的作用来看,此处传入的第二个参数displayId应该为第一个参数ActivityRecord所在的stack的displayId,即next所在的stack的参数,不应该为当前stack的,故此处考虑将其改为
if (finishingActivityInNonFocusedStack) {
if(next != null){
mStackSupervisor.ensureVisibilityAndConfig(next , next. getDisplayId(),
false /* markFrozenIfConfigChanged */, true /* deferResume */);
}
}
如果next为null这种情况出现也不要紧,因为紧跟在后面会调用mStackSupervisor.resumeFocusedStackTopActivityLocked();方法,此方法中会再次调用ensureActivitiesVisibleLocked方法。