前言
本篇是 Activity 启动过程(中)篇,承接上篇文章 Android Activity 启动过程(上)
Android Activity 启动过程(上) 主要描述SystemServer 进程如何处理startActivity请求以及如何走到请求Zygote fork app 进程的流程
此篇主要是介绍Zygote进程接收到 来自SystemServer进程请求创建App进程的消息后,如何去fork app 进程。
Zygote 进程 fork App进程过程
frameworks/base/core/java/com/android/internal/os/ZygoteServer.java
scss
Runnable runSelectLoop(String abiList) {
ArrayList<FileDescriptor> socketFDs = new ArrayList<>();
ArrayList<ZygoteConnection> peers = new ArrayList<>();
// 省略
mUsapPoolRefillTriggerTimestamp = INVALID_TIMESTAMP;
while (true) {
fetchUsapPoolPolicyPropsWithMinInterval();
mUsapPoolRefillAction = UsapPoolRefillAction.NONE;
// 省略
try {
pollReturnValue = Os.poll(pollFDs, pollTimeoutMs);//chld 正常情况下此处传入的值是 -1,即一直轮询等待数据
} catch (ErrnoException ex) {
throw new RuntimeException("poll failed", ex);
}
if (pollReturnValue == 0) {
mUsapPoolRefillTriggerTimestamp = INVALID_TIMESTAMP;
mUsapPoolRefillAction = UsapPoolRefillAction.DELAYED;
} else {
boolean usapPoolFDRead = false;
while (--pollIndex >= 0) {
if ((pollFDs[pollIndex].revents & POLLIN) == 0) {
continue;
}
if (pollIndex == 0) {//chld - 走到 pollIndex 说明此时没有需要处理的创建进程的需求,而是有的新的连接到来
// Zygote server socket
ZygoteConnection newPeer = acceptCommandPeer(abiList);// chld 等待 新的链接,没有时 阻塞着
peers.add(newPeer);
socketFDs.add(newPeer.getFileDescriptor());
} else if (pollIndex < usapPoolEventFDIndex) { // chld 说明此时系统不支持 usap.(如果有的话,pollIndex 值是大于等于 usapPoolEventFDIndex 的,原因具体情况Android 11 对应代码,此处相关逻辑已被删除)
// Session socket accepted from the Zygote server socket
try {
ZygoteConnection connection = peers.get(pollIndex);
final Runnable command = connection.processOneCommand(this);// 代码1
// TODO (chriswailes): Is this extra check necessary?
if (mIsForkChild) {// chld 此时处于fork出来的应用进程中,返回 Runnable 对象,去在`ZygoteInit::main`方法中执行
// We're in the child. We should always have a command to run at
// this stage if processOneCommand hasn't called "exec".
if (command == null) {
throw new IllegalStateException("command == null");
}
return command;
} else {
if (connection.isClosedByPeer()) {//代码2 Zygote 进程关闭 socket 连接
connection.closeSocket();
peers.remove(pollIndex);
socketFDs.remove(pollIndex);
}
}
} catch (Exception e) {
// 省略
} finally {
// 重置标志
mIsForkChild = false;
}
} else {// chld USAP 启动
// 省略
usapPoolFDRead = true;
}
}
if (usapPoolFDRead) {
int usapPoolCount = Zygote.getUsapPoolCount();
if (usapPoolCount < mUsapPoolSizeMin) {//chld 最小值默认是1,即表示此时 可用USAP进程小于1个
// Immediate refill
mUsapPoolRefillAction = UsapPoolRefillAction.IMMEDIATE;
} else if (mUsapPoolSizeMax - usapPoolCount >= mUsapPoolRefillThreshold) {//chld 可使用的USAP 进程低于阈值(最大10个,阈值一般是最大值的一半)
// Delayed refill
mUsapPoolRefillTriggerTimestamp = System.currentTimeMillis();
}
}
}
if (mUsapPoolRefillAction != UsapPoolRefillAction.NONE) {// chld 说明此时使用 usap 方式启动
int[] sessionSocketRawFDs =
socketFDs.subList(1, socketFDs.size())
.stream()
.mapToInt(FileDescriptor::getInt$)
.toArray();
final boolean isPriorityRefill =
mUsapPoolRefillAction == UsapPoolRefillAction.IMMEDIATE;// chld 判断是否立即将usap 进程补充完
final Runnable command =
fillUsapPool(sessionSocketRawFDs, isPriorityRefill);// 代码3
if (command != null) {
return command;
} else if (isPriorityRefill) {
// Schedule a delayed refill to finish refilling the pool.
mUsapPoolRefillTriggerTimestamp = System.currentTimeMillis();
}
}
}
}
代码3 处,是在使用usap 方式启动。此处暂时不细讲了,有兴趣的同学自行搜索下吧,usap 只是免除了进程的fork,加快了应用启动速度,其他流程基本一致。(USAP 进程默认会创建10个待用,待低于一半值时重新填空完成,同时其是在避免影响应用进程创建的,在没有应用创建请求的时候去创建) 而在未开启 usap 启动方式的情况下,正常是走到代码1处, processOneCommand 返回一个 Runnable 对象,该 Runnable 对象将在 ZygnoteInit::main 被执行(注:在Android 8.1 及之前,是通过抛出异常的方式抛到 ZygnoteInit::main 方法中被捕获后执行的,一种奇奇怪怪的古怪方式,在Android 9开始终于才用 返回的方式了)。 该 Runnable对象的 run方法中将执行 ActivityThread 的创建方法。
接下来看下 ZygoteConnection::processOneCommand 方法吧
frameworks/base/core/java/com/android/internal/os/ZygoteConnection.java
ini
Runnable processOneCommand(ZygoteServer zygoteServer) {
String[] args;
try {
args = Zygote.readArgumentList(mSocketReader);
} catch (IOException ex) {
throw new IllegalStateException("IOException on command socket", ex);
}
if (args == null) {
isEof = true;
return null;
}
ZygoteArguments parsedArgs = new ZygoteArguments(args);//代码1
//... 省略
// 代码2
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.mWhitelistedDataInfoList,
parsedArgs.mBindMountAppDataDirs, parsedArgs.mBindMountAppStorageDirs);
try {
if (pid == 0) {//代码3 子进程
zygoteServer.setForkChild();
zygoteServer.closeServerSocket();
IoUtils.closeQuietly(serverPipeFd);// 关闭与server的通道
serverPipeFd = null;
return handleChildProc(parsedArgs, childPipeFd, parsedArgs.mStartChildZygote);// 代码4
} else {
IoUtils.closeQuietly(childPipeFd);
childPipeFd = null;
handleParentProc(pid, serverPipeFd);// 代码5
// 代码6
return null;
}
} finally {
// 最终再尝试一次关闭管道
IoUtils.closeQuietly(childPipeFd);
IoUtils.closeQuietly(serverPipeFd);
}
}
代码1处 将 SystemServer发送来的请求数参数进行转换成 ZygoteArguments 对象。 代码2处 应用将根据这些参数调用 Zygote.forkAndSpecialize方法去fork 一个进程,forkAndSpecialize 下面的处理就涉及作者盲区了。 代码3处 fork后进入代码3判断处,此处根据 pid 的值判断当前是处于fork 处理的子进程还是 父进程。 子线程将进入代码4处调用 handleChildProc 方法处理。而 Zygote 将进入代码5处,并在代码6处返回 null,通过null 来告知上层,此时是处于子进程中。并且在 handleParentProc方法中 Zygote 进程将告知 SystemServer 创建的进程的id.
先看下 handleParentProc方法。
frameworks/base/core/java/com/android/internal/os/ZygoteConnection.java
csharp
private void handleParentProc(int pid, FileDescriptor pipeFd) {
//... 省略
try {
mSocketOutStream.writeInt(pid);
mSocketOutStream.writeBoolean(usingWrapper);
} catch (IOException ex) {
throw new IllegalStateException("Error writing to command socket", ex);
}
}
此处通过socket 将进程pid 和 usingWrapper 数据传回给了 SysmtemServer 进程的 2.6 章节处 的 ZygoteProcess::attemptZygoteSendArgsAndGetResult方法中.
接下来返回看下 ZygoteConnection::handleChildProc 方法中是怎么去创建一个 Runnable 对象的。
typescript
private Runnable handleChildProc(ZygoteArguments parsedArgs,
FileDescriptor pipeFd, boolean isZygote) {
closeSocket();
Zygote.setAppProcessName(parsedArgs, TAG);//此时设置进程名称
// chld 应用进程此处 mInvokeWith:null isZygote:false
// 输出结果: I/Zygote: chld handleChildProc setAppProcessName mNiceName:com.example.myapplication2 mPackageName:com.example.myapplication2 mInvokeWith:null isZygote:false
Log.i(TAG,"chld handleChildProc setAppProcessName mNiceName:" + parsedArgs.mNiceName + " mPackageName:" + parsedArgs.mPackageName + " mInvokeWith:" + parsedArgs.mInvokeWith +" isZygote:" + isZygote);
if (parsedArgs.mInvokeWith != null) {
WrapperInit.execApplication(parsedArgs.mInvokeWith,
parsedArgs.mNiceName, parsedArgs.mTargetSdkVersion,
VMRuntime.getCurrentInstructionSet(),
pipeFd, parsedArgs.mRemainingArgs);
// Should not get here.
throw new IllegalStateException("WrapperInit.execApplication unexpectedly returned");
} else {
if (!isZygote) {
return ZygoteInit.zygoteInit(parsedArgs.mTargetSdkVersion,
parsedArgs.mDisabledCompatChanges,
parsedArgs.mRemainingArgs, null /* classLoader */);
} else {// chld isZygote 判断创建的这个新的进程本身是不是一个Zygote(孵化器) 进程,创建的是应用进程显然是走此处
return ZygoteInit.childZygoteInit(parsedArgs.mTargetSdkVersion,
parsedArgs.mRemainingArgs, null /* classLoader */);//代码2
}
}
}
代码1 处此时设置了进程的名字,一般是应用的包名。在此之前 Zygote名称为 Zygote,有兴趣的同学可以试试。注意:如果是UASP 方式启动,在调用 Zygote.setAppProcessName之前名称为 UASP 。 代码2 这个Runnable 对象还得再通过 ZygoteInit::childZygoteInit 方法去创建啊。此处还需要的参数只剩下了 parsedArgs.mTargetSdkVersion,parsedArgs.mRemainingArgs
接下来去看下 ZygoteInit::childZygoteInit 方法的实现吧。
frameworks/base/core/java/com/android/internal/os/ZygoteInit.java
arduino
static final Runnable childZygoteInit(
int targetSdkVersion, String[] argv, ClassLoader classLoader) {
RuntimeInit.Arguments args = new RuntimeInit.Arguments(argv);
return RuntimeInit.findStaticMain(args.startClass, args.startArgs, classLoader);
}
frameworks/base/core/java/com/android/internal/os/RuntimeInit.java
php
protected static Runnable findStaticMain(String className, String[] argv,
ClassLoader classLoader) {
Class<?> cl;
try {
cl = Class.forName(className, true, classLoader);
} catch (ClassNotFoundException ex) {
throw new RuntimeException(
"Missing class when invoking static main " + className,
ex);
}
Method m;
try {
m = cl.getMethod("main", new Class[] { String[].class });
} catch (NoSuchMethodException ex) {
throw new RuntimeException(
"Missing static main on " + className, ex);
} catch (SecurityException ex) {
throw new RuntimeException(
"Problem getting static main on " + className, ex);
}
int modifiers = m.getModifiers();
if (! (Modifier.isStatic(modifiers) && Modifier.isPublic(modifiers))) {
throw new RuntimeException(
"Main method is not public and static on " + className);
}
/*
* This throw gets caught in ZygoteInit.main(), which responds
* by invoking the exception's run() method. This arrangement
* clears up all the stack frames that were required in setting
* up the process.
*/
return new MethodAndArgsCaller(m, argv);// 代码1
}
findStaticMain 方法中传入的 className 就是 2.5 ProcessList ProcessList::startProcessLocked 方法中 final String entryPoint = "android.app.ActivityThread" 值;, 此处获取了该类的静态方法,并将参数传入。 然后一路返回到 ZygoteInit::main方法吧。
frameworks/base/core/java/com/android/internal/os/ZygoteInit.java
java
public static void main(String argv[]) {
ZygoteServer zygoteServer = null;
Runnable caller;
try {
//...
zygoteServer = new ZygoteServer(isPrimaryZygote);
if (startSystemServer) {// 代码1 fork SystemServer 进程
Runnable r = forkSystemServer(abiList, zygoteSocketName, zygoteServer);
// {@code r == null} in the parent (zygote) process, and {@code r != null} in the
// child (system_server) process.
if (r != null) {
r.run();
return;
}
}
Log.i(TAG, "Accepting command socket connections");
// The select loop returns early in the child process after a fork and
// loops forever in the zygote.
caller = zygoteServer.runSelectLoop(abiList);// 代码2 等待AMS创建进行请求,当创建完应用子进程后,应用子进程会返回一个`Runnable` 对象
} catch (Throwable ex) {
Log.e(TAG, "System zygote died with exception", ex);
throw ex;
} finally {
if (zygoteServer != null) {
zygoteServer.closeServerSocket();
}
}
if (caller != null) {//chld 应用进程f
caller.run();
}
}
可以看到此处执行了 MethodAndArgsCaller::run方法,在该 run方法中去进行了 ActivityAThread::main 方法的反射调用。
为什么不直接在
RuntimeInit::findStaticMain调用呢? 这是为了清除 创建过程中堆栈,让应用进程看起来是直接从ZygoteInit::main方法中直接启动的。(太长的栈,那遇到异常打印出来,得携带多少无用的栈数据啊)
frameworks/base/core/java/com/android/internal/os/RuntimeInit.java
php
static class MethodAndArgsCaller implements Runnable {
/** method to call */
private final Method mMethod;
/** argument array */
private final String[] mArgs;
public MethodAndArgsCaller(Method method, String[] args) {
mMethod = method;
mArgs = args;
}
public void run() {
try {
mMethod.invoke(null, new Object[] { mArgs });
} catch (IllegalAccessException ex) {
throw new RuntimeException(ex);
} catch (InvocationTargetException ex) {
Throwable cause = ex.getCause();
if (cause instanceof RuntimeException) {
throw (RuntimeException) cause;
} else if (cause instanceof Error) {
throw (Error) cause;
}
throw new RuntimeException(ex);
}
}
}
如上是 MethodAndArgsCaller类,run 没什么可说的,就是一个反射调用。接下来就走到 ActivityThread类中啦,也真正的进入应用层了。