前言
到上一篇结束,我们已经概括的梳理出系统启动过程中创建的主要进程,本篇我们就来看看与我们息息相关的APP进程从无到有是怎么诞生的。本篇内容也是系统启动章节的最后一篇。
既然是关注从无到有,那么我们就明确一点,本篇讲述的是Launcher冷启动APP的主线流程,着重关注Zygote和SystemServer如何配合创建APP进程的过程。AMS(ActivitiyManagerService)和ATMS(ActivityTaskManagerService)对于Activity的管理逻辑、启动之前的条件逻辑等我们都暂时略过。
以下是Zygote、SystemServer、APP进程之间的通信关系概括图。
Launcher
Launcher在收到点击图标事件后会辗转来到Activity.startActivity(),然后间接调用以下函数。
java
// frameworks/base/core/java/android/app/Instrumentation.java
public ActivityResult execStartActivity(
Context who, IBinder contextThread, IBinder token, Activity target,
Intent intent, int requestCode, Bundle options) {
... ...
// ① 通过binder跨进程调用ATMS的接口
int result = ActivityTaskManager.getService().startActivity(whoThread,
who.getOpPackageName(), who.getAttributionTag(), intent,
intent.resolveTypeIfNeeded(who.getContentResolver()), token,
target != null ? target.mEmbeddedID : null, requestCode, 0, null, options);
... ...
}
① ActivityTaskManager.getService()通过ServiceManager获取到ActivityTaskManagerService的对外binder接口。其中whoThread参数为IApplicationThread.Stub.Proxy,又是一个binder接口,是本进程(APP进程)的对外接口。在我们这个场景下whoThread接口就是Launcher应用的接口。
AMS
这里虽然标题是AMS,但其中很大一部分逻辑由ATMS负责,我们可以概括的认为ATMS属于AMS的一部分。
java
// frameworks/base/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
private int startActivityAsUser(IApplicationThread caller, String callingPackage,
@Nullable String callingFeatureId, Intent intent, String resolvedType,
IBinder resultTo, String resultWho, int requestCode, int startFlags,
ProfilerInfo profilerInfo, Bundle bOptions, int userId, boolean validateIncomingUser) {
... ...
// 构造ActivityStarter
return getActivityStartController().obtainStarter(intent, "startActivityAsUser")
// caller(IApplicationThread.Stub.Proxy):发起进程(Launcher)的对外接口
.setCaller(caller)
... ...
// resultTo(ActivityRecord.Token):流程发起Activity的Token
.setResultTo(resultTo)
... ...
.execute();
}
接下来的流程主要任务是处理Activity相关的信息和数据,经过ActivityStarter、Task、ActivityTaskSupervisor等类的构建和检查,最终启动信息被打包成一个带Runnable的msg由ATMS投递到Looper中,而这个Runnable中携带一个函数指针指向ActivityManagerService的startProcess函数。
java
// frameworks/base/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
void startProcessAsync(ActivityRecord activity, boolean knownToBeDead, boolean isTop, String hostingType) {
... ...
final Message m = PooledLambda.obtainMessage(ActivityManagerInternal::startProcess,
mAmInternal, activity.processName, activity.info.applicationInfo, knownToBeDead,
isTop, hostingType, activity.intent.getComponent());
mH.sendMessage(m);
... ...
}
下面的流程从消息队列取出msg执行Runnable函数开始,主要任务是处理启动进程相关信息和数据。流程比较长,且过程中的函数参数非常多,我们只关注几个重要的节点即可,别忘记我们的初衷:着重创建流程中跨进程的交互逻辑。
流程辗转来到以下函数,startProcess函数开启最后的请求创建过程,且返回创建结果。后面紧跟handleProcessStartedLocked函数处理创建结果。我们先继续往下跟,待会儿再回来看看后续处理流程。
java
// frameworks/base/services/core/java/com/android/server/am/ProcessList.java
boolean startProcessLocked(HostingRecord hostingRecord, String entryPoint, ProcessRecord app, int uid, int[] gids, int runtimeFlags, int zygotePolicyFlags, int mountExternal, String seInfo, String requiredAbi, String instructionSet, String invokeWith, long startUptime, long startElapsedTime) {
... ...
final Process.ProcessStartResult startResult = startProcess(hostingRecord,
entryPoint, app,
uid, gids, runtimeFlags, zygotePolicyFlags, mountExternal, seInfo,
requiredAbi, instructionSet, invokeWith, startUptime);
// 获得创建结果后,处理后续流程
handleProcessStartedLocked(app, startResult.pid, startResult.usingWrapper,
startSeq, false);
... ...
}
private Process.ProcessStartResult startProcess(HostingRecord hostingRecord, String entryPoint, ProcessRecord app, int uid, int[] gids, int runtimeFlags, int zygotePolicyFlags, int mountExternal, String seInfo, String requiredAbi, String instructionSet, String invokeWith, long startTime) {
... ...
if (hostingRecord.usesWebviewZygote()) {
startResult = startWebView(...);
} else if (hostingRecord.usesAppZygote()) {
startResult = appZygote.getProcess().start(...);
} else {
// 启动普通APP进程,上面两个分支都是通过特殊的Zygote启动相应进程
startResult = Process.start(...);
}
}
又经过一连串的"函数轰炸",终于它来了!我们看到了Socket的IO操作。
java
// frameworks/base/core/java/android/os/ZygoteProcess.java
private Process.ProcessStartResult attemptZygoteSendArgsAndGetResult(
ZygoteState zygoteState, String msgStr) throws ZygoteStartFailedEx {
try {
final BufferedWriter zygoteWriter = zygoteState.mZygoteOutputWriter;
final DataInputStream zygoteInputStream = zygoteState.mZygoteInputStream;
// 写入请求数据
zygoteWriter.write(msgStr);
// 发送数据
zygoteWriter.flush();
Process.ProcessStartResult result = new Process.ProcessStartResult();
// 阻塞,读取结果
result.pid = zygoteInputStream.readInt();
result.usingWrapper = zygoteInputStream.readBoolean();
if (result.pid < 0) {
throw new ZygoteStartFailedEx("fork() failed");
}
return result;
} catch (IOException ex) {
zygoteState.close();
Log.e(LOG_TAG, "IO Exception while communicating with Zygote - "
+ ex.toString());
throw new ZygoteStartFailedEx(ex);
}
}
Zygote
还记得吗,前面的章节我们讲到Zygote启动后就在runSelectLoop函数中等待Socket消息,来回顾一下:
java
// frameworks/base/core/java/com/android/internal/os/ZygoteServer.java
Runnable runSelectLoop(String abiList) {
... ...
while (true) {
... ...
// pollReturnValue返回值:
// 为0:没有文件描述符可用,即没有需要处理的消息
// 为正:有文件描述符可用,即需要处理消息
// 为负:出现错误
pollReturnValue = Os.poll(pollFDs, pollTimeoutMs);
... ...
if (pollReturnValue == 0) {
// 没有消息,继续循环等待
... ...
} else {
... ...
// 需要处理消息
final Runnable command = connection.processCommand(this, multipleForksOK);
... ...
}
}
}
java
// frameworks/base/core/java/com/android/internal/os/ZygoteConnection.java
Runnable processCommand(ZygoteServer zygoteServer, boolean multipleOK) {
... ...
// 间接调用系统调用fork
pid = Zygote.forkAndSpecialize(parsedArgs.mUid, parsedArgs.mGid,
parsedArgs.mGids, parsedArgs.mRuntimeFlags, rlimits,
parsedArgs.mMountExternal, parsedArgs.mSeInfo, parsedArgs.mNiceName,
fdsToClose, fdsToIgnore, parsedArgs.mStartChildZygote,
parsedArgs.mInstructionSet, parsedArgs.mAppDataDir,
parsedArgs.mIsTopApp, parsedArgs.mPkgDataInfoList,
parsedArgs.mAllowlistedDataInfoList, parsedArgs.mBindMountAppDataDirs,
parsedArgs.mBindMountAppStorageDirs);
... ...
if (pid == 0) {
// 新的APP进程中
... ...
return handleChildProc(parsedArgs, childPipeFd, parsedArgs.mStartChildZygote);
} else {
// Zygote进程中
... ...
handleParentProc(pid, serverPipeFd);
return null;
}
}
新的进程创建完毕之后,程序"分叉",先看新进程怎么走。
java
// frameworks/base/core/java/com/android/internal/os/ZygoteConnection.java
private Runnable handleChildProc(ZygoteArguments parsedArgs,
FileDescriptor pipeFd, boolean isZygote) {
// 关闭socket连接
closeSocket();
//设置进程名
Zygote.setAppProcessName(parsedArgs, TAG);
if (parsedArgs.mInvokeWith != null) {
// ① 处理Wrapper方式启动的进程的后续流程
WrapperInit.execApplication(parsedArgs.mInvokeWith,
parsedArgs.mNiceName, parsedArgs.mTargetSdkVersion,
VMRuntime.getCurrentInstructionSet(),
pipeFd, parsedArgs.mRemainingArgs);
throw new IllegalStateException("WrapperInit.execApplication unexpectedly returned");
} else {
if (!isZygote) {
// ② 处理普通APP进程的后续流程
return ZygoteInit.zygoteInit(parsedArgs.mTargetSdkVersion,
parsedArgs.mDisabledCompatChanges,
parsedArgs.mRemainingArgs, null /* classLoader */);
} else {
// ③ 处理特殊zygote孵化的进程的后续流程,例如:WebViewZygote孵化的进程
return ZygoteInit.childZygoteInit(
parsedArgs.mRemainingArgs /* classLoader */);
}
}
}
① 在某些分析或开发场景下,需要由分析工具来启动对应的APP进程(方便读取内存等进程相关数据),而不是Zygote来启动,因此,Android提供这种Wrapper方式来满足此需求。
② 这里的后续流程和SystemServer的创建就一样了,唯一不同的是传入参数为android.app.ActivityThread,那么接下来就会反射调用ActivityThread的main函数,接着就是进入Looper.loop()循环,APP进程就这样跑起来了。
③ WebViewZygote孵化WebView进程后的处理流程。WebViewZygote主要用于提升WebView使用体验,加快启动速度、减少内存占用、与APP进程隔离等。
再来看另一条岔路,Zygote进程在创建新进程之后做了什么?
java
// frameworks/base/core/java/com/android/internal/os/ZygoteConnection.java
private void handleParentProc(int pid, FileDescriptor pipeFd) {
... ...
// 通过socket连接回写pid和是否使用wrapper方式启动
mSocketOutStream.writeInt(pid);
mSocketOutStream.writeBoolean(usingWrapper);
... ...
}
回写到哪里去了?当然是与Zygote建立socket通信的System Server。前面我们分析到最后SystemServer发出请求时是在attemptZygoteSendArgsAndGetResult函数中。
java
// frameworks/base/core/java/android/os/ZygoteProcess.java
private Process.ProcessStartResult attemptZygoteSendArgsAndGetResult(
ZygoteState zygoteState, String msgStr) throws ZygoteStartFailedEx {
... ...
// 阻塞,读取结果
result.pid = zygoteInputStream.readInt();
result.usingWrapper = zygoteInputStream.readBoolean();
if (result.pid < 0) {
throw new ZygoteStartFailedEx("fork() failed");
}
return result;
} catch (IOException ex) {
zygoteState.close();
Log.e(LOG_TAG, "IO Exception while communicating with Zygote - "
+ ex.toString());
throw new ZygoteStartFailedEx(ex);
}
}
将pid和usingWrapper打包进ProcessStartResult,然后一路返回,直到ProcessList中。
java
// frameworks/base/services/core/java/com/android/server/am/ProcessList.java
boolean startProcessLocked(HostingRecord hostingRecord, String entryPoint, ProcessRecord app, int uid, int[] gids, int runtimeFlags, int zygotePolicyFlags, int mountExternal, String seInfo, String requiredAbi, String instructionSet, String invokeWith, long startUptime, long startElapsedTime) {
... ...
final Process.ProcessStartResult startResult = startProcess(hostingRecord,
entryPoint, app,
uid, gids, runtimeFlags, zygotePolicyFlags, mountExternal, seInfo,
requiredAbi, instructionSet, invokeWith, startUptime);
// 获得创建结果后,处理后续流程
handleProcessStartedLocked(app, startResult.pid, startResult.usingWrapper,
startSeq, false);
... ...
}
boolean handleProcessStartedLocked(ProcessRecord app, int pid, boolean usingWrapper, long expectedStartSeq, boolean procAttached) {
... ...
// 检查Zygote启动的进程是否合法
final String reason = isProcStartValidLocked(app, expectedStartSeq);
if (reason != null) {
... ...
// 进程不合法,杀死进程
killProcessQuiet(pid);
... ...
return false;
}
... ...
// ① EventLog打印日志
EventLog.writeEvent(EventLogTags.AM_PROC_START,
UserHandle.getUserId(app.getStartUid()), pid, app.getStartUid(),
app.processName, app.getHostingRecord().getType(),
app.getHostingRecord().getName() != null ? app.getHostingRecord().getName() : "");
... ...
// 在ProcessRecord中记录进程信息
app.setPid(pid);
app.setUsingWrapper(usingWrapper);
app.setPendingStart(false);
... ...
}
① 这里就是我们常见的那句EventLog打印的地方,出现这条日志,表明APP进程完全启动成功。
12-02 15:01:30.013 635 677 I am_proc_start: [0,2741,10136,com.vincent.sample,next-top-activity,{com.vincent.sample/com.vincent.sample.MainActivity}]
对照这打印日志的代码,再看这条日志,其中每一项的含义就非常容易理解了,因此,学习看EventLog最好的方法就是直接看日志打印代码,源码真是好东西,无需过多的文字描述,一切尽在不言中。
总结
经过本篇的探索,开头那幅概括图的粒度就太粗了,看了跟没看差不多,我们就来细化一下它,加深印象。
Q&A
调试AMS时发现参数、局部变量值无法查看
原因是services.jar默认开启了混淆,需要将混淆开关关闭,重新编译上传。
bp
// frameworks/base/services/Android.bp
system_optimized_java_defaults {
name: "services_java_defaults",
soong_config_variables: {
SYSTEM_OPTIMIZE_JAVA: {
optimize: {
enabled: true, //将这一行的值改为false
}
... ...
}
}
}