AMS源码分析--startActivities

1,856 阅读5分钟

前言

之前做开放能力优化的时候,想到了Activity#startActivities方法,虽然这个方法平时开发中使用不多,但是深度跳转需求时非常有用。

但是还是有一个问题,由于我们项目工程使用的是组件化框架,不能直接new两个Intent对象。咋办?试了下连续执行两次startActivity,发现效果是一样的:》

翻看google开发文档,两个连续startActivity能实现这个效果是有依据的。google对这个方法的介绍如下:

(startActivities会同时启动多个Activity,通常和连续使用startActivity效果相同。注意:只有Intent数组中最好一个Activity才会得到创建,等用户点击返回时,前一个Activity才进行创建。)

让我困惑的是下面两个问题: 问题1.startActivities为什么和多个startActivity效果相同 问题2.startActivities是怎么做到只最后一个Activity可见,之前Activity只是压入栈中

问题1.startActivities为什么和多个startActivity效果相同

startActivities调用流程如下

1.Activity#startActivities
2.Instrumentation#execStartActivities
3.Instrumentation#execStartActivitiesAsUser
4.ActivityManager#startActivities
5.ActivityManagerService#startActivities
6.ActivityStarter#startActivities
...

看下ActivityStarter#startActivities的源码

final int startActivities(IApplicationThread caller, int callingUid, String callingPackage,
            Intent[] intents, String[] resolvedTypes, IBinder resultTo,
            Bundle bOptions, int userId, String reason) {
    // 省略部分代码。。。
    ActivityRecord[] outActivity = new ActivityRecord[1];
    for (int i=0; i<intents.length; i++) {
        Intent intent = intents[i];
        if (intent == null) {
            continue;
        }

        // 省略部分代码。。
        int res = startActivityLocked(caller, intent, null /*ephemeralIntent*/,
                resolvedTypes[i], aInfo, null /*rInfo*/, null, null, resultTo, null, -1,
                callingPid, callingUid, callingPackage,
                realCallingPid, realCallingUid, 0,
                options, false, componentSpecified, outActivity, null, null, reason);
        if (res < 0) {
            return res;
        }

        resultTo = outActivity[0] != null ? outActivity[0].appToken : null;
    }

}

可以看出startActivities方法最终是通过for循环调用startActivityLocked方法实现的。而startActivity也会调用startActivityLocked方法,这也解释了这个问题。

问题2.startActivities是怎么做到只最后一个Activity可见,其他的Activity只是压入栈中

Activity启动流程也看过很多文章,别人分析的流程大致是这样:

1.Activity#startActivity
2.Activity#startActivityForResult
3.Instrumentation#execStartActivity
4.ActivityManager#startActivity
5.ActivityManagerService#startActivity
6.ActivityManagerService#startActivityAsUser
7.ActivityStarter#startActivityMayWait
8.ActivityStarter#startActivityLocked
9.ActivityStarter#startActivity
10.ActivityStarter#startActivityUnchecked
11.ActivityStackSupervisor#resumeFocusedStackTopActivityLocked
12.ActivityStack#resumeTopActivityUncheckedLocked
13.ActivityStack#resumeTopActivityInnerLocked
14.ActivityStack#startPausingLocked()
15.ActivityStackSupervisor#startSpecificActivityLocked()
16.ActivityStackSupervisor#realStartActivityLocked
17.app.thread.scheduleLaunchActivity

这个流程有一个问题,比如说:执行AActivity的app.thread.scheduleLaunchActivity方法后,AActivity的scheduleLaunchActivity消息会放在MessageQueue中等待执行。执行BActivity的startActivity的过程中必须要取消AActivity的scheduleLaunchActivity消息,不然AActivity肯定会在BActivity之前创建。带着这个思路反复看源码,发现并没有任何地方取消之前scheduleLaunchActivity消息。。。

答案是这个流程有两个关键点遗漏了:

14.ActivityStack#startPausingLocked()
16.ActivityStackSupervisor.realStartActivityLocked

ActivityStack#startPausingLocked()方法

final boolean startPausingLocked(boolean userLeaving, boolean uiSleeping,
            ActivityRecord resuming, boolean pauseImmediately) {
        // 省略部分代码。。。
        mResumedActivity = null;
        mPausingActivity = prev;
        mLastPausedActivity = prev;
        mLastNoHistoryActivity = (prev.intent.getFlags() & Intent.FLAG_ACTIVITY_NO_HISTORY) != 0
                || (prev.info.flags & ActivityInfo.FLAG_NO_HISTORY) != 0 ? prev : null;
        // 重点是这个,将前一个ActivityRecord的state设置为PAUSING
        prev.state = ActivityState.PAUSING;
        prev.getTask().touchActiveTime();
        clearLaunchTime(prev);
        final ActivityRecord next = mStackSupervisor.topRunningActivityLocked();
        //省略部分代码

        if (prev.app != null && prev.app.thread != null) {
            if (DEBUG_PAUSE) Slog.v(TAG_PAUSE, "Enqueueing pending pause: " + prev);
            try {
                EventLog.writeEvent(EventLogTags.AM_PAUSE_ACTIVITY,
                        prev.userId, System.identityHashCode(prev),
                        prev.shortComponentName);
                mService.updateUsageStats(prev, false);
                // 暂停流程
                prev.app.thread.schedulePauseActivity(prev.appToken, 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;
        }
        //省略部分代码
    }

这个方法做一件事:设置state为PAUSING ActivityStackSupervisor#realStartActivityLocked方法

final boolean realStartActivityLocked(ActivityRecord r, ProcessRecord app,
            boolean andResume, boolean checkConfig) throws RemoteException {

        if (!allPausedActivitiesComplete()) {
            // While there are activities pausing we skipping starting any new activities until
            // pauses are complete. NOTE: that we also do this for activities that are starting in
            // the paused state because they will first be resumed then paused on the client side.
            if (DEBUG_SWITCH || DEBUG_PAUSE || DEBUG_STATES) Slog.v(TAG_PAUSE,
                    "realStartActivityLocked: Skipping start of r=" + r
                    + " some activities pausing...");
            return false;
        }
        //省略部分代码
        
}

 boolean allPausedActivitiesComplete() {
        boolean pausing = true;
        for (int displayNdx = mActivityDisplays.size() - 1; displayNdx >= 0; --displayNdx) {
            ArrayList<ActivityStack> stacks = mActivityDisplays.valueAt(displayNdx).mStacks;
            for (int stackNdx = stacks.size() - 1; stackNdx >= 0; --stackNdx) {
                final ActivityStack stack = stacks.get(stackNdx);
                final ActivityRecord r = stack.mPausingActivity;
                // 因为前一个Activity的state是ActivityState.PAUSING
                if (r != null && r.state != PAUSED && r.state != STOPPED && r.state != STOPPING) {
                    if (DEBUG_STATES) {
                        Slog.d(TAG_STATES,
                                "allPausedActivitiesComplete: r=" + r + " state=" + r.state);
                        pausing = false;
                    } else {
                        return false;
                    }
                }
            }
        }
        return pausing;
    }

可以看出,realStartActivityLocked在 if(!allPausedActivitiesComplete()) 时已经终止了。也就是说不会立刻发送scheduleLaunchActivity消息,那么什么时候会执行scheduleLaunchActivity方法呢?继续往下看

ActivityThread#handlePauseActivity方法

private void handlePauseActivity(IBinder token, boolean finished,
            boolean userLeaving, int configChanges, boolean dontReport, int seq) {
        ActivityClientRecord r = mActivities.get(token);
        if (DEBUG_ORDER) Slog.d(TAG, "handlePauseActivity " + r + ", seq: " + seq);
        if (!checkAndUpdateLifecycleSeq(seq, r, "pauseActivity")) {
            return;
        }
        if (r != null) {
            //Slog.v(TAG, "userLeaving=" + userLeaving + " handling pause of " + r);
            if (userLeaving) {
                performUserLeavingActivity(r);
            }

            r.activity.mConfigChangeFlags |= configChanges;
            performPauseActivity(token, finished, r.isPreHoneycomb(), "handlePauseActivity");

            // Make sure any pending writes are now committed.
            if (r.isPreHoneycomb()) {
                QueuedWork.waitToFinish();
            }

            // Tell the activity manager we have paused.
            if (!dontReport) {
                try {
                    // 重点是这个方法 ActivityManager.getService().activityPaused(token);
                } catch (RemoteException ex) {
                    throw ex.rethrowFromSystemServer();
                }
            }
            mSomeActivitiesChanged = true;
        }
    }

ActivityManager.getService().activityPaused(token)很关键。其最终会调用到ActivityManagerService的activityPaused方法中,代码如下

    @Override
    public final void activityPaused(IBinder token) {
        final long origId = Binder.clearCallingIdentity();
        synchronized(this) {
            ActivityStack stack = ActivityRecord.getStackLocked(token);
            if (stack != null) {
                stack.activityPausedLocked(token, false);
            }
        }
        Binder.restoreCallingIdentity(origId);
    }

可见又交给了ActivityStack来处理,看下ActivityStack的activityPausedLocked方法

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) {
            // 移除超时消息
            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 {
                EventLog.writeEvent(EventLogTags.AM_FAILED_TO_PAUSE,
                        r.userId, System.identityHashCode(r), r.shortComponentName,
                        mPausingActivity != null
                            ? mPausingActivity.shortComponentName : "(none)");
                if (r.state == ActivityState.PAUSING) {
                    r.state = ActivityState.PAUSED;
                    if (r.finishing) {
                        if (DEBUG_PAUSE) Slog.v(TAG,
                                "Executing finish of failed to pause activity: " + r);
                        finishCurrentActivityLocked(r, FINISH_AFTER_VISIBLE, false);
                    }
                }
            }
        }
        mStackSupervisor.ensureActivitiesVisibleLocked(null, 0, !PRESERVE_WINDOWS);
}

private void completePauseLocked(boolean resumeNext, ActivityRecord resuming) {
        ActivityRecord prev = mPausingActivity;
        if (DEBUG_PAUSE) Slog.v(TAG_PAUSE, "Complete pause: " + prev);

        if (prev != null) {
            final boolean wasStopping = prev.state == STOPPING;
            // 终于在这里给设置上了PAUSED状态
            prev.state = ActivityState.PAUSED;
            if (prev.finishing) {
                if (DEBUG_PAUSE) Slog.v(TAG_PAUSE, "Executing finish of activity: " + prev);
                prev = finishCurrentActivityLocked(prev, FINISH_AFTER_VISIBLE, false);
            } else if (prev.app != null) {
                if (DEBUG_PAUSE) Slog.v(TAG_PAUSE, "Enqueue pending stop if needed: " + prev
                        + " wasStopping=" + wasStopping + " visible=" + prev.visible);
                if (mStackSupervisor.mActivitiesWaitingForVisibleActivity.remove(prev)) {
                    if (DEBUG_SWITCH || DEBUG_PAUSE) Slog.v(TAG_PAUSE,
                            "Complete pause, no longer waiting: " + prev);
                }
                if (prev.deferRelaunchUntilPaused) {
                    // Complete the deferred relaunch that was waiting for pause to complete.
                    if (DEBUG_PAUSE) Slog.v(TAG_PAUSE, "Re-launching after pause: " + prev);
                    prev.relaunchActivityLocked(false /* andResume */,
                            prev.preserveWindowOnDeferredRelaunch);
                } else if (wasStopping) {
                    // We are also stopping, the stop request must have gone soon after the pause.
                    // We can't clobber it, because the stop confirmation will not be handled.
                    // We don't need to schedule another stop, we only need to let it happen.
                    prev.state = STOPPING;
                } else if ((!prev.visible && !hasVisibleBehindActivity())
                        || mService.isSleepingOrShuttingDownLocked()) {
                    // Clear out any deferred client hide we might currently have.
                    prev.setDeferHidingClient(false);
                    // If we were visible then resumeTopActivities will release resources before
                    // stopping.
                    addToStopping(prev, true /* scheduleIdle */, false /* idleDelayed */);
                }
            } else {
                if (DEBUG_PAUSE) Slog.v(TAG_PAUSE, "App died during pause, not stopping: " + prev);
                prev = null;
            }
            // It is possible the activity was freezing the screen before it was paused.
            // In that case go ahead and remove the freeze this activity has on the screen
            // since it is no longer visible.
            if (prev != null) {
                prev.stopFreezingScreenLocked(true /*force*/);
            }
            mPausingActivity = null;
        }

        if (resumeNext) {
            final ActivityStack topStack = mStackSupervisor.getFocusedStack();
            // 在这里调用了resumeFocusedStackTopActivityLocked,保证realStartActivityLocked再次得到执行
            if (!mService.isSleepingOrShuttingDownLocked()) {
                mStackSupervisor.resumeFocusedStackTopActivityLocked(topStack, prev, null);
            } else {
                mStackSupervisor.checkReadyForSleepLocked();
                ActivityRecord top = topStack.topRunningActivityLocked();
                if (top == null || (prev != null && top != prev)) {
                    // If there are no more activities available to run, do resume anyway to start
                    // something. Also if the top activity on the stack is not the just paused
                    // activity, we need to go ahead and resume it to ensure we complete an
                    // in-flight app switch.
                    mStackSupervisor.resumeFocusedStackTopActivityLocked();
                }
            }
        }

        if (prev != null) {
            prev.resumeKeyDispatchingLocked();

            if (prev.app != null && prev.cpuTimeAtResume > 0
                    && mService.mBatteryStatsService.isOnBattery()) {
                long diff = mService.mProcessCpuTracker.getCpuTimeForPid(prev.app.pid)
                        - prev.cpuTimeAtResume;
                if (diff > 0) {
                    BatteryStatsImpl bsi = mService.mBatteryStatsService.getActiveStatistics();
                    synchronized (bsi) {
                        BatteryStatsImpl.Uid.Proc ps =
                                bsi.getProcessStatsLocked(prev.info.applicationInfo.uid,
                                        prev.info.packageName);
                        if (ps != null) {
                            ps.addForegroundTimeLocked(diff);
                        }
                    }
                }
            }
            prev.cpuTimeAtResume = 0; // reset it
        }

        // Notify when the task stack has changed, but only if visibilities changed (not just
        // focus). Also if there is an active pinned stack - we always want to notify it about
        // task stack changes, because its positioning may depend on it.
        if (mStackSupervisor.mAppVisibilitiesChangedSinceLastPause
                || mService.mStackSupervisor.getStack(PINNED_STACK_ID) != null) {
            mService.mTaskChangeNotificationController.notifyTaskStackChanged();
            mStackSupervisor.mAppVisibilitiesChangedSinceLastPause = false;
        }

        mStackSupervisor.ensureActivitiesVisibleLocked(resuming, 0, !PRESERVE_WINDOWS);
    }

到这里,这个问题也能解释了,主要就是pause的延迟