Activity是Android四大组建之一,负责用户交互界面展示,其重要性不可言喻。Android系统由ActivityManagerService负责管理Activity。熟悉Activity的启动,将对我们学习了解ActivityManagerService大有裨益。本文基于AndroidP将以首次点击桌面应用图标的方式为线,分析Activity的启动,包括分析应用进程启动。文中将涉及不少应用启动的字眼,为了避免理解上的误解,先做一解释,其实应用启动的过程,便会伴随着Activity的启动,分析应用的启动流程包括Activity的启动。这里不讨论无界面应用(比如只托管service的应用进程启动),特指有界面的应用程序。由于篇幅原因文章中,只列举一些启动过程中的关键代码,将会更多介绍AMS相关的内容,至于涉及到WMS等其他内容暂不做重点讨论。我们在理解这些关键的基础上,回头再看Activity的启动将“如履平地”般容易。
09-19 15:58:37.864 1311 3040 I ActivityManager: START u0 {act=android.intent.action.MAIN cat=[android.intent.category.LAUNCHER] flg=0x10200000 cmp=com.tencent.mobileqq/.activity.SplashActivity bnds=[329,1575][497,1743] (has extras)} from uid 10032
09-19 15:58:37.879 1311 3040 I wm_task_created: [6415,1]//来自TaskWindowContainerController构造方法,
09-19 15:58:37.880 1311 3040 I wm_task_moved: [6415,1,9]//来自TaskWindowContainerController构造方法,具体是在TaskStack中打出的,因为这里调用了stack.addTask
09-19 15:58:37.880 1311 3040 I am_focused_stack: [0,1,0,reuseOrNewTask]
09-19 15:58:37.880 1311 3040 I wm_task_moved: [6415,1,9]//会做一次位置调整addOrReparentStartingActivity,来自于此,之后还会移动一次ActivityStack
09-19 15:58:37.882 1311 3040 I am_create_task: [0,6415]//打印出如下的Log的时候,此时ams启动Activity已经就该应用Activity创建了属于他的TaskRecord
09-19 15:58:37.882 1311 3040 I am_create_activity: [0,72463824,6415,com.tencent.mobileqq/.activity.SplashActivity,android.intent.action.MAIN,NULL,NULL,270532608]
09-19 15:58:37.882 1311 3040 I wm_task_moved: [6415,1,9]//ActivityStack.startActivityLocked 先做一次ActivityStack的移动
09-19 15:58:37.885 1311 3040 I am_pause_activity: [3167,192963549,com.miui.home/.launcher.Launcher] //启动一个Activity之前先暂停之前显示的Activity
09-19 15:58:37.888 3167 3167 I am_on_paused_called: [0,com.miui.home.launcher.Launcher,handlePauseActivity]
09-19 15:58:37.888 1311 3311 I am_uid_running: 10158
09-19 15:58:37.900 1311 3311 I am_proc_start: [0,31868,10158,com.tencent.mobileqq,activity,com.tencent.mobileqq/.activity.SplashActivity]//来自ActivityManagerService startProcessLocked
09-19 15:58:37.905 1311 3311 I am_proc_bound: [0,31868,com.tencent.mobileqq]//来自ActivityManagerService attachApplicationLocked 当系统创建好系统应用进程时,
//会调用ActivityThread的main方法,此时会通过Binder调用ams的attachApplication,进而调用attachApplicationLocked
09-19 15:58:37.909 1311 3311 I am_restart_activity: [0,72463824,6415,com.tencent.mobileqq/.activity.SplashActivity,31868]
09-19 15:58:37.909 1311 3311 I am_restart_activity_ai: [31868,com.tencent.mobileqq/.activity.SplashActivity,false]//xmiaomi特有AI启动?黑科技?
09-19 15:58:37.912 1311 3311 I am_set_resumed_activity: [0,com.tencent.mobileqq/.activity.SplashActivity,minimalResumeActivityLocked]
09-19 15:58:38.045 31868 31868 I am_on_resume_called: [0,com.tencent.mobileqq.activity.SplashActivity,LAUNCH_ACTIVITY]//应用进程Log输出
09-19 15:58:38.104 1311 1388 I am_activity_launch_time: [0,72463824,com.tencent.mobileqq/.activity.SplashActivity,213,213]
09-19 15:58:38.426 1311 1327 I am_stop_activity: [0,192963549,com.miui.home/.launcher.Launcher]
09-19 15:58:38.436 3167 3167 I am_on_stop_called: [0,com.miui.home.launcher.Launcher,handleStopActivity]
以上这份Log基本可以说是涵盖了Acitivity启动过程中所有的关键点信息,作为初学者,可能不知所以然,不过没关系,本文将详细介绍,这其中的原理。你可以获得Activity启动的耗时时间,你可以获得Activity启动所在的Stack和归属的Task id号……
Activity 生命周期回调方法汇总表。
方法 | 说明 | 是否能事后终止? | 后接 |
onCreate | 首次创建 Activity 时调用。 您应该在此方法中执行所有正常的静态设置 — 创建视图、将数据绑定到列表等等。 系统向此方法传递一个 Bundle 对象,其中包含 Activity 的上一状态,不过前提是捕获了该状态(请参阅后文的保存 Activity 状态)。 始终后接 onStart()。 |
NO | onStart |
onReStart | 在 Activity 已停止并即将再次启动前调用。 始终后接 onStart() |
NO | onStart |
onStart | 在 Activity 即将对用户可见之前调用。 如果 Activity 转入前台,则后接 onResume(),如果 Activity 转入隐藏状态,则后接 onStop()。 |
NO | onResume 或 onStop |
onResume | 在 Activity 即将开始与用户进行交互之前调用。 此时,Activity 处于 Activity 堆栈的顶层,并具有用户输入焦点。 始终后接 onPause()。 |
NO | onPause |
onPause | 当系统即将开始继续另一个 Activity 时调用。 此方法通常用于确认对持久性数据的未保存更改、停止动画以及其他可能消耗 CPU 的内容,诸如此类。 它应该非常迅速地执行所需操作,因为它返回后,下一个 Activity 才能继续执行。 如果 Activity 返回前台,则后接 onResume(),如果 Activity 转入对用户不可见状态,则后接 onStop()。 |
YES | onResume 或 onStop |
onStop | 在 Activity 对用户不再可见时调用。如果 Activity 被销毁,或另一个 Activity(一个现有 Activity 或新 Activity)继续执行并将其覆盖,就可能发生这种情况。 如果 Activity 恢复与用户的交互,则后接 onRestart(),如果 Activity 被销毁,则后接 onDestroy()。 |
YES | onRestart 或 onDestroy |
onDestory | 在 Activity 被销毁前调用。这是 Activity 将收到的最后调用。 当 Activity 结束(有人对 Activity 调用了 finish()),或系统为节省空间而暂时销毁该 Activity 实例时,可能会调用它。 您可以通过 isFinishing() 方法区分这两种情形。 | YES | 无 |
了解Activity,就要知道Activity的生命周期(Lifecycle),知道Activity各个生命周期出现的场景。如图所示,Activity从开始到结束可细分为:onCreate,onStart,onResume,onPause,onStop,onDestory。Activity的启动方式,显示,隐式:
Intent explicitIntent = new Intent("i.am.a.explicit.start");
getBaseContext().startActivity(explicitIntent);
Intent implicitIntent = new Intent(getBaseContext(), MainActivity.class);
getBaseContext().startActivity(implicitIntent);
一般我们都是如此启动,当然我们也可以通过配置AndroidManifest我们的某个Activity作为应用启动首选Activity:
Android规定Activity有四种启动方式standard,singleTop,singleTask,singleInstance。
讲Activity启动之前,必须强调一下ActivityThread,ActivityThread的main方法便是Android应用程序的入口,代表应用程序执行开始。
public static void main(String[] args) {
Process.setArgV0("");
Looper.prepareMainLooper();
ActivityThread thread = new ActivityThread();
thread.attach(false, startSeq);
if (sMainThreadHandler == null) {
sMainThreadHandler = thread.getHandler();
}
Looper.loop();
throw new RuntimeException("Main thread loop unexpectedly exited");
}
Android应用程序的事件操作是基于消息机制的,简单概述一下Android的消息机制,Android消息机制三大核心Handler,MessageQueue,Loop。Handler负责处理消息,MessageQueue用来保存消息,Loop用来循环取消息。ActivityThread的main方法的最后一行Looper.loop()(这是一个死循环,后续介绍Android消息机制的时候详细解释)意思就是我们的应用进入工作循环模式,开始运行,准备接受处理消息了。直白的说就是应用跑起来了。既然应用程序已经运行起来了,那么我们就需要Activity界面展示了。我们就要启动我们的Activity了,本文将以首次点击QQ桌面图标启动QQ首Activity为线,来分析Activity的启动流程及原理。
当我们首次通过点击QQ桌面图标来启动QQ应用时,QQ启动的动作请求来自桌面应用miui Launcher,经过一些列的调用,便请求AMS启动QQ应用,最终调用AMS的ActivityStarter的startActivity方法,该方法可以认为是Activity启动过程中的第一步,负责启动时最基本的检查操作,比如检查Activity信息是否存在,创建ActivityRecord等:
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,
SafeActivityOptions options,
boolean ignoreTargetSecurity, boolean componentSpecified, ActivityRecord[] outActivity,
TaskRecord inTask, boolean allowPendingRemoteAnimationRegistryLookup) {
int err = ActivityManager.START_SUCCESS;
// Pull the optional Ephemeral Installer-only bundle out of the options early.
final Bundle verificationBundle
= options != null ? options.popAppVerificationBundle() : null;
ProcessRecord callerApp = null;//启动Activity动作的发起进程,本例中之miui Launcher
if (caller != null) {
callerApp = mService.getRecordForAppLocked(caller);
if (callerApp != null) {
callingPid = callerApp.pid;
callingUid = callerApp.info.uid;
} else {
Slog.w(TAG, "Unable to find app for caller " + caller
+ " (pid=" + callingPid + ") when starting: "
+ intent.toString());
err = ActivityManager.START_PERMISSION_DENIED;
}
}
final int userId = aInfo != null && aInfo.applicationInfo != null
? UserHandle.getUserId(aInfo.applicationInfo.uid) : 0;
//这就是我们看见的第一行Log
if (err == ActivityManager.START_SUCCESS) {
Slog.i(TAG, "START u" + userId + " {" + intent.toShortString(true, true, true, false)
+ "} from uid " + callingUid);
}
ActivityRecord sourceRecord = null;//startActivityForResult方式启动时,需要记录启动Activity,可以理解为call activity
ActivityRecord resultRecord = null;//startActivityForResult方式启动时,记录setResult最终返回的Activity
if (resultTo != null) {//当使用startActivityForResult方式启动时,resultTo指的是启动Activity
sourceRecord = mSupervisor.isInAnyStackLocked(resultTo);
if (DEBUG_RESULTS) Slog.v(TAG_RESULTS,
"Will send result to " + resultTo + " " + sourceRecord);
if (sourceRecord != null) {
if (requestCode >= 0 && !sourceRecord.finishing) {
//一段美妙的逻辑,很有意思,借助requestCode来解决冲突
//默认我们通过startActivityForResult方式启动时,requestCode都会>=0
resultRecord = sourceRecord;//此时call activity = setResult最终返回的Activity
}
}
}
final int launchFlags = intent.getFlags();//获取启动的flags信息
if ((launchFlags & Intent.FLAG_ACTIVITY_FORWARD_RESULT) != 0 && sourceRecord != null) {
// Transfer the result target from the source activity to the new
// one being started, including any failures.
//一段美妙的逻辑,很有意思,借助requestCode来解决冲突,规定使用FLAG_ACTIVITY_FORWARD_RESULT flag时,
//比如A---startActivityForResult--requestCode>=0--->B---startActivityForResult--+FLAG_ACTIVITY_FORWARD_RESULT--requestCode<0--->C
//B的参数requestCode必须<=0,此时CsetResult的结果返回给A,这就是FLAG_ACTIVITY_FORWARD_RESULT的用法
if (requestCode >= 0) {
SafeActivityOptions.abort(options);
return ActivityManager.START_FORWARD_AND_REQUEST_CONFLICT;
}
resultRecord = sourceRecord.resultTo;//B的resultTo是A,获取A
if (resultRecord != null && !resultRecord.isInStackLocked()) {
resultRecord = null;
}
resultWho = sourceRecord.resultWho;
requestCode = sourceRecord.requestCode;
sourceRecord.resultTo = null;
if (resultRecord != null) {
resultRecord.removeResultsLocked(sourceRecord, resultWho, requestCode);
}
//此后的一部分代码略去,他们的工作主要用来设置一些resultActivity的信息,检查启动Activity是否合法,系统时候存在Activity相关信息,当这些条件都满足的时候就进行下一步,创建ActivityRecord
//为将要启动的Activity创建一个ActivityRecord
ActivityRecord r = new ActivityRecord(mService, callerApp, callingPid, callingUid,
callingPackage, intent, resolvedType, aInfo, mService.getGlobalConfiguration(),
resultRecord, resultWho, requestCode, componentSpecified, voiceSession != null,
mSupervisor, checkedOptions, sourceRecord);
return startActivity(r, sourceRecord, voiceSession, voiceInteractor, startFlags,
true /* doResume */, checkedOptions, inTask, outActivity);
}
该方法执行会输出对应Log
09-19 15:58:37.864 1311 3040 I ActivityManager: START u0 {act=android.intent.action.MAIN cat=[android.intent.category.LAUNCHER] flg=0x10200000 cmp=com.tencent.mobileqq/.activity.SplashActivity bnds=[329,1575][497,1743] (has extras)} from uid 10032
经过层层调用便会继续调用ActivityStarter的startActivityUnchecked方法,从命名可以看出,该方法中应该不做任何检查工作,之负责Activity启动时在AMS中的一些初始化的任务,我们知道Activity启动涉及启动flag,Task,Stack信息,我们需要做一个初始化:
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);//设置当前启动的Activity信息
computeLaunchingTaskFlags();//Android中一些Intent.flag会决定是否新建task,重新计算一下相关参数
computeSourceStack();//Android中规定Activity依附Task,Task存在于Stack中,Android存在好几种Stack
mIntent.setFlags(mLaunchFlags);
ActivityRecord reusedActivity = getReusableIntentActivity();//首次启动时为null
startActivityUnchecked方法还会为启动的Activity创建或复用对应的Stack和Task。更多Stack和Task信息请查看文章“任务(Task)和堆栈(Stack)”google网站翻译内容,介绍的非常详细。
/ 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;//获取当前获得焦点的Stack,由于启动时显示的是miui Launcher本里,就是miui Launcher所在的Stack
final ActivityRecord topFocused = topStack.getTopActivity();//获取当前Stack中有焦点的Activity,可以说是当前显示的Activity
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
|| isLaunchModeOneOf(LAUNCH_SINGLE_TOP, LAUNCH_SINGLE_TASK));
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(), preferredWindowingMode,
preferredLaunchDisplayId, topStack);
return START_DELIVERED_TO_TOP;
}
boolean newTask = false;
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);
}
//创建Task,或者依据taskToAffiliate进行复用,本例是新建taskToAffiliate=null
result = setTaskFromReuseOrCreateNewTask(taskToAffiliate, 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;
}
调用setTaskFromReuseOrCreateNewTask创建或者复用当前的启动Activity所在的Task,本例是创建。当应用内部Activity跳转排除启动模式和启动Flags干扰时,一般会复用之前Activity所在的Task。创建TaskRecod由ActivityStack.createTaskRecord完成,期间还会调用TaskRecord.createWindowContainer创建对应的TaskWindowContainerController,同时关联TaskRecord和TaskWindowContainerController,对应如下Log:
09-19 15:58:37.879 1311 3040 I wm_task_created: [6415,1]//来自TaskWindowContainerController构造方法,
09-19 15:58:37.880 1311 3040 I wm_task_moved: [6415,1,9]//来自TaskWindowContainerController构造方法,具体是在TaskStack中打出的,因为这里调用了stack.addTask
09-19 15:58:37.880 1311 3040 I am_focused_stack: [0,1,0,reuseOrNewTask]//来自setTaskFromReuseOrCreateNewTask最后调用mTargetStack.moveToFront("reuseOrNewTask")
09-19 15:58:37.880 1311 3040 I wm_task_moved: [6415,1,9]//mTargetStack.moveToFront中调用输出
private int setTaskFromReuseOrCreateNewTask(
TaskRecord taskToAffiliate, ActivityStack topStack) {
mTargetStack = computeStackFocus(mStartActivity, true, mLaunchFlags, mOptions);
// Do no move the target stack to front yet, as we might bail if
// isLockTaskModeViolation fails below.
if (mReuseTask == null) {
final TaskRecord task = mTargetStack.createTaskRecord(
mSupervisor.getNextTaskIdForUserLocked(mStartActivity.userId),
mNewTaskInfo != null ? mNewTaskInfo : mStartActivity.info,
mNewTaskIntent != null ? mNewTaskIntent : mIntent, mVoiceSession,
mVoiceInteractor, !mLaunchTaskBehind /* toTop */, mStartActivity, mSourceRecord,
mOptions);
addOrReparentStartingActivity(task, "setTaskFromReuseOrCreateNewTask - mReuseTask");
updateBounds(mStartActivity.getTask(), mLaunchParams.mBounds);
if (DEBUG_TASKS) Slog.v(TAG_TASKS, "Starting new activity " + mStartActivity
+ " in new task " + mStartActivity.getTask());
} else {
addOrReparentStartingActivity(mReuseTask, "setTaskFromReuseOrCreateNewTask");
}
if (taskToAffiliate != null) {
mStartActivity.setTaskToAffiliateWith(taskToAffiliate);
}
if (mService.getLockTaskController().isLockTaskModeViolation(mStartActivity.getTask())) {
Slog.e(TAG, "Attempted Lock Task Mode violation mStartActivity=" + mStartActivity);
return START_RETURN_LOCK_TASK_MODE_VIOLATION;
}
if (mDoResume) {
mTargetStack.moveToFront("reuseOrNewTask");
}
return START_SUCCESS;
}
当Task创建好了之后,我们已经将Task和Activity关联好,并且Task的头部Activity便是我们要启动的Activity,继续执行启动,对应Log如下:
09-19 15:58:37.882 1311 3040 I am_create_task: [0,6415]//打印出如下的Log的时候,此时ams启动Activity已经就该应用Activity创建了属于他的TaskRecord
09-19 15:58:37.882 1311 3040 I am_create_activity: [0,72463824,6415,com.tencent.mobileqq/.activity.SplashActivity,android.intent.action.MAIN,NULL,NULL,270532608]
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;
mSupervisor.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.
mService.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");
}
mSupervisor.resumeFocusedStackTopActivityLocked(mTargetStack, mStartActivity,
mOptions);
}
} else if (mStartActivity != null) {
mSupervisor.mRecentTasks.add(mStartActivity.getTask());
}
mSupervisor.updateUserStackLocked(mStartActivity.userId, mTargetStack);
mSupervisor.handleNonResizableTaskIfNeeded(mStartActivity.getTask(), preferredWindowingMode,
preferredLaunchDisplayId, mTargetStack);
return START_SUCCESS;
}
其中mTargetStack.startActivityLocked方法,对应如下Log:
09-19 15:58:37.882 1311 3040 I wm_task_moved: [6415,1,9]//ActivityStack.startActivityLocked 先做一次ActivityStack的移动
当我们创建设置好Task和Stack之后,我们就要真正开始resume启动激活Activity了,继续执行 mSupervisor.resumeFocusedStackTopActivityLocked最终经过层层调用直到ActivityStack的resumeTopActivityInnerLocked:这也是启动过程中第一次调用该方法,由于整个启动过程函数调用相当复杂,这里暂且先预览一下整个执行过程
miui Launcher进程
Activity.java
startActivity(Intent intent)
startActivity(intent, null)
startActivityForResult(intent, -1);
startActivityForResult(intent, requestCode, null);
Instrumentation.java
execStartActivity(this, mMainThread.getApplicationThread(), mToken, this, intent, requestCode, options);---Binder IPC--->startActivity
SystemServer进程
ActivityManagerService.java
startActivity(whoThread, who.getBasePackageName(), intent,intent.resolveTypeIfNeeded(who.getContentResolver()),token, target, requestCode, 0, null, options);
startActivityAsUser(caller, callingPackage, intent, resolvedType, resultTo,resultWho, requestCode, startFlags, profilerInfo, bOptions,UserHandle.getCallingUserId());
startActivityAsUser(caller, callingPackage, intent, resolvedType, resultTo,resultWho, requestCode, startFlags, profilerInfo, bOptions, userId,true /*validateIncomingUser*/);
ActivityStarter.java
startActivity(mRequest.caller, mRequest.intent, mRequest.ephemeralIntent,mRequest.resolvedType, mRequest.activityInfo, mRequest.resolveInfo,mRequest.voiceSession, mRequest.voiceInteractor, mRequest.resultTo,mRequest.resultWho, mRequest.requestCode, mRequest.callingPid,mRequest.callingUid, mRequest.callingPackage, mRequest.realCallingPid,mRequest.realCallingUid, mRequest.startFlags, mRequest.activityOptions,mRequest.ignoreTargetSecurity, mRequest.componentSpecified,mRequest.outActivity, mRequest.inTask, mRequest.reason,mRequest.allowPendingRemoteAnimationRegistryLookup)
startActivity(caller, intent, ephemeralIntent, resolvedType,aInfo, rInfo, voiceSession, voiceInteractor, resultTo, resultWho, requestCode,callingPid, callingUid, callingPackage, realCallingPid, realCallingUid, startFlags,options, ignoreTargetSecurity, componentSpecified, mLastStartActivityRecord,inTask, allowPendingRemoteAnimationRegistryLookup)
startActivity(r, sourceRecord, voiceSession, voiceInteractor, startFlags,true /* doResume */, checkedOptions, inTask, outActivity)
startActivityUnchecked(r, sourceRecord, voiceSession, voiceInteractor,startFlags, doResume, options, inTask, outActivity);
ActivityStackSupervisor.java
resumeFocusedStackTopActivityLocked(mTargetStack, mStartActivity,mOptions);
ActivityStack.java
resumeTopActivityUncheckedLocked(target, targetOptions);
resumeTopActivityInnerLocked(prev, options);
startPausingLocked(userLeaving, false, next, false);
ClientLifecycleManager.java
scheduleTransaction(prev.app.thread, prev.appToken,PauseActivityItem.obtain(prev.finishing, userLeaving,prev.configChangeFlags, pauseImmediately));
ClientTransaction.java
schedule();---Binder IPC--->scheduleTransaction
miui Launcher进程
IApplicationThread.java
scheduleTransaction(this);
ActivityThread.java
scheduleTransaction(transaction);
sendMessage(EXECUTE_TRANSACTION)->handleMessage
TransactionExecutor.java
execute(transaction);
executeCallbacks(transaction);
executeLifecycleState(transaction);
PauseActivityItem.java
execute(mTransactionHandler, token, mPendingActions);
ActivityThread.java
handlePauseActivity(token, mFinished, mUserLeaving, mConfigChanges, pendingActions,"PAUSE_ACTIVITY_ITEM");
postExecute(mTransactionHandler, token, mPendingActions);---Binder IPC--->activityPaused
SystemServer进程
ActivityManagerService.java
activityPaused(token);
ActivityStack.java
activityPausedLocked(token, false);
completePauseLocked(true /* resumeNext */, null /* resumingActivity */);
ActivityStackSupervisor.java
resumeFocusedStackTopActivityLocked(topStack, prev, null);
ActivityStack.java
resumeTopActivityUncheckedLocked(target, targetOptions);
ActivityStackSupervisor.java
startSpecificActivityLocked(next, true, true);
ActivityManagerService.java
startProcessLocked(r.processName, r.info.applicationInfo, true, 0,"activity", r.intent.getComponent(), false, false, true,(null != r.launchedFromPackage ? r.launchedFromPackage : "NA"));
QQ进程
一些列调用到ActivityThread.java的main方法,我们称之为应用程序的入口
attach(false, startSeq);
attachApplication(mAppThread, startSeq);---Binder IPC--->attachApplicationLocked
SystemServer进程
ActivityManagerService.java
attachApplicationLocked(thread, callingPid, callingUid, startSeq);//am_proc_bound
bindApplication(processName, appInfo, providers, null, profilerInfo,null, null, null, testMode,mBinderTransactionTrackingEnabled, enableTrackAllocation,isRestrictedBackupMode || !normalMode, app.persistent,new Configuration(getGlobalConfiguration()), app.compat,getCommonServicesLocked(app.isolated),mCoreSettingsObserver.getCoreSettingsLocked(),buildSerial, isAutofillCompatEnabled);
ActivityStackSupervisor.java
attachApplicationLocked
realStartActivityLocked(activity, app,top == activity /* andResume */, true /* checkConfig */)//am_restart_activity
ClientLifecycleManager.java
scheduleTransaction(clientTransaction);
ClientTransaction.java
schedule();---Binder IPC--->scheduleTransaction
QQ进程
IApplicationThread.java
scheduleTransaction(this);
ActivityThread.java
scheduleTransaction(transaction);
sendMessage(EXECUTE_TRANSACTION)->handleMessage
TransactionExecutor.java
execute(transaction);
executeCallbacks(transaction);
LaunchActivityItem.java
execute(mTransactionHandler, token, mPendingActions);
ActivityThread.java
handleLaunchActivity(r, pendingActions, null /* customIntent */);
executeLifecycleState(transaction);
ResumeActivityItem.java
execute(mTransactionHandler, token, mPendingActions);
ActivityThread.java
handleResumeActivity(token, true /* finalStateRequest */, mIsForward,"RESUME_ACTIVITY");
postExecute(mTransactionHandler, token, mPendingActions);---Binder IPC--->activityResumed
SystemServer进程
ActivityManagerService.java
activityResumed(token);
第一次调用resumeTopActivityInnerLocked,我们会发现参数prev和next都是指向要启动的QQ的SplashActivity,而当前手机显示的应用是miui launcher,因此我们在启动新的Activity之前需要暂停当前正在显示的应用,因此此时会输出对应的Log:
09-19 15:58:37.885 1311 3040 I am_pause_activity: [3167,192963549,com.miui.home/.launcher.Launcher] //启动一个Activity之前先暂停之前显示的Activity
当miui launcher成功暂停的时候,输出对应Log:
09-19 15:58:37.888 3167 3167 I am_on_paused_called: [0,com.miui.home.launcher.Launcher,handlePauseActivity]
接着会继续第二次执行resumeTopActivityInnerLocked去真正的启动我们的QQ,但是由于QQ首次启动,因此我们先创建其进程,当创建OK好QQ进程之后,会调用QQ进程的ActivityThread的main函数,这样QQ进程便成功启动了,对应Log:
09-19 15:58:37.900 1311 3311 I am_proc_start: [0,31868,10158,com.tencent.mobileqq,activity,com.tencent.mobileqq/.activity.SplashActivity]//来自ActivityManagerService startProcessLocked
但是此时QQ应用进程在系统中的名称并未设置为对应的QQ的进程名,会在bindApplication操作中进行设置。对应Log:
09-19 15:58:37.905 1311 3311 I am_proc_bound: [0,31868,com.tencent.mobileqq]//来自ActivityManagerService attachApplicationLocked 当系统创建好系统应用进程时,
程创建好之后,继续Activity启动流程,从代码流程中我们可以看出最终是要执行realStartActivityLocked这个方法的,对应会输出Log:
09-19 15:58:37.909 1311 3311 I am_restart_activity: [0,72463824,6415,com.tencent.mobileqq/.activity.SplashActivity,31868]
09-19 15:58:37.909 1311 3311 I am_restart_activity_ai: [31868,com.tencent.mobileqq/.activity.SplashActivity,false]//xmiaomi特有AI启动?黑科技?
realStartActivityLocked方法中会调用stack.minimalResumeActivityLocked(r); 对应会输出Log
09-19 15:58:37.912 1311 3311 I am_set_resumed_activity: [0,com.tencent.mobileqq/.activity.SplashActivity,minimalResumeActivityLocked]
之后会到QQ应用进程去启动首界面SplashActivity,对应从应用程序中会输出Log:
09-19 15:58:38.045 31868 31868 I am_on_resume_called: [0,com.tencent.mobileqq.activity.SplashActivity,LAUNCH_ACTIVITY]//应用进程Log输出
此时应用进程便会开始绘制自己的界面,并与WMS和SurfaceFlinger通信,进行界面展示,至于Activity的界面绘制流程,计划后续会再进行介绍,此处不重点分析。
任务(Task)是指在执行特定作业时与用户交互的一系列 Activity。 这些 Activity 按照各自的打开顺序排列在堆栈(Stack)中,AMS中分别以TaskRecord和ActivityStack表示,Activity则用ActivityRecord表示。
大多数情况下设备主屏幕即launcher界面,就是任务的起点,当用户触摸应用启动器中的图标(或主屏幕上的快捷方式)时,该应用的任务将出现在前台。 如果应用不存在任务(应用最近未曾使用),则会创建一个新任务,并且该应用的“主”Activity 将作为堆栈中的根 Activity 打开。
当前 Activity 启动另一个 Activity 时,该新 Activity 会被推送到堆栈顶部,成为焦点所在。 前一个 Activity 仍保留在堆栈中,但是处于停止状态。Activity 停止时,系统会保持其用户界面的当前状态。 用户按“返回”按钮时,当前 Activity 会从堆栈顶部弹出(Activity 被销毁),而前一个 Activity 恢复执行(恢复其 UI 的前一状态)。 堆栈中的 Activity 永远不会重新排列,仅推入和弹出堆栈:由当前 Activity 启动时推入堆栈;用户使用“返回”按钮退出时弹出堆栈。 因此,返回栈以“后进先出”对象结构运行。
下图 通过时间线显示 Activity 之间的进度以及每个时间点的当前返回栈,直观呈现了这种行为。
显示任务中的每个新 Activity 如何向返回栈添加项目。 用户按“返回”按钮时,当前 Activity 随即被销毁,而前一个 Activity 恢复执行。
如果用户继续按“返回”,堆栈中的相应 Activity 就会弹出,以显示前一个 Activity,直到用户返回主屏幕为止,当所有 Activity 均从堆栈中移除后,任务即不复存在。
任务是从用户角度来管理Activity的,当用户开始新的任务或者按home键回到主屏幕时,可以移到到后台,当该任务到后台时,任务中所有的Activity全部处于停止状态,但是任务的堆栈顺序是不会改变的,从window的角度来说,该任务只是失去焦点而已,
两个任务:任务 B 在前台接收用户交互,而任务 A 则在后台等待恢复。
当该任务获得焦点时,用户即可回到离开时的状态。但是如果后台运行着多个任务,系统为了保持流畅性,可能会开始销毁后台的Activity,以回收内存资源,从而导致Activity的状态丢失,详细请参见Activity状态
正常情况下,由于栈中的 Activity 永远不会重新排列,因此如果应用允许用户从多个 Activity 中启动特定 Activity,则会创建该 Activity 的新实例并推入堆栈中(而不是将 Activity 的任一先前实例置于顶部),因此,应用中的一个 Activity 可能会多次实例化(即使 Activity 来自不同的任务)如下图
但是,如果我们不希望 Activity 多次实例化,则可修改此行为,详细请参见Task管理。系统中Activity和Task的默认行为如下:
当 Activity A 启动 Activity B 时,Activity A 将会停止,但系统会保留其状态(例如,滚动位置和已输入表单中的文本)。如果用户在处于 Activity B 时按“返回”按钮,则 Activity A 将恢复其状态,继续执行。
用户通过按“主页”按钮离开任务时,当前 Activity 将停止且其任务会进入后台。 系统将保留任务中每个 Activity 的状态。如果用户稍后通过选择开始任务的启动器图标来恢复任务,则任务将出现在前台并恢复执行堆栈顶部的 Activity。
如果用户按“返回”按钮,则当前 Activity 会从堆栈弹出并被销毁。 堆栈中的前一个 Activity 恢复执行。销毁 Activity 时,系统不会保留该 Activity 的状态。
即使来自其他任务,Activity 也可以多次实例化。
刚写本篇之初,以”ActivityManagerService之Activity启动揭秘“为题,斟酌一下,还是以”ActivityManagerService之Activity启动初探“,本篇内容更多的接近于介绍Activity初次启动的流程,部分涉及AMS中Stack和Task的管理,并未详细介绍其中的细节,因此计划后续将AMS中各个细节部分,深入剖析一下。