APP的启动过程是任何一个学习Android的人所必须了解的,这篇文章将会基于Android P完整的解析一个APP从其图标被点击到其界面显示完毕的全过程。
1、概述
概括地来说,一个APP的
APP的启动过程是任何一个学习Android的人所必须了解的,这篇文章将会基于Android P完整的解析一个APP从其图标被点击到其界面显示完毕的全过程。
1、概述
概括地来说,一个APP的冷启动过程可以划分如下:
请求阶段
1.发起请求
2.解析Intent
3.创建ActivityRecord
4.分配Task
5.Pause前台activity
6.Resume请求的activity
进程启动阶段
7.AMS请求创建进程
8.Zygote fork进程
9.初始化 Runtime
10.注册进程到system_server
11.创建application
Activity初始化阶段
12.真正地start activity
13.加载activity
14.初始化窗口
Activity显示阶段
15.新建DecorView
16.新建ViewRootImpl
17.添加到Display
18.显示
注意,这里说的是APP冷启动的过程,如果不是冷启动,经过的阶段是上述阶段的一个子集。
2、发出启动Activity的请求
Android为了降低开发难度,屏蔽底层进程间通讯等细节,抽象出了四大组件。通过四大组件,我们可以方便、快捷地和其他组件进行交互,而不管目标组件是在哪个APP实现的、哪个进程中运行的。而一个APP的主Activity通常声明如下:
android.intent.action.MAIN:决定应用的入口Activity,也就是我们启动应用时首先显示哪一个Activity。
android.intent.category.LAUNCHER:表示activity应该被列入系统的启动器(launcher)(允许用户启动它)。Launcher是安卓系统中的桌面启动器,是桌面UI的统称。
凡是声明了上面filter的activity,都会被launcher解析出来,对应图标排列在桌面。这样,用户就可以通过点击的方式启动Activity了。
APP端启动activity流程是:
Acticity.startActivity(new Intent(this,target.class))
Acticity.startActivityForResult(...)
Instrumentation.execStartActivity(...)
IActivityManager.startActivity(...)
(为了简洁起见,上面过程省略一些重载的调用,以后调用过程同样如此)。
APP请求启动activity最重要的两个参数:Context和Intent。
Context指明我是谁。为了安全认证和管理,系统必须知道谁请求启动activity,而context里面包含着身份信息例如activity token等表明了我是谁,这样ActivityManagerService(以后简称AMS)才允许启动activity。
Intent指明我想做什么。上面的intent是显式intent,显式intent通常用在包内启动组件,如果是启动其他APP的组件,则通常用隐式intent。显式intent里面包含了一个ComponentName,ComponentName由包名 类名组成,可以唯一标识一个组件,系统通过ComponentName就可以找到要启动的组件。隐式intent通常通过Action来过滤出要启动的组件,这一点我们将在第3节中展开讲述。
在表明身份和说明意图后,最后通过Binder调用请求AMS启动目标activity
3、AMS解析Intent
本阶段的调用流程是:
AMS.startActivity(...)
AMS.startActivityAsUser(...)
ActivityStarter.startActivity(...)
ActivityStarter.startActivityMayWait(...)
ResolveInfo rInfo = ASS.resolveIntent(intent,uid,...)
PackageManagerService.resolveIntent()
ActivityInfo aInfo = ASS.resolveActivity(intent, rInfo...);
ActivityStarter.startActivty(intent,aInfo,...)
AMS解析Intent主要是通过PackageManagerService后面简称(PMS)来解析的,因为我们四大组件都是必须声明在AndroidManifest.xml文件中的(广播接收器允许动态注册)。Android这么做的原因上面也说过了,为了屏蔽进程间通讯细节,应用之间通过组件就可以交互,系统会在必要的时候拉起对方进程。在应用没起来之前,只有PMS知道应用都有哪些组件。应用四大组件的信息在应用安装的时候,就已经被PMS解析保存起来了。如果没有声明在AndroidManifest.xml文件中,那么AMS就无法获取目标组件的信息,对于显式intent,会抛出错误;对于隐式intent,也会启动失败。
解析出来的信息叫ResolveInfo,是一个容器类,里面包含了ActivityInfo,ServiceInfo,ProviderInfo等成员来表示四大组件的信息,activity和broadcast信息都是用ActivityInfo来表示的。这三个成员只有一个不为空,这里我们启动的是activity,所以ActivityInfo是不为空的。ActivityInfo包含了各种各样的activity信息,都是声明在AndroidManifest.xml文件中的,比较重要的包括launchMode、theme、screenOrientation等,其中还包含了ApplicationInfo,提供packageName、targetSdkVersion等重要信息。
额外说一句,这里的ActivityStarter.startActivityMayWait并不会等待,只有从AMS. startActivityMayWait调过来的才会真正等待activity起来,这个通常用在调试APP的启动过程。这个时候App还没起来,我们无法选择要调试的进程,这个就可以使用startActivityMayWait,App进程起来以后,会等我们设置断点,这样就可以第一时间调试了。命令如下:
adb shell am set-debug-app -w com.example.demo
4、创建ActivityRecord
本阶段的调用流程是:
ActivityStarter.startActivity(...)
ActivityRecord r = new ActivityRecord(callerApp,intent,aInfo,mSupervisor,...)
appToken = new Token(this, _intent);
setState(INITIALIZING, "ActivityRecord ctor");
在解析完intent以后,AMS会进一步的做各种的权限检查,包括没有找到目标组件的处理。随后就会新建一个ActivityRecord。ActivityRecord是AMS中保存activity信息的数据结构,AMS就是通过着一个结构来管理activity的,其主要包括的信息有:appToken、launchedFromPid、launchedFromUid、launchedFromPackage、userId、intent、componentName、requestCode、visible、taskAffinity、icon、logo、theme、processName、launchMode、fullscreen等信息。
在其构造函数中比较重要的工作是:
1)新建了AppToken,这是activity的唯一标识
2)ActivityRecord的状态初始化为INITIALIZING
到了这一步,表明activity相关档案已被建立,接下来要做的事情就是为该activity分配任务栈。
5、分配Task
本阶段的调用流程是:
ActivityStarter.startActivity(r,sourceRecord,true/* doResume */,...)
ActivityStarter.startActivityUnchecked(r,sourceRecord,doResume...)
ActivityRecord reusedActivity = getReusableIntentActivity()
result = setTaskFromReuseOrCreateNewTask(taskToAffiliate, topStack)
mTargetStack.startActivityLocked(mStartActivity, topFocused, newTask...)
mSupervisor.resumeFocusedStackTopActivityLocked(mTargetStack, mStartActivity,...)
startActivityUnchecked是本阶段的重点函数,需要详细分析
ActivityStarter.startActivityUnchecked(r, sourceRecord,doResume..){
//计算mLaunchFlags
computeLaunchingTaskFlags();
// 设置刚才在上面计算出来的mLaunchFlags
mIntent.setFlags(mLaunchFlags);
// 根据mLaunchFlags来查找是否有可重用的activity
ActivityRecord reusedActivity = getReusableIntentActivity();
if (reusedActivity != null) {
//如果找到了可重用的activity,需要清理掉原来的信息,并把当前启动的activity的信息拷贝进去
做清理和拷贝工作...
if((mLaunchFlags & FLAG_ACTIVITY_CLEAR_TOP) != 0 ||
isLaunchModeOneOf(LAUNCH_SINGLE_INSTANCE, LAUNCH_SINGLE_TASK)){
// 没必要新建实例,回调onNewIntent并将top移至前台
deliverNewIntent(top);
}
// 计算哪个task和activity要移至前台,必要时会进行task的清理工作
reusedActivity = setTargetStackAndMoveToFrontIfNeeded(reusedActivity);
return mMovedToFront ? START_TASK_TO_FRONT : START_DELIVERED_TO_TOP;
}
if (mStartActivity.resultTo == null && mInTask == null && !mAddingToTask
&& (mLaunchFlags & FLAG_ACTIVITY_NEW_TASK) != 0) {
newTask = true;
// 重用或者新建task
result = setTaskFromReuseOrCreateNewTask(taskToAffiliate, topStack);
}else if (mSourceRecord != null) {
// 不是新建task的,重用原activity的task
result = setTaskFromSourceRecord();
}
mTargetStack.startActivityLocked(mStartActivity, topFocused, newTask...);
if (mDoResume) {
// 将任务栈移至前台
mTargetStack.moveToFront("startActivityUnchecked");
// 开始resume
mSupervisor.resumeFocusedStackTopActivityLocked(...)
}
}
总体来说,这个阶段就是根据activty的启动模式找到合适的task来放置activity,如果找不到或者强制新建,就会新建一个。具体过程如下:
首先,根据launchMode和Intent中的FLAG_ACTIVITY_NEW_TASK等flag综合计算activity的启动模式,结果保存在mLaunchFlags中。计算的过程不仅要考虑目标activity的launchMode,也要考虑原来activity的launchMode和Intent中所带着的flag。例如原来activity的launchMode是LAUNCH_SINGLE_INSTANCE,那么新activity只能新建task。
调用getReusableIntentActivity()查找是否有可以重用的activity,这个只对LAUNCH_SINGLE_INSTANCE和LAUNCH_SINGLE_TASK或者FLAG_ACTIVITY_NEW_ TASK不为0有用的actvity,对于standard的activity,该方法永远返回null。查找的依据包括ActivityType、ComponentName和taskAffinity等。
如果成功找到了可以重用的activity,要进行清理工作,把原来activity的信息替换成现在activity的信息。例如同一个activity,两次启动的caller可能不同,要进行更新。同时,还要进行根据launchMode来进行task内的清理。例如LAUNCH_SINGLE_TASK的activity,如果此类所在的task上面有其它Activity,那么其它的Activity会被销毁。
找到了可以重用的activity,那么就相当于把原来的activty替换成现在的activty,也就不用新建task了。但是如果没有找到重用的activity,那么调用setTaskFromReuseOrCreateNewTask()尝试寻找可以重用的task,注意此时前提是mStartActivity.resultTo == null && mLaunchFlags & FLAG_ACTIVITY_NEW_TASK,即,如果我们调用startActivityForResult启动的activity,那么是不能新建task的,二者是不兼容的。没有找到重用的task,此时会新建一个TaskRecord,置于前台,然后把activityt也置于task中activity队列的最后(即前台)。如果是重用原activity的task,那么执行setTaskFromSourceRecord,首先也是把该task移到前台,然后把activity置于该task的activity队列的最后。
经过前面四步,actvity及其对应的task位置已经安排妥当,现在可以准备让这个activity开始工作了!那么这开工的第一步就是mTargetStack.startActivityLocked,这里面的工作主要是通知WindowManagerService(以后简称为WMS)准备activty的过渡动画和startingWindow。
至此,为activity分配task的工作已经完毕。
6、Pause前台activity
上面说到了为activity分配了任务栈之后,会调用resumeFocusedStackTopActivityLocked准备进行其resume。调用流程如下:
ActivityStackSupervisor.resumeFocusedStackTopActivityLocked(targetStack,target...)
ActivityStack.resumeTopActivityUncheckedLocked(...)
ActivityStack.resumeTopActivityInnerLocked(...)
// next是要resume的activity
final ActivityRecord next = topRunningActivityLocked(true /* focusableOnly */);
next.launching = true; // 将activity的状态置为launching
// pause所有的任务栈
boolean pausing = mStackSupervisor.pauseBackStacks(userLeaving, next, false);
// pause当前栈的activity
if (mResumedActivity != null && mResumedActivity != next) {
pausing |= startPausingLocked(userLeaving, false, next, false);
}
if (pausing && !resumeWhilePausing) {
//第一次进来的时候会在这里返回
return true;
}
if (next.app != null && next.app.thread != null {
//进行resume操作 {1}
}else{
// 这里会启动app进程 {2}
ActivityStackSupervisor.startSpecificActivityLocked(next, true, true);
}
因为之前已经新的activity做好入栈工作了,就是放在栈顶,所以通过topRunningActivityLocked可以找到它。如果当前要resume的activity不是之前已经resume的activity,那么必须将pause之前的activity才行,具体工作分两部分完成:
pause其他所有已经focus的任务栈的mResumedActivity
pause当前任务栈mResumedActivity
两步都会调到核心函数是:ActivityStack.startPausingLocked
粗略介绍如下:
ActivityStack.startPausingLocked()
ActivityRecord prev = mResumedActivity;
// 变更ActivityStack中pauseActivity的记录
mPausingActivity = prev;
mLastPausedActivity = prev;
prev.setState(PAUSING, "startPausingLocked");
if (prev.app != null && prev.app.thread != null) {
// 通知APP执行pause操作
mService.getLifecycleManager().scheduleTransaction(prev.app.thread, prev.appToken,
PauseActivityItem.obtain(prev.finishing, userLeaving,
prev.configChangeFlags, pauseImmediately));
}
// 监控APP是否pause超时,时间只有500ms
schedulePauseTimeout(prev);
上面代码很简单,就是变更一下mPausingActivity的状态,然后通过LifecycleManager来通知APP进行pause操作,最后设置一下超时msg,至此完成了所有resumedActivity的pause工作。
7、Resume请求的activity
接上面,当APP pause以后,会回调activityPaused通知AMS,然后AMS会执行completePauseLocked。该函数也会调用resumeTopActivityInnerLocked。这一次,由于所有resumedActivity都已经paused了,所以返回的结果pausing为false,所以可以继续进行目标activity的resume工作。
不过因为目标App的进程还没起来,所以上面的resumeTopActivityInnerLocked函数并没有真正做了resume操作,只是将target activtiy置为launching状态,通知前台的activity进行pause,然后调用startSpecificActivityLocked来启动进程,进程起来以后会再一次调到resumeTopActivityInnerLocked继续resume操作。
如果不是冷启动,在通知前台activity pause以后,会在{1}(第6节中的resumeTopActivityInnerLocked函数)中进行resume的工作,然后通知target app进行resume操作。所以在时序上应该是前台activity pause --> 目标activity resume,可以用图显示如下:
图中虚线表示跨进程调用,同一种颜色表示同一个进程,启动时的入口是startActivtiyUncheck,app pause完了以后的入口是completePauseLocked。
准备阶段总结
其实各节的小标题已经说的很清楚了,做了一下事情:
1.发起请求,主要是是表明身份和目的
2.解析Intent,验证合法性
3.创建ActivityRecord,建立activity的档案记录
4.分配Task,安排activity的位置
5.Pause前台activity,先下
6.Resume请求的activity,后上
除了第1点是在APP进程进行的,其实都是在SystemServer进程进行的。