Activity启动过程中ActivityStack TaskRecord ActivityRecord创建或设置

Activity在启动的过程中需要获得一个ActivityRecord实体,表示Activity在ActivityManagerService中的状态,
ActivityRecord创建与Activity的启动模式有关系:
Activity有四种启动模式:
standard:standard模式启动的Activity默认会进入启动它的Activity所属的任务栈TaskRecord
这种启动方式出现的问题:
1 从非Activity的Context启动Activity出现异常,因为非Activity组件没有任务栈的概念
new AndroidRuntimeException(
"Calling startActivity() from outside of an Activity "
+ " context requires the FLAG_ACTIVITY_NEW_TASK flag."
+ " Is this really what you want?");
2 从Launcher中启动Activity会加上FLAG_ACTIVITY_NEW_TASK,防止普通activity走人launcher所在的task。
public boolean startActivitySafely(View v, Intent intent, ItemInfo item) {
......
// Prepare intent
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
......
}

singleTop:和standard类似,只有一点优化,如果启动的activity正好在栈顶,那么不用重新创建Activity,省了onCreate调用,而是调用onNewIntent传递新的启动参数,然后执行onResume生命周期。

singleTask:singleTask相当于类似于单例模式,在启动过程中会先找到所需要的taskRecord,这个TaskRecord默认是包名,可以通过指定 android:taskAffinity= ""方式来定制。这种类型启动的Activity具有clear_top的功能。因此存在破坏返回栈的风险。

singleInstance:与singleTask类似,比singleTask加强的一点是,此种类型的Activity启动后会放置在单独的任务栈中。因此也会存在破坏返回栈的风险。另外需要说明的是,homeactivity就有点这个意味。

singleTask、singleInstance模式启动的Activity会自动加上FLAG_ACTIVITY_NEW_TASK,那么Activity启动默认就会跑到所属的task
具体原因可以查看ActivityStarter.computeLaunchingTaskFlags

ActivityRecord、TaskRecord创建与launchMode和启动flag相关,这两个创建是新建或者重用,当然也 可以指定启动的task。而ActivityStack在Android N之前只有两种homeStack(launcher和recents Activity)和其他,Android N开始有5种,增加了DockedStack(分屏Activity)、PinnedStack(画中画Activity)、freeformstack(自由模式Activity)。dockedStack、PinnedStack、freeformStack第一个Activity需要特殊进入方法,那么其他从这个Activity启动默认就是启动Activity所属的stack,当然也 可以指定启动的stack。指定task和stack通过Activityoptions来完成。 getLaunchTaskId 和getLaunchStack这就是定制的接口。

ActivityRecord、TaskRecord、ActivityStack查找和创建流程
1 ActivityRecord创建在ActivityStarter的startActivityLocked方法中创建,创建ActivityRecord意味着

2 创建完ActivityRecord之后,需要先找到所属的TaskRecord和ActivityStack,这个流程都在ActivityStarter.startActivityUnchecked中处理
按照步骤先找到TaskRecord
根据启动模式或者flag找到Task和Stack
task由launchMode、启动flag决定,具体来说就是
第一种情况:standard模式启动:走setTaskFromSourceRecord,直接复用当前activity的taskRecord、activityStack,

第二种情况:根据singleTask、singleInstance、flag_new_task, documentluanchMode设置
这种情况下是要根据activity的taskaffinity查找是不是有匹配的task,如果有匹配的task,还是走setTaskFromSourceRecord,不过

根据不同情况走三条道路
1 setTaskFromReuseOrCreateNewTask需要重新创建task
2 setTaskFromSourceRecord不需要创建TaskRecord
3 setTaskFromInTask ActivityStartInterceptor指定Task
4 setTaskToCurrentTopOrCreateNewTask兜底,按道理不会发生

Android N源码剖析
Android N将Activity启动的代码都集中到ActivityStarter中。启动Activity之前会执行查找合适的Activity和Task的操作。
/**
* Decide whether the new activity should be inserted into an existing task. Returns null
* if not or an ActivityRecord with the task into which the new activity should be added.
*/
private ActivityRecord getReusableIntentActivity() {
// We may want to try to place the new activity in to an existing task. We always
// do this if the target activity is singleTask or singleInstance; we will also do
// this if NEW_TASK has been requested, and there is not an additional qualifier telling
// us to still place it in a new task: multi task, always doc mode, or being asked to
// launch this as a new task behind the current one.
boolean putIntoExistingTask = ((mLaunchFlags & FLAG_ACTIVITY_NEW_TASK) != 0 &&
(mLaunchFlags & FLAG_ACTIVITY_MULTIPLE_TASK) == 0)
|| mLaunchSingleInstance || mLaunchSingleTask;
// FLAG_ACTIVITY_NEW_TASK和singleTask与singleInstance的情况
// If bring to front is requested, and no result is requested and we have not been given
// an explicit task to launch in to, and we can find a task that was started with this
// same component, then instead of launching bring that one to the front.
putIntoExistingTask &= mInTask == null && mStartActivity.resultTo == null;
ActivityRecord intentActivity = null;
if (mOptions != null && mOptions.getLaunchTaskId() != -1) {
final TaskRecord task = mSupervisor.anyTaskForIdLocked(mOptions.getLaunchTaskId());
intentActivity = task != null ? task.getTopActivity() : null;
} else if (putIntoExistingTask) {
if (mLaunchSingleInstance) {
// There can be one and only one instance of single instance activity in the
// history, and it is always in its own unique task, so we do a special search.
intentActivity = mSupervisor. findActivityLocked(mIntent, mStartActivity.info, false);
//查找方式特殊一点,只通过 component进行匹配
} else if ((mLaunchFlags & FLAG_ACTIVITY_LAUNCH_ADJACENT) != 0) {
// For the launch adjacent case we only want to put the activity in an existing
// task if the activity already exists in the history.
intentActivity = mSupervisor. findActivityLocked(mIntent, mStartActivity.info,
!mLaunchSingleTask);//singleTask此情况和singleInstance相同
//FLAG_ACTIVITY_LAUNCH_ADJACENT多窗口下使用的标记
} else {
// Otherwise find the best task to put the activity in.
intentActivity = mSupervisor. findTaskLocked(mStartActivity);
// 遍历所有的ActivityStack里面的TaskRecord,注意只和TaskRecord的toprunningActivity进行比较,比较的结果有可能是component匹配,也有可能taskAffinity匹配。
}
}
return intentActivity;
}
FLAG_ACTIVITY_NEW_TASK && !FLAG_ACTIVITY_MULTIPLE_TASK(documentLaunchMode= intoExisting)、SingleInstance、SingleTask,这几种情况下会检查是不是需要重用task,这里查找task得到的是task的首个activity,如果找到这个activity,说明可以重用这个activity的task。
首先activity启动可以通过ActivityOptions.setLaunchTaskId指定
然后通过ActivityStack.findActivityLocked找到与activity的intent匹配task,这里只和task的topactivity进行比较。

ActivityStarter启动Activity最核心的方法
private int startActivityUnchecked(final ActivityRecord r, ActivityRecord sourceRecord,
IVoiceInteractionSession voiceSession, IVoiceInteractor voiceInteractor,
int startFlags, boolean doResume, ActivityOptions options, TaskRecord inTask) {

setInitialState(r, options, inTask, doResume, startFlags, sourceRecord, voiceSession,
voiceInteractor);

computeLaunchingTaskFlags();

computeSourceStack();

mIntent.setFlags(mLaunchFlags);

mReusedActivity = getReusableIntentActivity(); //看是否存在重用activity
Slog.e(TAG, "zhanghao startActivityUnchecked mReusedActivity=" + mReusedActivity);

final int preferredLaunchStackId =
(mOptions != null) ? mOptions.getLaunchStackId() : INVALID_STACK_ID;

if (mReusedActivity != null) {
// When the flags NEW_TASK and CLEAR_TASK are set, then the task gets reused but
// still needs to be a lock task mode violation since the task gets cleared out and
// the device would otherwise leave the locked task.
if (mSupervisor.isLockTaskModeViolation(mReusedActivity.task,
(mLaunchFlags & (FLAG_ACTIVITY_NEW_TASK | FLAG_ACTIVITY_CLEAR_TASK))
== (FLAG_ACTIVITY_NEW_TASK | FLAG_ACTIVITY_CLEAR_TASK))) {
mSupervisor.showLockTaskToast();
Slog.e(TAG, "startActivityUnchecked: Attempt to violate Lock Task Mode");
return START_RETURN_LOCK_TASK_MODE_VIOLATION;
}

if (mStartActivity.task == null) {
mStartActivity.task = mReusedActivity.task; //针对找到activity,那么就找到task
}
if (mReusedActivity.task.intent == null) {
// This task was started because of movement of the activity based on affinity...
// Now that we are actually launching it, we can assign the base intent.
mReusedActivity.task.setIntent(mStartActivity);
}

// This code path leads to delivering a new intent, we want to make sure we schedule it
// as the first operation, in case the activity will be resumed as a result of later
// operations.
if ((mLaunchFlags & FLAG_ACTIVITY_CLEAR_TOP) != 0
|| mLaunchSingleInstance || mLaunchSingleTask) {
// In this situation we want to remove all activities from the task up to the one
// being started. In most cases this means we are resetting the task to its initial
// state.
final ActivityRecord top = mReusedActivity.task.performClearTaskForReuseLocked(
mStartActivity, mLaunchFlags); //有好几个地方执行这种操作,
if (top != null) {
if (top.frontOfTask) {
// Activity aliases may mean we use different intents for the top activity,
// so make sure the task now has the identity of the new intent.
top.task.setIntent(mStartActivity);
}
ActivityStack.logStartActivity(AM_NEW_INTENT, mStartActivity, top.task);
top.deliverNewIntentLocked(mCallingUid, mStartActivity.intent,
mStartActivity.launchedFromPackage);
}
}

sendPowerHintForLaunchStartIfNeeded(false /* forceSend */);

mReusedActivity = setTargetStackAndMoveToFrontIfNeeded(mReusedActivity);//执行目标stack、task移动
Slog.e(TAG, "zhanghao startActivityUnchecked mStartActivity = "+mStartActivity+ " \n mReusedActivity: " + mReusedActivity + " mStartFlags=" + mStartFlags+" mAddingToTask="+mAddingToTask+" mLaunchFlags: "+mLaunchFlags);

if ((mStartFlags & START_FLAG_ONLY_IF_NEEDED) != 0) {
// We don't need to start a new activity, and the client said not to do anything
// if that is the case, so this is it! And for paranoia, make sure we have
// correctly resumed the top activity.
resumeTargetStackIfNeeded();
return START_RETURN_INTENT_TO_CALLER;
}
setTaskFromIntentActivity(mReusedActivity);

if (!mAddingToTask && mReuseTask == null) {
// We didn't do anything... but it was needed (a.k.a., client don't use that
// intent!) And for paranoia, make sure we have correctly resumed the top activity.
resumeTargetStackIfNeeded();
return START_TASK_TO_FRONT; //没有添加
}
}

if (mStartActivity.packageName == null) {
if (mStartActivity.resultTo != null && mStartActivity.resultTo.task.stack != null) {
mStartActivity.resultTo.task.stack.sendActivityResultLocked(
-1, mStartActivity.resultTo, mStartActivity.resultWho,
mStartActivity.requestCode, RESULT_CANCELED, null);
}
ActivityOptions.abort(mOptions);
return START_CLASS_NOT_FOUND;
}

// If the activity being launched is the same as the one currently at the top, then
// we need to check if it should only be launched once.
final ActivityStack topStack = mSupervisor.mFocusedStack;
final ActivityRecord top = topStack.topRunningNonDelayedActivityLocked(mNotTop);
final boolean dontStart = top != null && mStartActivity.resultTo == null
&& top.realActivity.equals(mStartActivity.realActivity)
&& top.userId == mStartActivity.userId
&& top.app != null && top.app.thread != null
&& ((mLaunchFlags & FLAG_ACTIVITY_SINGLE_TOP) != 0
|| mLaunchSingleTop || mLaunchSingleTask);
if (dontStart) {
ActivityStack.logStartActivity(AM_NEW_INTENT, top, top.task);
// For paranoia, make sure we have correctly resumed the top activity.
topStack.mLastPausedActivity = null;
if (mDoResume) {
mSupervisor.resumeFocusedStackTopActivityLocked();
}
ActivityOptions.abort(mOptions);
if ((mStartFlags & START_FLAG_ONLY_IF_NEEDED) != 0) {
// We don't need to start a new activity, and the client said not to do
// anything if that is the case, so this is it!
return START_RETURN_INTENT_TO_CALLER;
}
top.deliverNewIntentLocked(
mCallingUid, mStartActivity.intent, mStartActivity.launchedFromPackage);

// Don't use mStartActivity.task to show the toast. We're not starting a new activity
// but reusing 'top'. Fields in mStartActivity may not be fully initialized.
mSupervisor.handleNonResizableTaskIfNeeded(
top.task, preferredLaunchStackId, topStack.mStackId);

return START_DELIVERED_TO_TOP;
}

boolean newTask = false;
final TaskRecord taskToAffiliate = (mLaunchTaskBehind && mSourceRecord != null)
? mSourceRecord.task : null;
Slog.e(TAG, "zhanghao startActivityUnchecked mStartActivity=" + mStartActivity+ " mStartActivity.resultTo: "+ mStartActivity.resultTo+" mInTask= "+ mInTask+ " mAddingToTask= "+ mAddingToTask);

// Should this be considered a new task?
if (mStartActivity.resultTo == null && mInTask == null && !mAddingToTask
&& (mLaunchFlags & FLAG_ACTIVITY_NEW_TASK) != 0) {
newTask = true;
setTaskFromReuseOrCreateNewTask(taskToAffiliate);

if (mSupervisor.isLockTaskModeViolation(mStartActivity.task)) {
Slog.e(TAG, "Attempted Lock Task Mode violation mStartActivity=" + mStartActivity);
return START_RETURN_LOCK_TASK_MODE_VIOLATION;
}
if (!mMovedOtherTask) {
// If stack id is specified in activity options, usually it means that activity is
// launched not from currently focused stack (e.g. from SysUI or from shell) - in
// that case we check the target stack.
updateTaskReturnToType(mStartActivity.task, mLaunchFlags,
preferredLaunchStackId != INVALID_STACK_ID ? mTargetStack : topStack);
}
} else if (mSourceRecord != null) {
if (mSupervisor.isLockTaskModeViolation(mSourceRecord.task)) {
Slog.e(TAG, "Attempted Lock Task Mode violation mStartActivity=" + mStartActivity);
return START_RETURN_LOCK_TASK_MODE_VIOLATION;
}

final int result = setTaskFromSourceRecord();
if (result != START_SUCCESS) {
return result;
}
} else if (mInTask != null) {
// The caller is asking that the new activity be started in an explicit
// task it has provided to us.
if (mSupervisor.isLockTaskModeViolation(mInTask)) {
Slog.e(TAG, "Attempted Lock Task Mode violation mStartActivity=" + mStartActivity);
return START_RETURN_LOCK_TASK_MODE_VIOLATION;
}

final int result = setTaskFromInTask();
if (result != START_SUCCESS) {
return result;
}
} else {
// This not being started from an existing activity, and not part of a new task...
// just put it in the top task, though these days this case should never happen.
setTaskToCurrentTopOrCreateNewTask();
}

mService.grantUriPermissionFromIntentLocked(mCallingUid, mStartActivity.packageName,
mIntent, mStartActivity.getUriPermissionsLocked(), mStartActivity.userId);

if (mSourceRecord != null && mSourceRecord.isRecentsActivity()) {
mStartActivity.task.setTaskToReturnTo(RECENTS_ACTIVITY_TYPE);
}
if (newTask) {
EventLog.writeEvent(
EventLogTags.AM_CREATE_TASK, mStartActivity.userId, mStartActivity.task.taskId);
}
ActivityStack.logStartActivity(
EventLogTags.AM_CREATE_ACTIVITY, mStartActivity, mStartActivity.task);
mTargetStack.mLastPausedActivity = null;

sendPowerHintForLaunchStartIfNeeded(false /* forceSend */);

mTargetStack.startActivityLocked(mStartActivity, newTask, mKeepCurTransition, mOptions);
if (mDoResume) {
if (!mLaunchTaskBehind) {
// TODO(b/26381750): Remove this code after verification that all the decision
// points above moved targetStack to the front which will also set the focus
// activity.
mService.setFocusedActivityLocked(mStartActivity, "startedActivity");
}
final ActivityRecord topTaskActivity = mStartActivity.task.topRunningActivityLocked();
if (!mTargetStack.isFocusable()
|| (topTaskActivity != null && topTaskActivity.mTaskOverlay
&& mStartActivity != topTaskActivity)) {
// If the activity is not focusable, we can't resume it, but still would like to
// make sure it becomes visible as it starts (this will also trigger entry
// animation). An example of this are PIP activities.
// Also, we don't want to resume activities in a task that currently has an overlay
// as the starting activity just needs to be in the visible paused state until the
// over is removed.
mTargetStack.ensureActivitiesVisibleLocked(null, 0, !PRESERVE_WINDOWS);
// Go ahead and tell window manager to execute app transition for this activity
// since the app transition will not be triggered through the resume channel.
mWindowManager.executeAppTransition();
} else {
mSupervisor.resumeFocusedStackTopActivityLocked(mTargetStack, mStartActivity,
mOptions);
}
} else {
mTargetStack.addRecentActivityLocked(mStartActivity);
}
mSupervisor.updateUserStackLocked(mStartActivity.userId, mTargetStack);

mSupervisor.handleNonResizableTaskIfNeeded(
mStartActivity.task, preferredLaunchStackId, mTargetStack.mStackId);

return START_SUCCESS;
}

//这个方法,前提是找到重用task,intentActivity不为null
private void setTaskFromIntentActivity(ActivityRecord intentActivity) {
Slog.e(TAG, "zhanghao setTaskFromIntentActivity intentActivity enter= "+intentActivity +" mAddingToTask="+mAddingToTask+" mLaunchFlags: "+mLaunchFlags);
if ((mLaunchFlags & (FLAG_ACTIVITY_NEW_TASK | FLAG_ACTIVITY_CLEAR_TASK))
== (FLAG_ACTIVITY_NEW_TASK | FLAG_ACTIVITY_CLEAR_TASK)) {
// The caller has requested to completely replace any existing task with its new
// activity. Well that should not be too hard...
mReuseTask = intentActivity.task;
mReuseTask.performClearTaskLocked();
mReuseTask.setIntent(mStartActivity);
// When we clear the task - focus will be adjusted, which will bring another task
// to top before we launch the activity we need. This will temporary swap their
// mTaskToReturnTo values and we don't want to overwrite them accidentally.
mMovedOtherTask = true;
Slog.e(TAG, "zhanghao setTaskFromIntentActivity intentActivity 1111 = "+intentActivity +" mAddingToTask="+mAddingToTask+" mLaunchFlags: "+mLaunchFlags);
} else if ((mLaunchFlags & FLAG_ACTIVITY_CLEAR_TOP) != 0
|| mLaunchSingleInstance || mLaunchSingleTask) {
ActivityRecord top = intentActivity.task.performClearTaskLocked(mStartActivity,
mLaunchFlags);
if (top == null) { // 执行清理后,说明这个task中没有activity实例,找到组织但是没找到实例
// A special case: we need to start the activity because it is not currently
// running, and the caller has asked to clear the current task to have this
// activity at the top.
mAddingToTask = true;
Slog.d(TAG_TASKS, "zhanghao setTaskFromIntentActivity: top is null");
// Now pretend like this activity is being started by the top of its task, so it
// is put in the right place.
mSourceRecord = intentActivity;
final TaskRecord task = mSourceRecord.task;
if (task != null && task.stack == null) {
// Target stack got cleared when we all activities were removed above.
// Go ahead and reset it.
mTargetStack = computeStackFocus(mSourceRecord, false /* newTask */,
null /* bounds */, mLaunchFlags, mOptions); // 计算得到所属的activityStack,新建ActivityStack
mTargetStack.addTask(task,
!mLaunchTaskBehind /* toTop */, "startActivityUnchecked");
}
}
Slog.e(TAG, "zhanghao setTaskFromIntentActivity intentActivity 2222 = "+intentActivity +" mAddingToTask="+mAddingToTask+" mLaunchFlags: "+mLaunchFlags);
} else if (mStartActivity.realActivity.equals(intentActivity.task.realActivity)) {
// In this case the top activity on the task is the same as the one being launched,
// so we take that as a request to bring the task to the foreground. If the top
// activity in the task is the root activity, deliver this new intent to it if it
// desires.
if (((mLaunchFlags & FLAG_ACTIVITY_SINGLE_TOP) != 0 || mLaunchSingleTop)
&& intentActivity.realActivity.equals(mStartActivity.realActivity)) {
ActivityStack.logStartActivity(AM_NEW_INTENT, mStartActivity,
intentActivity.task);
if (intentActivity.frontOfTask) {
intentActivity.task.setIntent(mStartActivity);
}
intentActivity.deliverNewIntentLocked(mCallingUid, mStartActivity.intent,
mStartActivity.launchedFromPackage);
} else if ( !intentActivity.task.isSameIntentFilter(mStartActivity)) {
// In this case we are launching the root activity of the task, but with a
// different intent. We should start a new instance on top.
mAddingToTask = true;
mSourceRecord = intentActivity; //关键home键回来intent会一样,大部分情况下走这条线,rootactivity一般是桌面启动的,后续启动的activity intentfilter一般不同。
}

Slog.e(TAG, "zhanghao setTaskFromIntentActivity intentActivity 3333 = "+intentActivity +" mAddingToTask="+mAddingToTask+" mLaunchFlags: "+mLaunchFlags);
} else if ((mLaunchFlags & FLAG_ACTIVITY_RESET_TASK_IF_NEEDED) == 0) {
// In this case an activity is being launched in to an existing task, without
// resetting that task. This is typically the situation of launching an activity
// from a notification or shortcut. We want to place the new activity on top of the
// current task.
mAddingToTask = true;
mSourceRecord = intentActivity; //home键回来这个标志是1,也不满足, 适合notification和shortcut启动方式,mSourceRecord 被改写,后续就使用mSourceRecord的task
Slog.e(TAG, "zhanghao setTaskFromIntentActivity intentActivity 4444 = "+intentActivity +" mAddingToTask="+mAddingToTask+" mLaunchFlags: "+mLaunchFlags);
} else if (!intentActivity.task.rootWasReset) {
// In this case we are launching into an existing task that has not yet been started
// from its front door. The current task has been brought to the front. Ideally,
// we'd probably like to place this new task at the bottom of its stack, but that's
// a little hard to do with the current organization of the code so for now we'll
// just drop it.
intentActivity.task.setIntent(mStartActivity);
Slog.e(TAG, "zhanghao setTaskFromIntentActivity intentActivity 5555 = "+intentActivity +" mAddingToTask="+mAddingToTask+" mLaunchFlags: "+mLaunchFlags);
}
Slog.e(TAG, "zhanghao setTaskFromIntentActivity intentActivity exit = "+intentActivity +" mAddingToTask="+mAddingToTask+" mLaunchFlags: "+mLaunchFlags);
// home键回来mAddingToTask为false
}

所以activity在启动的过程中根据Activity是否重用分为两种情况主线进行


猜你喜欢

转载自blog.csdn.net/xuning2516/article/details/79942954