请求并创建目标Activity进程
Android四大组件源码实现详解系列博客目录:
Android应用进程创建流程大揭秘
[Android四大组件之bindService源码实现详解
Android四大组件之Activity启动流程源码实现详解概要
Activity启动流程(一)发起端进程请求启动目标Activity
Activity启动流程(二)system_server进程处理启动Activity请求
Activity启动流程(三)-Activity Task调度算法复盘分析
Activity启动流程(四)-Pause前台显示Activity,Resume目标Activity
Activity启动流程(五)请求并创建目标Activity进程
Activity启动流程(六)注册目标Activity进程到system_server进程以及创建目标Activity进程Application
Activity启动流程(七)初始化目标Activity并执行相关生命周期流程
本篇博客编写思路总结和关键点说明:

为了更加方便的读者阅读博客,通过导读思维图的形式将本博客的关键点列举出来,从而方便读者取舍和阅读!
引言
还记得我们在前面博客Activity启动流程(四)Pause前台显示Activity,Resume目标Activity以及更前面一系列博客中做的艰苦卓越的斗争吗!这些战役之惨烈,战况之持久前所未有!虽然过程是疼苦的,但是战果也是显赫和令人满意的,通过上述战役我们不仅消灭了启动目标Activity落上的各种拦路虎圆满完成了启动目标Activity请求阶段的重任,并且更是取得了如下的阶段性成果(不是老王卖瓜自卖自夸啊!):
- 为目标Activity创建了ActivityRecord,从而在AMS中目标Activity有了相关的信息档案
- 为目标Activity安排好了TaskRecord任务栈以及Stack栈,并且为其在上述结构中安排好了合适的位置
- Pause好了前台相关的Activity
但是上述的努力远远不过,如果我们把Activity的启动比喻为万里长征的话,我们还只完成了Activity的请求阶段,在本篇博客中我将会带领小伙们一起分析目标Activity所属进程的启动流程,其主要包括如下的相关子流程:
- AMS请求Zygote创建目标Activity所属进程
- Zygote孵化目标Activity所属进程
- 初始化目标Activity所属进程
- 注册目标Activity所属进程到system_server
- 目标Activity所属进程创建Application实例对象
注意:本篇的介绍是基于Android 7.xx平台为基础的,其中涉及的代码路径如下:
frameworks/base/services/core/java/com/android/server/am/
--- ActivityManagerService.java
--- ProcessRecord.java
--- ActivityRecord.java
--- ActivityResult.java
--- ActivityStack.java
--- ActivityStackSupervisor.java
--- ActivityStarter.java
--- TaskRecord.java
frameworks/base/services/core/java/com/android/server/pm/
--- PackageManagerService.java
frameworks/base/core/java/android/content/pm/
--- ActivityInfo.java
frameworks/base/core/java/android/app/
--- IActivityManager.java
--- ActivityManagerNative.java (内部包含AMP)
--- ActivityManager.java
--- AppGlobals.java
--- Activity.java
--- ActivityThread.java(内含AT)
--- LoadedApk.java
--- AppGlobals.java
--- Application.java
--- Instrumentation.java
--- IApplicationThread.java
--- ApplicationThreadNative.java (内部包含ATP)
--- ActivityThread.java (内含ApplicationThread)
--- ContextImpl.java
frameworks/base/core/java/com/android/internal/os/*
---ZygoteConnection.java
---Zygote.java
---ZygoteInit.java
---RuntimeInit.java
frameworks/base/core/java/android/os/Process.java
frameworks/base/core/jni/include/android_runtime/AndroidRuntime.h
frameworks/base/core/jni/AndroidRuntime.cpp
frameworks/base/cmds/app_process/App_main.cpp
libcore/libart/src/main/java/java/lang/Daemons.java
libcore/dalvik/src/main/java/dalvik/system/ZygoteHooks.java
art/runtime/native/dalvik_system_ZygoteHooks.cc
art/runtime/runtime.cc
frameworks/base/core/jni/com_android_internal_os_Zygote.cpp
bionic/libc/bionic/fork.cpp
bionic/libc/bionic/pthread_atfork.cpp
art/runtime/native/dalvik_system_ZygoteHooks.cc
art/runtime/runtime.cc
art/runtime/signal_catcher.cc
并且在后续的源码分析过程中为了简述方便,会将做如下简述:
- ApplicationThreadProxy简称为ATP
- ActivityManagerProxy简称为AMP
- ActivityManagerService简称为AMS
- ActivityManagerNative简称AMN
- ApplicationThreadNative简称ATN
- PackageManagerService简称为PKMS
- ApplicationThread简称为AT
- ActivityStarter简称为AS,这里不要和ActivityServices搞混淆了
- ActivityStackSupervisor简称为ASS
在正式开始今天博客相关源码分析前,还是先奉上Activity启动调用的整体时序图以便小伙们先从整体上有个清晰的概括,然后再从细节开撸!

一. Android应用进程创建流程整体概括
关于Android应用进程创建我有写过一个专门的博客Android应用进程创建流程大揭秘 ,但是是以Android 9为原型开展的,虽然创建流程和Android 7的源码逻辑相差不是很大,但是其中还是有些差异的,并且本文是一个一系列的专题文章,所以感觉还是关于这部分还是花点时间对Android 7的版本重新梳理,花点时间重新来一遍。在这里先来奉上一张Android应用冷启动进程创建的完整流程图,内容比较多,有兴趣的可以跟着图走一圈看看。

上图是Android应用进程创建的整个详细流程图,东西有点多!好吗,我们还是简化一下,从整体架构上入手来分析,这样可能会容易一些,也容易开啃一些!

上面的图示涉及到了几个中要的角色,下面我们来分别介绍介绍:
-
发起进程端:这里的的发起端通常指代我们的桌面Launhcer,如果是从某App内启动远程进程,则发送进程便是该App所在进程。发起进程先通过Binder关联AMS服务发送消息给system_server进程
-
system_server进程:system_server进程在收到启动Activity/Service以后,其中的AMS服务经过一系列的处理,最终调用Process.start开启进程创建的流程,在这个阶段主要是和zygote进程建立socket连接,将要创建的进程的相关数据发送过去,在这个阶段system_server对于发起端进程来说扮演的是服务端,而对于zygote进程来说system_server扮演的客户端角色
-
zygote进程:通过前面的篇章分析我们知道,在Android终端启动过程中,Zygote进程就已经早早的启动了,然后会创建LocalServerSocket服务端等待客户端的请求,然后在runSelectLoop开启了一个死循环静候accept客户端的连接,当system_server进程收到发起端进程的请求之后,就会创建LocalSocket和zygote进程的LocalServerSocket通信从而进行send/recev数据了,此时将相关数据发送给LocalServerSocket告知我要创建一个进程。进程fork完成之后,返回结果给system_sever进程的AMS。
-
新建进程:Zygote进程把进程fork出来之后,需要做进程的初始化操作,比如设置进程异常的捕获方式,开始Binder线程池等等,最后进入了ActivityThread的main方法,从而到这里一个有血有肉的进程正式被启动了
二. AMS请求Zygote创建目标Activity所属进程
在前面的博客Activity启动流程(四)- Pause前台显示Activity,Resume目标Activity最后我们知道,在Resume目标Activity时如果目标Activity所在的App进程还没有创建,则会调用startSpecificActivityLocked来发起创建目标进程,让我们朝着目标发起猛烈跑火开干!
注意,注意,注意:
Activity的启动过程中,存在一种特殊情况就是假如目标Activity在AndroidManifest.xml中配置了android:process相关的属性,那怕目标Activity所属进程已经创建了依然会走startSpecificActivityLocked流程创建Activity专有的Process,这个一定要注意!
<activity
android:name="com.example.test.BActivity"
android:process=":process"
>
</activity>
2.1 ASS.startSpecificActivityLocked(…)
当我们第一次启动目标Activity时,其所对应的进程肯定还没有创建则会进入此方法中。此方法命名很霸气啊Specific,那就让我们看看它有啥Specific的,难不成它是内裤外穿不成。
//[ActivityStackSupervisor.java]
void startSpecificActivityLocked(ActivityRecord r, //目标Activity相关信息
boolean andResume, //此时传递的参数值为true
boolean checkConfig) //此时传递的参数值为false
{
//检查目标Activity所属的应用进程是否创建,假如是冷启动的情况当然是木有创建的
ProcessRecord app = mService.getProcessRecordLocked(r.processName,
r.info.applicationInfo.uid, true);
r.task.stack.setLaunchTime(r);
/*
当目标Activity对应的App进程和进程对应的ApplicationThread被创建时,进入此分支,此时的我们是木有机会了
*/
if (app != null && app.thread != null) {
//
try {
/*
判断此Activity是否携带FLAG_MULTIPROCESS或者其对应的进程名是"android"
那"android"对应的进程到底是啥呢,其实它是system_server,这个是在zygote孵化它时指定的
*/
if ((r.info.flags&ActivityInfo.FLAG_MULTIPROCESS) == 0
|| !"android".equals(r.info.packageName)) {
app.addPackage(r.info.packageName, r.info.applicationInfo.versionCode,
mService.mProcessStats);
}
//正式启动目标Activity,开启生命周期
realStartActivityLocked(r, app, andResume, checkConfig);
return;
} catch (RemoteException e) {
...
}
}
//调用AMS开启startProcess处理流程
mService.startProcessLocked(r.processName, r.info.applicationInfo, true, 0,
"activity", r.intent.getComponent(), false, false, true);//详见章节2.2
}
分析一番下来,该方法并没有内裤外穿仅仅是做了一些常规检查和处理,其主要逻辑如下:
- 在开启创建目标Activity对应的进程之前,再次检查目标Activity对应的ProcessRecord是否创建,如果已经创建则直接启动目标Activity
- 如若木有,则交由AMS的startProcessLocked方法继续处理下一流程
2.2 AMS.startProcessLocked(String processName,…)
在AMS中startProcessLocked方法被重载了很多次,所以在接下来的分析中一定得注意不要搞错了!而且startProcessLocked涉及的参数也很多,说实话其中有些参数的作用很多我也没有整明白,但是这并不妨碍我们对整体流程的分析!
//[ActivityManagerService.java]
final ProcessRecord startProcessLocked(String processName,//目标Activity对应的进程名,通常上是包名
ApplicationInfo info, //Activity所属App的Application信息
boolean knownToBeDead, //这个参数具体意义不明,传递过来的值为true
int intentFlags,//参数的值为0
String hostingType, //这个参数意义不明,参数值为"activity"
ComponentName hostingName, //启动目标Activity时解析intent时得到的ComponentName值
boolean allowWhileBooting,//是否允许在开启启动时,参数未false
boolean isolated, //参数为false,这个值的意思是该进程是否是以隔离模式启动
boolean keepIfLarge) //参数意义不明,传递的值为true
{
return startProcessLocked(processName, info, knownToBeDead, intentFlags, hostingType,
hostingName, allowWhileBooting, isolated, 0 /* isolatedUid */, keepIfLarge,
null /* ABI override */, null /* entryPoint */, null /* entryPointArgs */,
null /* crashHandler */);
}
此方法很懒,啥也没有干直接调用了另外一个重载的startProcessLocked方法!
//[ActivityManagerService.java]
//这个参数够多的,真是够多的
final ProcessRecord startProcessLocked(String processName, //目标Activity对应的进程名,通常上是包名
ApplicationInfo info,//Activity所属App的Application信息
boolean knownToBeDead, //这个参数具体意义不明,传递过来的值为true
int intentFlags, //参数的值为0
String hostingType, //这个参数意义不明,参数值为"activity"
ComponentName hostingName,//启动目标Activity时解析intent时得到的
boolean allowWhileBooting, //是否允许在开启启动时,参数未false
boolean isolated, //参数为false,这个值的意思是该进程是否是隔离的
int isolatedUid, //指定隔离进程的UID
boolean keepIfLarge,//参数意义不明,传递的值为true
String abiOverride, //是否覆盖目标Activity所属app安装时的abi,abi通常影响so库的选择,取值为null
String entryPoint, //参数意义不明,取值为null
String[] entryPointArgs, //参数意义不明,取值为null
Runnable crashHandler) //参数意义不明,取值为null
{
...
ProcessRecord app;
if (!isolated) {
//当不是隔离进程时
/*
获取AMS中是否存在目标Activity所属进进程的ProcessRecord记录,在非isolated模式下AMS可以重用先前的ProcessRecord记录
这里小伙们肯定有个疑问就是为啥startSpecificActivityLocked中检查了一次,这里又检查一次,
而和startSpecificActivityLocked处理不同的是这里并没有因为ProcessRecord的存在而停止后续的脚步呢
好吗,此处的逻辑感觉有点不是很清晰啊
*/
app = getProcessRecordLocked(processName, info.uid, keepIfLarge);
// 启动参数中,带有FLAG_FROM_BACKGROUND标志,表示进程需要后台启动
if ((intentFlags & Intent.FLAG_FROM_BACKGROUND) != 0) {
// 后台启动一个BadProcess,直接退出
if (mAppErrors.isBadProcessLocked(info)) {
return null;
}
} else {
//清理进程Crash消息
mAppErrors.resetProcessCrashTimeLocked(info);
if (mAppErrors.isBadProcessLocked(info)) {
// 前台启动,则需要将宿主进程从坏的进程中剔除
mAppErrors.clearBadProcessLocked(info);
if (app != null) {
app.bad = false;
}
}
}
} else {
app = null;
}
/*
此处涉及到了超自然现象,看注释的意思是对于大小核的架构的设备的话,此时使用cpuset将前台任务迁移到大核上
这个已经超过本人的能力范畴之外了,pass
*/
nativeMigrateToBoost();
mIsBoosted = true;
...
// 当进程已经被分配了PID时
if (app != null && app.pid > 0) {
// 进程还处于启动的过程中
if ((!knownToBeDead && !app.killed) || app.thread == null) {
app.addPackage(info.packageName, info.versionCode, mProcessStats);
return app;
}
/*
这个地方要怎么理解呢,可能前面目标Activity所属App进程被创建过
但是已经over了再启动时需要对其做一些清理工作
*/
killProcessGroup(app.uid, app.pid);
handleAppDiedLocked(app, true, true);
}
String hostingNameStr = hostingName != null
? hostingName.flattenToShortString() : null;
if (app == null) {
//在AMS中创建ProcessRecord信息记录
app = newProcessRecordLocked(info, processName, isolated, isolatedUid);//详见章节2.3
if (app == null) {
return null;
}
//设置crashHandler
app.crashHandler = crashHandler;
} else {
app.addPackage(info.packageName, info.versionCode, mProcessStats);
}
// 如果系统还未启动,则需要将待启动进程先保持住,等系统启动后,再来启动这些进程
//final ArrayList<ProcessRecord> mProcessesOnHold = new ArrayList<ProcessRecord>();
if (!mProcessesReady
&& !isAllowedWhileBooting(info)
&& !allowWhileBooting) {
if (!mProcessesOnHold.contains(app)) {
mProcessesOnHold.add(app);
}
return app;
}
//又调用另外一个重载的startProcessLocked
startProcessLocked(
app, hostingType, hostingNameStr, abiOverride, entryPoint, entryPointArgs);//详见章节2.4
return (app.pid != 0) ? app : null;
}
这里可以看到上述方法的处理逻辑主要体现在三个方面:
-
首先判断是否是以isolated模式启动的目标Activity所属App进程,那么这个solated模式是什么意思呢,网上说通过isolated来判断要启动的进程是否是隔离的,当为true时意味着这是一个隔离的进程(虽然这么说,但是本人是没有搞明白,这个隔离到底隔离的是啥,如果对此处有比较深入的小伙们可以告诉我)。
对于隔离的进程而言,每次启动都是独立的,不能复用已有的进程信息。如果要启动一个非隔离的进程,那么就需要区分进程是在前台启动还是后台启动,这是用户体验相关的设计。在AMS中维护了一个badProcesses的结构体,用于保存一些“坏进程”,什么才是“坏进程”呢?如果一个进程在一分钟内连续崩溃两次,那就变成了一个“坏进程”。对于后台启动的进程而言(即启动参数中带有FLAG_FROM_BACKGROUND标识),如果进程崩溃了,会造成用户使用的困惑,因为进程崩溃时,会弹出一个对话框,而后台启动的进程是没有任何操作界面的,这时候弹一个框,用户会觉得自己什么都没干,却弹出了一个对话框。所以,后台启动一个“坏进程”时,会直接退出。
当进程是在前台启动时,即便是一个"坏进程",那也应该宽恕这个进程以前的不良记录,因为这通常是用户通过界面主动要唤起的进程。本着用户是上帝的原则,还是得让用户达到启动进程的目的,即便这个进程可能再次崩溃。 -
接着判断目标Activity所对应的ProcessRecord是否创建,如果还没有创建这时候会调用AMS.newProcessRecord来创建一个新的ProcessRecord
-
接着调用AMS的另一个重载方法startProcessLocked继续目标Activity对应进程创建的处理流程
2.3 AMS.newProcessRecordLocked(…)在AMS中创建目标Activity对应的Process记录
//[ActivityManagerService.java]
final ProcessRecord newProcessRecordLocked(ApplicationInfo info, String customProcess,
boolean isolated, int isolatedUid) {
String proc = customProcess != null ? customProcess : info.processName;
//电量统计相关
BatteryStatsImpl stats = mBatteryStatsService.getActiveStatistics();
final int userId = UserHandle.getUserId(info.uid);
int uid = info.uid;
//根据isolated模式调整uid,至于怎么具体调整的这块我还没有整明白,就忽略了
if (isolated) {
if (isolatedUid == 0) {
int stepsLeft = Process.LAST_ISOLATED_UID - Process.FIRST_ISOLATED_UID + 1;
while (true) {
if (mNextIsolatedProcessUid < Process.FIRST_ISOLATED_UID
|| mNextIsolatedProcessUid > Process.LAST_ISOLATED_UID) {
mNextIsolatedProcessUid = Process.FIRST_ISOLATED_UID;
}
uid = UserHandle.getUid(userId, mNextIsolatedProcessUid);
mNextIsolatedProcessUid++;
if (mIsolatedProcesses.indexOfKey(uid) < 0) {
break;
}
stepsLeft--;
if (stepsLeft <= 0) {
return null;
}
}
} else {
uid = isolatedUid;
}
}
//根据传入的参数和获取到的uid信息构建ProcessRecord
final ProcessRecord r = new ProcessRecord(stats, info, proc, uid);
if (!mBooted && !mBooting
&& userId == UserHandle.USER_SYSTEM
&& (info.flags & PERSISTENT_MASK) == PERSISTENT_MASK) {
r.persistent = true;
}
addProcessNameLocked(r);//将ProcessRecord信息在AMS中存储起来
/*************************************************/
private final void addProcessNameLocked(ProcessRecord proc) {
ProcessRecord old = removeProcessNameLocked(proc.processName, proc.uid);
if (old == proc && proc.persistent) {
} else if (old != null) {
}
UidRecord uidRec = mActiveUids.get(proc.uid);
if (uidRec == null) {
uidRec = new UidRecord(proc.uid);
mActiveUids.put(proc.uid, uidRec);
noteUidProcessState(uidRec.uid, uidRec.curProcState);
enqueueUidChangeLocked(uidRec, -1, UidRecord.CHANGE_ACTIVE);
}
proc.uidRecord = uidRec;
uidRec.numProcs++;
mProcessNames.put(proc.processName, proc.uid, proc);
if (proc.isolated) {
mIsolatedProcesses.put(proc.uid, proc);
}
}
/*************************************************/
return r;
}
该方法无需动脑,逻辑也比较简单:
- 根据isolated的情况,调试uid的取值(这个地方先忽略了)
- 根据ApplicaitonInfo,uid等相关参数创建一个ProcessRecord,并将ProcessRecord加入AMS的管理范围,与此同时,与进程相关的电量统计也是在这一步被绑定上的
2.4 AMS.startProcessLocked(ProcessRecord app,…)依然范特西
注意将此处的方法startProcessLocked和2.2章节中的区分开来,其参数是完全不同的!此时表示目标Activity对应的rocessRecord已经构建好了,但是仅此而已,此时只是在AMS中有了相关Process的记录但是对应的进程是没有创建的,所以革命尚未成功同志仍需努力啊!继续干!
//[ActivityManagerService.java]
private final void startProcessLocked(ProcessRecord app,
String hostingType,
String hostingNameStr,
String abiOverride,
String entryPoint,
String[] entryPointArgs) {
if (app.pid > 0 && app.pid != MY_PID) {
// 新启动进程了,清除超时消息设置
synchronized (mPidsSelfLocked) {
mPidsSelfLocked.remove(app.pid);
mHandler.removeMessages(PROC_START_TIMEOUT_MSG, app);
}
app.setPid(0);
}
// 在HoldProcess中移除app
mProcessesOnHold.remove(app);
updateCpuStats();
...
try {
try {
// gid与进程的访问权限相关处理
//获取userId,通常为0
final int userId = UserHandle.getUserId(app.uid);
// 检查应用的package状态
AppGlobals.getPackageManager().checkPackageStartable(app.info.packageName, userId);
} catch (RemoteException e) {
}
int uid = app.uid;
int[] gids = null;
int mountExternal = Zygote.MOUNT_EXTERNAL_NONE;
if (!app.isolated) {
...
}
...
int debugFlags = 0;
// debugFlags相关的处理,与进程的调试选项相关处理,不过多分析
//我们为了调试通常会在AndroidManifest中配置android:debuggable="true"的参数
if ((app.info.flags & ApplicationInfo.FLAG_DEBUGGABLE) != 0) {
debugFlags |= Zygote.DEBUG_ENABLE_DEBUGGER;
debugFlags |= Zygote.DEBUG_ENABLE_CHECKJNI;
}
//primaryCpuAbi的值在应用安装的时候已经确定了,这个牵涉到应用的安装解析apk包的问题了
String requiredAbi

深入解析Android应用进程从AMS请求Zygote创建、进程初始化、注册system_server及Application实例创建的详细流程,涵盖Activity启动的请求到目标进程诞生的关键步骤。
最低0.47元/天 解锁文章
4447

被折叠的 条评论
为什么被折叠?



