pausetimeout问题流程分析及解决方案

这次出现问题的堆栈是处理activity的pausetimeout的堆栈,在分析问题之前我们先了解下pausetimeout。下其大致流程如下(Android P代码)

  1. pausetimeout触发机制

在我们需要对当前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方法中移除。

  1. pausetimeout流程

在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也移除。

   再贴代码的话这个就太长了,此处就先省略这两个方法实现的具体代码,如果后续再遇到其他问题,在详细了解。

  1. 问题分析及解决思路

本体出现问题的堆栈信息打印如下

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方法。

猜你喜欢

转载自blog.csdn.net/chi_wy/article/details/83104628