前言
之前做开放能力优化的时候,想到了Activity#startActivities方法,虽然这个方法平时开发中使用不多,但是深度跳转需求时非常有用。
但是还是有一个问题,由于我们项目工程使用的是组件化框架,不能直接new两个Intent对象。咋办?试了下连续执行两次startActivity,发现效果是一样的:》
翻看google开发文档,两个连续startActivity能实现这个效果是有依据的。google对这个方法的介绍如下:

让我困惑的是下面两个问题: 问题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的延迟