前言
本篇是 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
类中啦,也真正的进入应用层了。