[Android][Framework]Activity启动流程

不操千曲而后晓声,观千剑而后识器。这篇文章主要讲述[Android][Framework]Activity启动流程相关的知识,希望能为你提供帮助。
开始从Launcher点击一个App图标,会触发如下动作:
LauncherActivity.java


protected void (ListView l, View v, int position, long id) {
Intent intent = intentForPosition(position);
startActivity(intent);
}

走到了 Activity.java 的startActivity相关方法:

public void startActivity(Intent intent) {
this.startActivity(intent, null);
}


public void startActivity(Intent intent, @Nullable Bundle options) {
if (options != null) {
startActivityForResult(intent, -1, options);
} else {
// Note we want to go through this call for compatibility with
// applications that may have overridden the method.
startActivityForResult(intent, -1);
}
}

public void startActivityForResult(@RequiresPermission Intent intent, int requestCode) {
startActivityForResult(intent, requestCode, null);
}

public void startActivityForResult(@RequiresPermission Intent intent, int requestCode,
@Nullable Bundle options) {
if (mParent == null) {
options = transferSpringboardActivityOptions(options);
// 关键代码
Instrumentation.ActivityResult ar =
mInstrumentation.execStartActivity(
this, mMainThread.getApplicationThread(), mToken, this,
intent, requestCode, options);
//
if (ar != null) {
mMainThread.sendActivityResult(
mToken, mEmbeddedID, requestCode, ar.getResultCode(),
ar.getResultData());
}
if (requestCode > = 0) {
mStartedActivity = true;
}

cancelInputsAndStartExitTransition(options);
// TODO Consider clearing/flushing other event sources and events for child windows.
} else {
if (options != null) {
mParent.startActivityFromChild(this, intent, requestCode, options);
} else {
// Note we want to go through this method for compatibility with
// existing applications that may have overridden it.
mParent.startActivityFromChild(this, intent, requestCode);
}
}
}

public void startActivityFromChild(@NonNull Activity child, @RequiresPermission Intent intent, int requestCode, @Nullable Bundle options) {
options = transferSpringboardActivityOptions(options);
Instrumentation.ActivityResult ar =
mInstrumentation.execStartActivity(
this, mMainThread.getApplicationThread(), mToken, child,
intent, requestCode, options);
if (ar != null) {
mMainThread.sendActivityResult(
mToken, child.mEmbeddedID, requestCode,
ar.getResultCode(), ar.getResultData());
}
cancelInputsAndStartExitTransition(options);
}

由代码了解到,调用startActivity最终都会走到startActivityForResult方法。startActivityForResult的作用是,如果想在Activity中得到新打开的Activity关闭后返回的数据,就要用这个方法。但因为这里传入的requestCode是-1,所以不会返回结果。
这个函数首先会判断要启动的Activity是否是子Activity,如果不是,就会调用execStartActivity函数。反之,调用startActivityFromChild函数。不过在startActivityFromChild中依然是执行到execStartActivity方法中去。区别是参数传递的父类对象。
InstrumentationInstrumentation.java
Instrumentation之前在 ActivityThread 有提到过,它和Activity有些类似。它主要作用是连接Activity/Application的中间层,也就是启动Activity,包括声明周期都不是直接调用的,而是通过instrumentation作为中介调用,这样它就具有了监控App的能力。使用Instrumentation框架可以进行对App的自动化测试,精准控制Activity。
调用execStartActivity方法传入一个mMainThread.getApplicationThread()参数,这个mMainThread是个ActivityThread,在Activity.attach时拿到的。因为此时还在桌面界面启动,所以还处在Launcher进程中,所以是Launcher的主线程。另一个参数是mToken,是IBinder类型的对象。这里由于还是在Launcher进程,所以这个mToken也是在启动Launcer时的attach方法中创建。
public ActivityResult execStartActivity(
Context who, IBinder contextThread, IBinder token, Activity target,
Intent intent, int requestCode, Bundle options) {
IApplicationThread whoThread = (IApplicationThread) contextThread;
Uri referrer = target != null ? target.onProvideReferrer() : null;
if (referrer != null) {
intent.putExtra(Intent.EXTRA_REFERRER, referrer);
}
if (mActivityMonitors != null) {
synchronized (mSync) {
final int N = mActivityMonitors.size();
for (int i=0; i< N; i++) { // 通过ActivityMonitor对启动的activity进行检查
final ActivityMonitor am = mActivityMonitors.get(i);
ActivityResult result = null;
if (am.ignoreMatchingSpecificIntents()) {
result = am.onStartActivity(intent);
}
if (result != null) {
am.mHits++;
return result;
} else if (am.match(who, null, intent)) {
am.mHits++;
if (am.isBlocking()) {
return requestCode > = 0 ? am.getResult() : null;
}
break;
}
}
}
}
try {
intent.migrateExtraStreamToClipData();
intent.prepareToLeaveProcess(who);

// 通过binder将处理从launcher进程切换到ActivityManagerService
int result = ActivityManager.getService()
.startActivity(whoThread, who.getBasePackageName(), intent,
intent.resolveTypeIfNeeded(who.getContentResolver()),
token, target != null ? target.mEmbeddedID : null,
requestCode, 0, null, options);
checkStartActivityResult(result, intent);
} catch (RemoteException e) {
throw new RuntimeException(" Failure from system" , e);
}
return null;
}

方法执行过程见注释。下面是时序图:
[Android][Framework]Activity启动流程

文章图片

Server代码流程ActivityManagerService.java
ActivityManagerService.startActivity

public final int startActivity(IApplicationThread caller, String callingPackage,
Intent intent, String resolvedType, IBinder resultTo,String resultWho, int requestCode,
int startFlags, ProfilerInfo profilerInfo, Bundle bOptions) {
return startActivityAsUser(caller, callingPackage, intent, resolvedType, resultTo,
resultWho, requestCode, startFlags, profilerInfo, bOptions,
UserHandle.getCallingUserId());
}

为了适配多用户,直接调用startActivityAsUser方法。
ActivityManagerService.startActivityAsUser

public final int startActivityAsUser(IApplicationThread caller, String callingPackage,
Intent intent, String resolvedType, IBinder resultTo,String resultWho, int requestCode,
int startFlags, ProfilerInfo profilerInfo, BundlebOptions, int userId) {
enforceNotIsolatedCaller(" startActivity" ); // 验证CallingUid
userId = mUserController.handleIncomingUser(Binder.getCallingPid(),Binder.getCallingUid(),
userId, false, ALLOW_FULL_ONLY," startActivity" , null);
// TODO: Switch to user app stacks here.
return mActivityStarter.startActivityMayWait(caller, -1, callingPackage, intent,
resolvedType, null, null, resultTo,resultWho, requestCode, startFlags,
profilerInfo, null, null, bOptions, false,userId, null, " startActivityAsUser" );
}

参数说明
caller,调用者。这里是Launcher的所在进程。如果是am start启动的,就是为null
intent,要启动的intent。包含Action,Categories,Component
resultTo,启动一个应用后负责接收返回结果的binder
callingPackage,启动当前activity的应用包名,此处是Launcher包名
这里,通过调用enforceNotIsolatedCaller()判断当前的请求者Binder.getCallingUid()是否允许启动Activity。
handleIncomingUser()会校验userSerialNumber,用户是否匹配,userSerial检查Number是什么意思?它是配合多用户新引入的引入userSerialNumber来标识用户。
这个方法中最关键的就是mActivityStarter.startActivityMayWait
ActivityStarter.startActivityMayWait
贴一部分代码 ActivityStarter.java
这是一个预处理函数,列举一下主要功能:
// Collect information about the target of the Intent.
ActivityInfo aInfo = mSupervisor.resolveActivity(intent, rInfo, startFlags, profilerInfo);

这个mSupervisor是 ActivityStackSupervisor.java 类。首先从rInfo拿到aInfo(这个方法经过AMS对aInfo做了一些处理)
ActivityStackSupervisor.ActivityContainer container =(ActivityStackSupervisor.ActivityContainer)iContainer;
final ActivityStack stack;
if (container == null || container.mStack.isOnHomeDisplay()) {
stack = mSupervisor.mFocusedStack;
} else {
stack = container.mStack;
}

这里使用当前stack,if条件判断当前是不是在主屏幕上显示,是分屏的雏形。因为当前应用没有启动起来,当前焦点stack是Home。下面就是最重要的部分:
int res = startActivityLocked(caller, intent, ephemeralIntent, resolvedType,
aInfo, rInfo, voiceSession, voiceInteractor,
resultTo, resultWho, requestCode, callingPid,
callingUid, callingPackage, realCallingPid, realCallingUid, startFlags,
options, ignoreTargetSecurity, componentSpecified, outRecord, container,
inTask, reason);

上面拿到的aInfo和一些参数都传入了startActivityLocked方法,而startActivityLocked内部处理部分参数,直接把参数传给startActivity方法。这个startActivity方法是一个重量级的函数,内容很多,下面看一下:
ActivityStarter.startActivity
ProcessRecord callerApp = null;
if (caller != null) {
callerApp = mService.getRecordForAppLocked(caller);
if (callerApp != null) {
callingPid = callerApp.pid;
callingUid = callerApp.info.uid;

创建一个空的ProcessRecord对象。然后先判断启动方式(caller不为空正常启动),然后调用getRecordForAppLocked获得一个ProcessRecord对象。
ProcessRecord 注释:Full information about a particular process that is currently running.
这里运行的是Launcher的进程,ProcessRecord记录的也是Launcher进程信息。
ActivityRecord sourceRecord = null;
ActivityRecord resultRecord = null;
if (resultTo != null) { // 接收返回结果
sourceRecord = mSupervisor.isInAnyStackLocked(resultTo); // 拿到sourceRecord
if (DEBUG_RESULTS) Slog.v(TAG_RESULTS,
" Will send result to " + resultTo + " " + sourceRecord);
if (sourceRecord != null) {
if (requestCode > = 0 & & !sourceRecord.finishing) { // sourceRecord非结束状态
resultRecord = sourceRecord;
}
}
}

这部分就是对应startActivityForResult和onActivityResult处理逻辑,sourceRecord就是源Activity的Record,另一个是目标Activity的Record。这里得到的sourceRecord就是Launcher的ActivityRecord。
后面的代码作用是:
  1. 启动标记的获得和检查
  2. 检查是否有启动any activity的权限
  3. 检查是否有启动当前请求的componentName的权限
  4. 检查权限后,然后判断是否要启动Activity
boolean abort = !mSupervisor.checkStartAnyActivityPermission(intent, aInfo, resultWho, requestCode, callingPid, callingUid, callingPackage, ignoreTargetSecurity, callerApp, resultRecord, resultStack, options);

abort |= !mService.mIntentFirewall.checkStartActivity(intent, callingUid, callingPid, resolvedType, aInfo.applicationInfo);

abort为true时,就会禁止启动某一个Activity。
ActivityRecord r = new ActivityRecord(mService, callerApp,callingPid, callingUid, callingPackage, intent,resolvedType, aInfo, mService.getGlobalConfiguration(),resultRecord, resultWho, requestCode, componentSpecified,voiceSession != null, mSupervisor, container, options,sourceRecord);

一个Activity启动的关键步骤:
  • 找到它应该所在的Stack
  • 建立一个ActivityRecord
  • 建立TaskRecord
  • 建立运行的进程ProcessRecord
当然这里是第一次运行一个Activity。如果是第二次运行,要涉及到task和process的复用。后面看情况讲。
然后还有一段关键条件检查,检查App是否允许进行切换。就是在使用某个App的时候来了一个电话,就会切换到电话界面:
if (!mService.checkAppSwitchAllowedLocked(callingPid, callingUid,
realCallingPid, realCallingUid, " Activity start" )) {
PendingActivityLaunch pal =new PendingActivityLaunch(r,
sourceRecord, startFlags, stack, callerApp);
mPendingActivityLaunches.add(pal);
ActivityOptions.abort(options);
return ActivityManager.START_SWITCHES_CANCELED;
}

如果不允许切换,则存储起来,有机会再启动它。将保存到mPendingActivityLaunches中。
没有权限切换意味着当前显示的应用进行了stopAppSwitches。这个方法会使其他Task 的Activity无法显示在前端,但同一个Task中的Activity则不受制约。取消禁止切换使用 resumeAppSwitches。为了防止使用者不调用resumeAppSwitches,系统设置了一个超 时时间(5s),超时则自动resume。在resume中不会处理Pending起来的Activity,但超时会触发处理Pending的Activity。
这个startActivity执行完成之后,又会进入下一个startActivity的重载函数。
ActivityStarter.startActivityUnchecked
【[Android][Framework]Activity启动流程】进入新的startActivity函数后,执行try catch里就会先执行
result = startActivityUnchecked(r, sourceRecord, voiceSession, voiceInteractor,
startFlags, doResume, options, inTask, outActivity);

在startActivityUnchecked中会连续执行几个很重要的逻辑函数
ActivityStarter.setInitialState第一个执行setInitialState方法,做初始化。这个初始化有一段比较关键的代码:
// We' ll invoke onUserLeaving before onPause only if the launching
// activity did not explicitly state that this is an automated launch.
mSupervisor.mUserLeaving = (mLaunchFlags & FLAG_ACTIVITY_NO_USER_ACTION) == 0;

我把官方注释也放上去了,这个是说,当启动标记未设置Intent.FLAG_ACTIVITY_NO_USER_ACTION时, mUserLeaving为true。关于Intent.FLAG_ACTIVITY_NO_USER_ACTION标记用于判断是否是用户的行为导致的Activity切换,比如当你按home键时,会切换 Activity,此时会回调onUserLeaveHint(),如果是来电导致的Activity切换,就不会调用onUserLeaveHint(),也就是说如何设置了这个标记就表明当前启动的行为是非用户行为。这样切换到后台的Activity便不会回调onUserLeaveHint()这个生命周期函数。
ActivityStarter.computeLaunchingTaskFlags回到startActivityUnchecked方法,在执行完初始化函数后,接着执行computeLaunchingTaskFlags()方法。这个方法也有一个重要的代码段:
if (mSourceRecord == null & & mInTask != null & & mInTask.getStack() != null) {
final Intent baseIntent = mInTask.getBaseIntent();
final ActivityRecord root = mInTask.getRootActivity();

从最近任务栏启动开机恢复出来的activity时,会走这个逻辑,mInTask这个参数是5.0 上新增的一个参数,主要是处理任务栏相关的内容,当从最近任务栏启动“恢复的 Task”时,mInTask值是不为空的,但这时sourceRecord是为null 。当进入这个逻辑后,从mInTask中获得Intent和ActivityRecord对象,如果当前的获得的ActivityRecord对象为null时,这时候会重新调整启动flag参数、task的复用等一系列调整,这里不在过多分析。
一般启动Activity时都会走到这里:
if (mInTask == null) {
if (mSourceRecord == null) {
// This activity is not being started from another...in this
// case we -always- start a new task.
if ((mLaunchFlags & FLAG_ACTIVITY_NEW_TASK) == 0 & & mInTask == null) {
Slog.w(TAG, " startActivity called from non-Activity context; forcing " +
" Intent.FLAG_ACTIVITY_NEW_TASK for: " + mIntent);
mLaunchFlags |= FLAG_ACTIVITY_NEW_TASK;
}
} else if (mSourceRecord.launchMode == LAUNCH_SINGLE_INSTANCE) {
// The original activity who is starting us is running as a single
// instance...this new activity it is starting must go on its
// own task.
mLaunchFlags |= FLAG_ACTIVITY_NEW_TASK;
} else if (mLaunchSingleInstance || mLaunchSingleTask) {
// The activity being started is a single instance...it always
// gets launched into its own task.
mLaunchFlags |= FLAG_ACTIVITY_NEW_TASK;
}
}

上面这段代码主要是设置启动标识,设置Intent.FLAG_ACTIVITY_NEW_TASK的启动标识。下面几种情况设置这个标识:
  1. mSourceRecord == null,不是通过home上启动Activity的情况,比如用命令“am start”
  2. 启动目标的Activity的启动模式是singleInstance
  3. 启动目标的Activity的启动模式是 singleTask。我们都知道,当设置了这个flag后,要启动的Activity必须就要在新的Task中了。 继续向下分析,都是象电影一样,一个场景一个场景的。 下面的场景是mSourceRecord != null的状态。
ActivityStarter.computeSourceStack如果mSourceRecord为null,这里会立刻返回。如果不为空,则往下进行。
这个场景下,首先判断mSourceRecord 是不是正在 finish的状态,因为这种状态下,我们就不能在把这个mSourceRecord作为源了。
如果它的flag没有添加 FLAG_ACTIVITY_NEW_TASK这个flag,我们为它手动添加一个,然后将mSourceRecord ,mSourceStack置为null. 如果mSourceRecord不是在finish状态,那么会将源所在Stack指向一个新的Stack。这主要是为后面进行task复用在做准备。
private void computeSourceStack() {
if (mSourceRecord == null) {
mSourceStack = null;
return;
}
if (!mSourceRecord.finishing) {
mSourceStack = mSourceRecord.getStack();
return;
}

// If the source is finishing, we can' t further count it as our source. This is because the
// task it is associated with may now be empty and on its way out, so we don' t want to
// blindly throw it in to that task.Instead we will take the NEW_TASK flow and try to find
// a task for it. But save the task information so it can be used when creating the new task.
if ((mLaunchFlags & FLAG_ACTIVITY_NEW_TASK) == 0) {
Slog.w(TAG, " startActivity called from finishing " + mSourceRecord
+ " ; forcing " + " Intent.FLAG_ACTIVITY_NEW_TASK for: " + mIntent);
mLaunchFlags |= FLAG_ACTIVITY_NEW_TASK;
mNewTaskInfo = mSourceRecord.info;

// It is not guaranteed that the source record will have a task associated with it. For,
// example, if this method is being called for processing a pending activity launch, it
// is possible that the activity has been removed from the task after the launch was
// enqueued.
final TaskRecord sourceTask = mSourceRecord.getTask();
mNewTaskIntent = sourceTask != null ? sourceTask.intent : null;
}
mSourceRecord = null;
mSourceStack = null;
}

ActivityStarter.getReusableIntentActivity上面执行结束会执行下面方法:
ActivityRecord reusedActivity = getReusableIntentActivity();

这个方法就要检查是否有可以复用的Task或者Activity了。
/**
* 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;
// 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);
} 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);
} else {
// Otherwise find the best task to put the activity in.
intentActivity = mSupervisor.findTaskLocked(mStartActivity, mSourceDisplayId);
}
}
return intentActivity;
}

代码中可以看到,在不同的LaunchMode中会做findActivityLocked查找可复用Task。关于Task这边内容比较多,暂时先不梳理了。
TBC
原文:大专栏   [Android][Framework]Activity启动流程


    推荐阅读