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

本文深入探讨了Android中Activity启动过程中的关键概念,如ActivityRecord、TaskRecord和ActivityStack的作用及创建流程。分析了不同启动模式(standard、singleTop、singleTask、singleInstance)下Activity的启动行为,并详细解释了FLAG_ACTIVITY_NEW_TASK等启动标志的影响。

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是否重用分为两种情况主线进行

 

 

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值