ActivityManagerService解读之Activity启动初探一文以启动Log为切入点重点偏于流程分析,概要的介绍了AndroidActivity启动。本文将将从Activity启动模式选则,设置不同Intent Flag以及Activity在AndroidManifest文件中不同的设置对Activity Task影响为点,来再次分析Activity启动。本文将更多的涉及Android系统在Activity启动时对Task任务的管理。其中我们必须要了解的一些基础知识便是Activity的启动模式,启动Activity时通过Intent设置的Flag,以及Activity在AndroidManifest文件中可以配置的一些属性。
Activity启动模式
standard:标准模式,是Android的默认启动模式。该模式下每次启动Activity都会新建Activity实例
singleTop:栈顶模式,该模式下,如果启动的Activity位于Task顶部,则不需要重新创建实例,运行生命周期onPause- >onNewIntent->onResume
singleTask:栈模式,该模式下启动Activity,如果Task中存在该Activity,则会清除位于该Activity之上的所有Activity实例,并运行该Activity,运行生命周期onNewIntent->onRestart->onStart->onResume
singleInstance:栈内单例模式,该模式下启动Activity会单独为Activity启动一个新的Task,如果对应Task存在Activity,则会类似singleTask执行生命周期方法,只是系统不会将任何其他 Activity 启动到包含实例的任务中。该 Activity 始终是其Task唯一仅有的成员;由此 Activity 启动的任何 Activity 均在单独的任务中打开
Intent Flag
Flag Name | 解释 |
FLAG_ACTIVITY_BROUGHT_TO_FRONT | 该flag一般由系统自动添加,当Activity的启动模式为singleTask的时候,再次启动该Activity的时候系统会添加该flag |
FLAG_ACTIVITY_CLEAR_TASK | 设置该flag的时候会清除Task中的Activity,因此启动的Activity便是新的Root Activity |
FLAG_ACTIVITY_CLEAR_TOP | 该flag的行为和启动模式singleTask大体上相似,不同的是当Activity的启动模式是standard并且没有设置FLAG_ACTIVITY_SINGLE_TOP的时候,使用该flag的时候不会调用onNewIntent方法,而是finish掉之前的该Activity实例,重新创建新的该Activity实例 |
FLAG_ACTIVITY_CLEAR_WHEN_TASK_RESET | Deprecated |
FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS | 当设置该flag的时候,启动的Activity将不会在SystemUI Recents中显示 |
FLAG_ACTIVITY_FORWARD_RESULT | 当使用startActivityForResult方式启动Activity时,需要重新将result结果传递时使用该Flag |
FLAG_ACTIVITY_LAUNCH_ADJACENT | 该flag用于分屏模式或者多窗口模式中 |
FLAG_ACTIVITY_LAUNCHED_FROM_HISTORY | 该flag通常由系统设置 |
FLAG_ACTIVITY_MULTIPLE_TASK | |
FLAG_ACTIVITY_NEW_DOCUMENT | |
FLAG_ACTIVITY_NEW_TASK | 使用该flag会重新创建一个新的Activity,但是有种情况,比方说栈中有A,B,C三个Activity,此时在C中启动D的话,如果在Manifest.xml文件中给D添加了Affinity的值和Task中的不一样的话,则会在新标记的Affinity所存在的Task中压入这个Activity。如果是默认的或者指定的Affinity和Task一样的话,就和standard模式一样了启动一个新的Activity |
FLAG_ACTIVITY_NO_ANIMATION | 设置该flag的时候启动Activity时无动画 |
FLAG_ACTIVITY_NO_HISTORY | 设置该flag的Activity不会保存在Task中,后台即finish |
FLAG_ACTIVITY_NO_USER_ACTION | |
FLAG_ACTIVITY_PREVIOUS_IS_TOP | |
FLAG_ACTIVITY_REORDER_TO_FRONT | 设置该flag的时候,当启动的Activity已经存在,则会将此Activity从Task历史中移到Task的顶部,假设Task中存在A B C三个Activity此时C想启动A并且设置了该flag,此时Task中的Activity排序应为B C A同时Activity不会创建新的实例而是执行onNewIntent方法类似singleTask方式。但是此flag会忽略FLAG_ACTIVITY_CLEAR_TOP flag, |
FLAG_ACTIVITY_RESET_TASK_IF_NEEDED | 设置该flag的时候,启动的Activity要么新创建Task要么使用已经创建的Task,但是启动的Activity会作为Task的第一个Activity,这将会导致之前存在的Task执行clear操作 |
FLAG_ACTIVITY_RETAIN_IN_RECENTS | 设置该flag的时候,当Activity finish时仍然可以在Recents中显示该Activity |
FLAG_ACTIVITY_SINGLE_TOP | 与singleTop启动模式具有相同的行为 |
FLAG_ACTIVITY_TASK_ON_HOME | 该flag只能与FLAG_ACTIVITY_NEW_TASK一起使用,使用的效果是每次按返回键退出都会退到Home界面 |
AndroidManifest文件中Activity相关属性设置
属性 | 值 | 解释 |
android:allowTaskReparenting | TRUE | 当启动 Activity 的任务接下来转至前台时,Activity 是否能从该任务转移至与其有亲和关系的任务,true表示它可以转移,false表示它仍须留在启动它的任务处
|
FALSE | ||
android:alwaysRetainTaskState | TRUE | 该属性决定系统是否保持Activity所在Task的状态,true表示保持,false表示允许在特定情况下系统对起Task进行重置到初始状态。 默认值是false,该属性只对Task的根Activity有意义,对于其他Activity可以忽略该属性。 正常情况下,当用户从主屏幕重新选择某个任务时,系统会在特定情况下清除该任务(从根 Activity 之上的堆栈中移除所有 Activity)。 系统通常会在用户一段时间(如 30 分钟)内未访问任务时执行此操作。 不过,如果该属性的值是true,则无论用户如何到达任务,将始终返回到最后状态的任务。 例如,在网络浏览器这类存在大量用户不愿失去的状态(如多个打开的标签)的应用中,该属性会很有用。
|
FALSE | ||
android:autoRemoveFromRecents | TRUE | 由具有该属性的 Activity 启动的任务是否一直保留在概览屏幕中,直至任务中的最后一个 Activity 完成为止。 若为 true,则自动从概览屏幕中移除任务。 它会替换调用方使用的 FLAG_ACTIVITY_RETAIN_IN_RECENTS |
FALSE | ||
android:clearTaskOnLaunch | TRUE | 是否每当从主屏幕重新启动任务时都从中移除根 Activity 之外的所有 Activity 。true表示始终将任务清除到只剩其根 Activity,false表示不做清除。 默认值为false。该属性只对启动新任务的 Activity(根 Activity)有意义,对于任务中的所有其他 Activity,均忽略该属性。 当值为true时,每次用户再次启动任务时,无论用户最后在任务中正在执行哪个 Activity,也无论用户是使用返回还是主屏幕按钮离开,都会将用户转至任务的根 Activity。当值为false时,可在某些情况下清除任务中的 Activity(请参阅 alwaysRetainTaskState 属性),但并非一律可以。 |
FALSE | ||
android:documentLaunchMode | intoExisting | |
always | ||
none | ||
never | ||
android:excludeFromRecents | TRUE | 是否应将该Activity启动的任务排除在最近使用的应用列表(即概览屏幕)之外。也就是说,当该Activity是新任务的根Activity时,此属性确定任务是否应出现在最近使用的应用列表中。如果应将任务排除在列表之外,请设置true,如果应将其包括在内,则设置false。默认值为false。 |
FALSE | ||
android:finishOnTaskLaunch | TRUE | 该属性决定当系统再次启动现有的Task的时候,是否要关闭现有的Activity,true表示需要关闭,false是默认值,该属性如果与allowTaskReparenting 均为true的时候,优先使用该属性。 |
FALSE | ||
android:launchMode | standard | 参考启动模式中介绍 |
singleTop | ||
singleTask | ||
singleInstance | ||
android:noHistory | TRUE | 当Activity设置该属性为true的时候,只要该Activity不可以见的时候,便会finish掉(系统灭屏方式除外) |
FALSE | ||
android:relinquishTaskIdentity | TRUE | |
FALSE | ||
android:taskAffinity | String | 表示不同Activity之间的亲和关系。从概念上讲,具有相同亲和关系的 Activity 归属同一Task(从用户的角度来看,则是归属同一“应用”)。 Task的亲和关系由其根 Activity 的亲和关系确定。 亲和关系确定两件事 - Activity 更改到的父项任务(请参阅 allowTaskReparenting 属性)和通过 FLAG_ACTIVITY_NEW_TASK 标志启动 Activity 时将用来容纳它的Task
默认情况下同一个应用的Activity都具有相同的亲和关系,当然我们可以通过修改此属性来改变他们之间的亲和关系,如果没有设置该属性,系统会默认赋予值为应用的包名。 |
Android系统启动Activity是如何分别处理这些属性和Flag的?
此段代码来自ActivityStarter
/** DO NOT call this method directly. Use {@link #startActivityLocked} instead. */
private int startActivity(IApplicationThread caller, Intent intent, Intent ephemeralIntent,
String resolvedType, ActivityInfo aInfo, ResolveInfo rInfo,
IVoiceInteractionSession voiceSession, IVoiceInteractor voiceInteractor,
IBinder resultTo, String resultWho, int requestCode, int callingPid, int callingUid,
String callingPackage, int realCallingPid, int realCallingUid, int startFlags,
ActivityOptions options, boolean ignoreTargetSecurity, boolean componentSpecified,
ActivityRecord[] outActivity, TaskRecord inTask) {
//此处略去部分代码
if ((launchFlags & Intent.FLAG_ACTIVITY_FORWARD_RESULT) != 0 && sourceRecord != null) {
//一段美妙的逻辑,很有意思,借助requestCode来解决冲突,规定使用FLAG_ACTIVITY_FORWARD_RESULT flag时,
//比如A---startActivityForResult--requestCode>=0--->B---startActivityForResult--+FLAG_ACTIVITY_FORWARD_RESULT--requestCode<0--->C
//B的参数requestCode必须<=0,此时C setResult的结果返回给A,这就是FLAG_ACTIVITY_FORWARD_RESULT的用法
if (requestCode >= 0) {
ActivityOptions.abort(options);
return ActivityManager.START_FORWARD_AND_REQUEST_CONFLICT;
}
resultRecord = sourceRecord.resultTo;
if (resultRecord != null && !resultRecord.isInStackLocked()) {
resultRecord = null;
}
resultWho = sourceRecord.resultWho;
requestCode = sourceRecord.requestCode;
sourceRecord.resultTo = null;
if (resultRecord != null) {
resultRecord.removeResultsLocked(sourceRecord, resultWho, requestCode);
}
if (sourceRecord.launchedFromUid == callingUid) {
callingPackage = sourceRecord.launchedFromPackage;
}
}
}
Activity启动过程中,最先处理FLAG_ACTIVITY_FORWARD_RESULT Flag,因为每次启动的Activity的时候系统都会为启动的动作创建一个ActivityRecord,而创建的时候就应该指定该Activity的resultTo成员,当启动方式不为startActivityForResult的时候则为null。因此创建ActivityRecord的时候就应该先指定好对应的成员。从代码的解释中我们可以知道FLAG_ACTIVITY_FORWARD_RESULT的具体用处在与A -> B -> C,C setResult的结果传递给A。
此段代码来自ActivityStarter
// Note: This method should only be called from {@link startActivity}.
private int startActivityUnchecked(final ActivityRecord r, ActivityRecord sourceRecord,
IVoiceInteractionSession voiceSession, IVoiceInteractor voiceInteractor,
int startFlags, boolean doResume, ActivityOptions options, TaskRecord inTask,
ActivityRecord[] outActivity) {
setInitialState(r, options, inTask, doResume, startFlags, sourceRecord, voiceSession,
voiceInteractor);//设置ActivityStarter的一些基本属性
//根据Activity的启动模式和Intent中设置的flag计算出本次启动需要的具体flag,并保存在mLaunchFlags中
computeLaunchingTaskFlags();
//一般情况下sourceRecord都为启动的Activity,因此这里获得的mSourceStack一般都是启动Activity的Stack
//值得注意的时候如果sourceRecord已经finish了,那么此时需要保存之前Task intent信息到mNewTaskInfo和mNewTaskIntent
//因为需要这些信息新建一个Activity,此时mSourceRecord和mSourceStack一般都为null
computeSourceStack();//
mIntent.setFlags(mLaunchFlags);//此时mLaunchFlags已经是计算好的flag的整体了,将其设置到intent中去
//reusedActivity一般是针对singleTask和SingleInstance启动模式才有意义,当启动模式为standard的时候,它一般都为null
ActivityRecord reusedActivity = getReusableIntentActivity();
Log.d("ABM_Log", "ActivityStarter###startActivityUnchecked" + " mLaunchFlags: " + Integer.toHexString(mLaunchFlags) + " reusedActivity: " + reusedActivity);
final int preferredLaunchStackId =
(mOptions != null) ? mOptions.getLaunchStackId() : INVALID_STACK_ID;
final int preferredLaunchDisplayId =
(mOptions != null) ? mOptions.getLaunchDisplayId() : DEFAULT_DISPLAY;
//因此当reusedActivity不为null的时候
//根据前面的分析,singleTask和singleInstance都是会执行deliver intent的操作的,
//同时singleTask还是会按照需要删除掉一些Task中一些多余的Activity的,以下的代码将会有所见证
if (reusedActivity != null) {
if (mStartActivity.getTask() == null) {//设置mStartActivity的task为reusedActivity的task
mStartActivity.setTask(reusedActivity.getTask());
}
if (reusedActivity.getTask().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.
reusedActivity.getTask().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
|| isDocumentLaunchesIntoExisting(mLaunchFlags)
|| mLaunchSingleInstance || mLaunchSingleTask) {
final TaskRecord task = reusedActivity.getTask();
//这里我们可以看出当启动模式为singleInstance和singleTask的时候或者Intent中添加了FLAG_ACTIVITY_CLEAR_TOP flag
//我们会清除Task中最Top的实例之上的所有Activity(Task中可能存在该Activity多份实例,我们只找最接近Task Top的)
//有一种情况就是如果Activity的启动模式为standard,找到该实例的时候,清除之上的所有Activity之外并re-create该Activity,这里就是FLAG_ACTIVITY_CLEAR_TOP的解释
//具体可以关注performClearTaskForReuseLocked的实现,这里就不再赘述
final ActivityRecord top = task.performClearTaskForReuseLocked(mStartActivity,
mLaunchFlags);
// The above code can remove {@code reusedActivity} from the task, leading to the
// the {@code ActivityRecord} removing its reference to the {@code TaskRecord}. The
// task reference is needed in the call below to
// {@link setTargetStackAndMoveToFrontIfNeeded}.
if (reusedActivity.getTask() == null) {
reusedActivity.setTask(task);
}
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.getTask().setIntent(mStartActivity);
}
deliverNewIntent(top);//执行Activity的onNewIntent方法
}
}
sendPowerHintForLaunchStartIfNeeded(false /* forceSend */, reusedActivity);
reusedActivity = setTargetStackAndMoveToFrontIfNeeded(reusedActivity);
final ActivityRecord outResult =
outActivity != null && outActivity.length > 0 ? outActivity[0] : null;
// When there is a reused activity and the current result is a trampoline activity,
// set the reused activity as the result.
if (outResult != null && (outResult.finishing || outResult.noDisplay)) {
outActivity[0] = reusedActivity;
}
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(reusedActivity);
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();
if (outActivity != null && outActivity.length > 0) {
outActivity[0] = reusedActivity;
}
return START_TASK_TO_FRONT;
}
}
if (mStartActivity.packageName == null) {
final ActivityStack sourceStack = mStartActivity.resultTo != null
? mStartActivity.resultTo.getStack() : null;
if (sourceStack != null) {
sourceStack.sendActivityResultLocked(-1 /* callingUid */, mStartActivity.resultTo,
mStartActivity.resultWho, mStartActivity.requestCode, RESULT_CANCELED,
null /* data */);
}
ActivityOptions.abort(mOptions);
return START_CLASS_NOT_FOUND;
}
//当然还有使用其他方式deliver intent的,比如设置FLAG_ACTIVITY_SINGLE_TOP flag,或者启动方式为singleTop方式的,
// 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 topFocused = topStack.topActivity();
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) {
// 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;
}
deliverNewIntent(top);
// 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.getTask(), preferredLaunchStackId,
preferredLaunchDisplayId, topStack.mStackId);
return START_DELIVERED_TO_TOP;
}
boolean newTask = false;//这里就需要为mStartActivity,寻找或者创建合适的Activity
final TaskRecord taskToAffiliate = (mLaunchTaskBehind && mSourceRecord != null)
? mSourceRecord.getTask() : null;
// Should this be considered a new task?
int result = START_SUCCESS;
if (mStartActivity.resultTo == null && mInTask == null && !mAddingToTask
&& (mLaunchFlags & FLAG_ACTIVITY_NEW_TASK) != 0) {
newTask = true;
String packageName= mService.mContext.getPackageName();
if (mPerf != null) {
mStartActivity.perfActivityBoostHandler =
mPerf.perfHint(BoostFramework.VENDOR_HINT_FIRST_LAUNCH_BOOST, packageName, -1, BoostFramework.Launch.BOOST_V1);
}
result = setTaskFromReuseOrCreateNewTask(
taskToAffiliate, preferredLaunchStackId, topStack);
} else if (mSourceRecord != null) {
result = setTaskFromSourceRecord();
} else if (mInTask != null) {
result = setTaskFromInTask();
} 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();
}
if (result != START_SUCCESS) {
return result;
}
mService.grantUriPermissionFromIntentLocked(mCallingUid, mStartActivity.packageName,
mIntent, mStartActivity.getUriPermissionsLocked(), mStartActivity.userId);
mService.grantEphemeralAccessLocked(mStartActivity.userId, mIntent,
mStartActivity.appInfo.uid, UserHandle.getAppId(mCallingUid));
if (mSourceRecord != null) {
mStartActivity.getTask().setTaskToReturnTo(mSourceRecord);
}
if (newTask) {
EventLog.writeEvent(
EventLogTags.AM_CREATE_TASK, mStartActivity.userId,
mStartActivity.getTask().taskId);
}
ActivityStack.logStartActivity(
EventLogTags.AM_CREATE_ACTIVITY, mStartActivity, mStartActivity.getTask());
mTargetStack.mLastPausedActivity = null;
sendPowerHintForLaunchStartIfNeeded(false /* forceSend */, mStartActivity);
mTargetStack.startActivityLocked(mStartActivity, topFocused, newTask, mKeepCurTransition,
mOptions);
if (mDoResume) {
final ActivityRecord topTaskActivity =
mStartActivity.getTask().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 {
// If the target stack was not previously focusable (previous top running activity
// on that stack was not visible) then any prior calls to move the stack to the
// will not update the focused stack. If starting the new activity now allows the
// task stack to be focusable, then ensure that we now update the focused stack
// accordingly.
if (mTargetStack.isFocusable() && !mSupervisor.isFocusedStack(mTargetStack)) {
mTargetStack.moveToFront("startActivityUnchecked");
}//启动之前,要保证mTargetStack has focused
mSupervisor.resumeFocusedStackTopActivityLocked(mTargetStack, mStartActivity,
mOptions);//正常情况下,我们就要resume mStartActivity了
}
} else {
mTargetStack.addRecentActivityLocked(mStartActivity);
}
mSupervisor.updateUserStackLocked(mStartActivity.userId, mTargetStack);
mSupervisor.handleNonResizableTaskIfNeeded(mStartActivity.getTask(), preferredLaunchStackId,
preferredLaunchDisplayId, mTargetStack.mStackId);
return START_SUCCESS;
}
ActivityStarter的startActivityUnchecked方法的返回结果一般如下:
- START_TASK_TO_FRONT:正常使用singleTask和singleInstance模式下都会
- START_CLASS_NOT_FOUND:启动失败,没有找到启动的Activity
- START_DELIVERED_TO_TOP:对于standard启动模式下,再在Intent中添加FLAG_ACTIVITY_SINGLE_TOP flag的时候会deliver intent到当前Task Top的Activity实例中去,同样singleTop模式下如果Task TopActivity实例正是启动的Activity,也会返回该值
- START_SUCCESS:默认都是该返回值,一般standard启动模式时都会返回该结果
整体上startActivityUnchecked的逻辑还是较为清晰,我们可以将其分为4个部分:启动Activity之初设置ActivityStarter必要的基本属性,处理singleTask singleInstance模式启动的Activity,处理设置Intent flag需要deliver intent和设置singleTop模式情况的Activity,正常启动Activity(不管设置何种intent flag和启动模式,如果reusedActivity==null,系统都需要正常启动Activity)。
写在最后
Activity启动之初关于启动模式和设置不同的flag处理基本都在ActivityStarter的startActivityUnchecked方法中涵括了。至于在AndroidManifest中设置的一些属性,我们更多的认为这些属性影响的都会Activity和Task之间的关系以及Activity的行为,他们更多的和启动并没有多大关系,因此我们从类名可以看出ActivityStarter,这个类中应该关于其中的信息很少,因此本文也较少提及。至于AndroidManifest中设置的一些属性对在Activity启动过程中的影响将在后续的文章中继续分析介绍。文章都是自己的一些愚见,或许存在很多理解不到位的地方,望见谅,望指正。