应用程序的进程启动
简介
当我们打开android手机的时候,不知道你是否想过app是如何启动的呢?
接下来,我将从源码角度进行解析,当然,本文作为上篇,是介绍应用程序的进程启动过程,而不是应用程序的启动过程,他们的区别就是煮饭前要准备锅具,没有锅具就无法煮饭,本文就是准备锅具的,但是也不简单哦。
文章将从两个方面介绍,一个AMS发送请求,一个是Zygote接受请求。
AMS就是Activity Manager System,管理Activity的,而Zygote就是创建进程的一个进程,所以AMS要想创建进程必须从Zygote那里进行申请,一系列的故事,就此展开。
AMS发送请求
从图中可以简略看出,首先AMS自己调用了自己的startProcessLocked
方法,会创建相应的用户id,用户id组,以及对应的应用程序进程的主线程名——android.app.ActivityThread
。
private final void startProcessLocked(ProcessRecord app, String hostingType,
String hostingNameStr, String abiOverride, String entryPoint, String[] entryPointArgs) {
...
try {
int uid = app.uid;
int[] gids = null;
int mountExternal = Zygote.MOUNT_EXTERNAL_NONE;
if (!app.isolated) {
int[] permGids = null;
if (ArrayUtils.isEmpty(permGids)) {
gids = new int[3];
} else {
gids = new int[permGids.length + 3];
System.arraycopy(permGids, 0, gids, 3, permGids.length);
}
gids[0] = UserHandle.getSharedAppGid(UserHandle.getAppId(uid));
gids[1] = UserHandle.getCacheAppGid(UserHandle.getAppId(uid));
gids[2] = UserHandle.getUserGid(UserHandle.getUserId(uid));
}
String requiredAbi = (abiOverride != null) ? abiOverride : app.info.primaryCpuAbi;
if (requiredAbi == null) {
requiredAbi = Build.SUPPORTED_ABIS[0];
}
...
if (entryPoint == null) entryPoint = "android.app.ActivitThread";
...
if (hostingType.equals("webview_service")) {
...
} else {
startResult = Process.start(entryPoint,
app.processName, uid, uid, gids, debugFlags, mountExternal,
app.info.targetSdkVersion, seInfo, requiredAbi, instructionSet,
app.info.dataDir, invokeWith, entryPointArgs);
}
...
}
这里面的start中有一个参数名为requireAbi
,在下面会改成abi
起到比对的重要作用。
startProcessLocked
方法内部会调用Process的start
方法,而这个start方法只干了一件事,就是调用ZygoteProcess
的start
方法。
ZygoteProcess
的作用在于保持与Zygote通信的状态,在其start
方法中,会调用StartViaZygote
方法,这里的出现了下面很重要的abi。
public final Process.ProcessStartResult start(final String processClass,
final String niceName,
int uid, int gid, int[] gids,
...
String abi,
...
String[] zygoteArgs) {
try {
return startViaZygote(processClass, niceName, uid, gid, gids,
debugFlags, mountExternal, targetSdkVersion, seInfo,
abi, instructionSet, appDataDir, invokeWith, zygoteArgs);
...
}
StartViaZygote
方法会维护一个名为argsForZygote
的列表,顾名思义,所有能够启动应用程序的进程的参数,都会保存在这里。然后这个列表作为一个参数,被传入到ZygoteSendArgsAndGetResult
方法中,这个方法也是在StartViaZygote
方法中被调用.另外要注意的是,openZygoteSocketIfNeeded
方法也是作为参数传入了ZygoteSendArgsAndGetResult
方法中。
private Process.ProcessStartResult startViaZygote(final String processClass,
final String niceName,
final int uid, final int gid,
final int[] gids,
...
String abi,
...)
throws ZygoteStartFailedEx {
ArrayList argsForZygote = new ArrayList();
argsForZygote.add("--runtime-args");
argsForZygote.add("--setuid=" + uid);
argsForZygote.add("--setgid=" + gid);
...
synchronized(mLock) {
return zygoteSendArgsAndGetResult(openZygoteSocketIfNeeded(abi), argsForZygote);
}
}
ZygoteSendArgsAndGetResult
方法作用是将传入的参数,也就是argsForZygote
中保存的数据写入到ZygoteState
中,从图中我们虽然将ZygoteState
和ZygoteProcess
做了并列处理,但是ZygoteState
是ZygoteProcess
的一个静态内部类,上面提到的ZygoteProcess
的作用在于保持与Zygote通信的状态,其功能的完成就是在ZygoteState
中。
private static Process.ProcessStartResult zygoteSendArgsAndGetResult(
ZygoteState zygoteState, ArrayList args)
throws ZygoteStartFailedEx {
try {
final BufferedWriter writer = zygoteState.writer;
final DataInputStream inputStream = zygoteState.inputStream;
writer.write(Integer.toString(args.size()));
writer.newLine();
for (int i = 0; i < sz; i++) {
String arg = args.get(i);
writer.write(arg);
writer.newLine();
}
writer.flush();
...
result.pid = inputStream.readInt();
result.usingWrapper = inputStream.readBoolean();
...
}
从上面的方法看出,ZygoteSendArgsAndGetResult
方法的第一个参数变成了ZygoteState
,也就是说,openZygoteSocketIfNeeded
方法的返回值是ZygoteState
。
openZygoteSocketIfNeeded
方法也处于ZygoteProcess
中,就写在StartViaZygote
下面,可以看到,在第6行与Zygote进程建立了连接。
private ZygoteState openZygoteSocketIfNeeded(String abi) throws ZygoteStartFailedE
Preconditions.checkState(Thread.holdsLock(mLock), "ZygoteProcess lock not held");
if (primaryZygoteState == null || primaryZygoteState.isClosed()) {
try {
primaryZygoteState = ZygoteState.connect(mSocket);//建立连接
} catch (IOException ioe) {
throw new ZygoteStartFailedEx("Error connecting to primary zygote", ioe);
}
}
//这里primaryZygoteState是连接主模式后返回的ZygoteState,比对其是否与需要的abi匹配
if (primaryZygoteState.matches(abi)) {
return primaryZygoteState;
}
//如果主模式不成功,连接辅模式
if (secondaryZygoteState == null || secondaryZygoteState.isClosed()) {
try {
secondaryZygoteState = ZygoteState.connect(mSecondarySocket);
} catch (IOException ioe) {
throw new ZygoteStartFailedEx("Error connecting to secondary zygote", ioe);
}
}
//匹配辅模式的abi
if (secondaryZygoteState.matches(abi)) {
return secondaryZygoteState;
}
throw new ZygoteStartFailedEx("Unsupported zygote ABI: " + abi);
为什么第6行可以连接Zygote呢?因为Zygote是最先创建的进程,如果没有Zygote,我们的应用程序的进程也无从谈起,而Zygote为了创建其他进程,他会设置一个Socket监听,就是通过这个Socket,完成与Zygote的连接。
而对于主模式和辅助模式,这里不细讲,就是有两次连接并匹配abi
的机会。
要匹配abi
的原因在于,Zygote虽然可以fork自己不断创建进程,但是毕竟是有限的,而且需要资源,只有启动某个应用程序所必须的进程才有被创建的意义,这时候就在一开始的AMS中给设置一个abi
,如果与后来的Zygote匹配,证明是需要被创建的。
当匹配完成时,就会返回一个primaryZygoteState
,此时AMS发送请求这一流程就结束了,接下来,就是Zygote如何接受请求并创建应用程序的进程的过程了。
Zygote接受请求
依然是时序图,甚至说,有了时序图,根本就不需要看后面的文章啦。
看起来比上一个图稍微复杂些,不过可以发现还是很有规律的,Zygote先到ZygoteServer
再回来,再到ZygoteConnection
,再回来……
所以,后面几个方法都在ZygoteInit
中被调用,而且时间先后就是图中所示。
在AMS发送请求完成后,Zygote会连接到一个ZygoteState
,而这个ZygoteState
在之前的ZygoteSendArgsAndGetResult
方法中被写入了argsForZygote
所保存的数据,这些数据就是请求的具体参数。
现在Zygote将会收到这些参数,并在ZygoteInit
中进行相应操作。
ZygoteInit
首先会执行其main方法。
public static void main(String argv[]) {
...
try {
...
//设置Socket监听
zygoteServer.registerServerSocket(socketName);
if (!enableLazyPreload) {
bootTimingsTraceLog.traceBegin("ZygotePreload");
EventLog.writeEvent(LOG_BOOT_PROGRESS_PRELOAD_START,
SystemClock.uptimeMillis());
preload(bootTimingsTraceLog);
EventLog.writeEvent(LOG_BOOT_PROGRESS_PRELOAD_END,
SystemClock.uptimeMillis());
bootTimingsTraceLog.traceEnd(); // ZygotePreload
} else {
Zygote.resetNicePriority();
...
if (startSystemServer) {
startSystemServer(abiList, socketName, zygoteServer);//启动SystemServer进程
}
Log.i(TAG, "Accepting command socket connections");
zygoteServer.runSelectLoop(abiList);//等待AMS请求
zygoteServer.closeServerSocket();
...
}
这里最后一个注释,就是一切都准备好了,只等AMS发来请求,现在我们看看RunSelectLoop
方法是如何做的,猜测既然是等待,那肯定有一个死循环while,这个函数就在ZygoteServer
之中。
void runSelectLoop(String abiList) throws Zygote.MethodAndArgsCaller {
ArrayList fds = new ArrayList();
ArrayList peers = new ArrayList();
fds.add(mServerSocket.getFileDescriptor());
peers.add(null);
while (true) {
...
for (int i = pollFds.length - 1; i >= 0; --i) {
if ((pollFds[i].revents & POLLIN) == 0) {
continue;
}
if (i == 0) {
ZygoteConnection newPeer = acceptCommandPeer(abiList);
peers.add(newPeer);
fds.add(newPeer.getFileDesciptor());
} else {
//一个连接一个连接的运行
boolean done = peers.get(i).runOnce(this);
if (done) {
peers.remove(i);
fds.remove(i);
}
}
}
}
}
果然有一个while(true),哈哈,奖励自己晚上早睡十分钟。
可以看到这里维护了一个ZygoteConnection
型的列表,每当连接不够时,就会增加,然后一个一个连接的进行执行,执行玩一个连接,就移除一个。
runOnce
方法的作用就是读取argsForZygote
所保存的数据,完成交接,这个方法显然,在ZygoteConnection
中。
boolean runOnce(ZygoteServer zygoteServer) throws Zygote.MethodAndArgsCaller {
String args[];
Arguments parsedArgs = null;
FileDescriptor[] descriptors;
try {
args = readArgumentList(); //读取之前写入的数据
descriptors = mSocket.getAncillaryFileDescriptors();
} catch (IOException ex) {
Log.w(TAG, "IOException on command socket " + ex.getMessage());
closeSocket();
return true;
}
...
try {
parsedArgs = new Arguments(args);
...
//创建应用程序进程
pid = Zygote.forkAndSpecialize(parsedArgs.uid, parsedArgs.gid, parsedArgs.gids,
parsedArgs.debugFlags, rlimits, parsedArgs.mountExternal, parsedArgs.seInfo,
parsedArgs.niceName, fdsToClose, fdsToIgnore, parsedArgs.instructionSet,
parsedArgs.appDataDir);
}
...
try {
if (pid == 0) {//当前逻辑运行在子程序中
zygoteServer.closeServerSocket();
IoUtils.closeQuietly(serverPipeFd);
serverPipeFd = null;
handleChildProc(parsedArgs, descriptors, childPipeFd, newStderr);
return true;
}
...
}
}
如果在18行处的pid返回值为0,那么恭喜,此时的子进程已经创建完成,各种id和参数都传进去了,此时就会调用handleChildProc
方法来处理出现的应用程序的进程(还是要提醒,本文创建的是应用程序的进程,而非应用程序本身)。
handleChildProc
方法回调了ZygoteInit
中的zygoteInit
方法,注意,这里的z是小写!
private void handleChildProc(Arguments parsedArgs,
FileDescriptor[] descriptors, FileDescriptor pipeFd, PrintStream newStderr)
throws Zygote.MethodAndArgsCaller {
...
Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
if (parsedArgs.invokeWith != null) {
WrapperInit.execApplication(parsedArgs.invokeWith,
parsedArgs.niceName, parsedArgs.targetSdkVersion,
VMRuntime.getCurrentInstructionSet(),
pipeFd, parsedArgs.remainingArgs);
} else {
ZygoteInit.zygoteInit(parsedArgs.targetSdkVersion,
parsedArgs.remainingArgs, null /* classLoader */);
}
}
现在我们回到ZygoteInit
中,看看zygoteInit
是怎么写的。
public static final void zygoteInit(int targetSdkVersion, String[] argv,
ClassLoader classLoader) throws Zygote.MethodAndArgsCaller {
if (RuntimeInit.DEBUG) {
Slog.d(RuntimeInit.TAG, "RuntimeInit: Starting application from zygote");
}
Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "ZygoteInit");
RuntimeInit.redirectLogStreams();
RuntimeInit.commonInit();
ZygoteInit.nativeZygoteInit();
RuntimeInit.applicationInit(targetSdkVersion, argv, classLoader);
}
注意到这个方法是一个静态的final方法,在12行代码处,会执行RuntimeInit
的applicationInit
方法。
protected static void applicationInit(int targetSdkVersion, String[]
throws Zygote.MethodAndArgsCaller {
...
final Arguments args;
try {
args = new Arguments(argv);
} catch (IllegalArgumentException ex) {
Slog.e(TAG, ex.getMessage());
// let the process exit
return;
}
Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
invokeStaticMain(args.startClass, args.startArgs, classLoader);
}
同样在最后一行,调用了invokeStaticMain
方法,这里传入了三个参数,第一个参数args.startClass
就是本文一开始说的那个主线程的名字android.app.MainThread
。
终于我们到了最后一站!现在我们进入invokeStaticMain
方法。
private static void invokeStaticMain(String className, String[] argv, ClassLoader
throws Zygote.MethodAndArgsCaller {
Class> cl;
try {
cl = Class.forName(className, true, classLoader);//反射获取MainThread
} 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 });//获得main
} 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);
...
throw new Zygote.MethodAndArgsCaller(m, argv);
}
这里的className就是我们刚刚提到的args.startClass
,也就是主线程名字,通过第6行的反射获取到对应的类,命名为c1,然后再通过15行代码获取主方法main,并将main传入到MethodAndArgsCaller
方法中,当做异常抛出。
为什么这里要用抛出异常的方法呢?
联想到这是最后一步,抛出异常的方式将会清除设置过程中需要的堆栈帧,仅仅留下一个main方法,使得main方法看起来像一个入口方法,毕竟,前面那么多的工作,那么多类的流血牺牲,最后都是为了成就他。当他出现时,前面的东西,也就没有必要存在了。
现在异常抛出了,谁来接受呢?当然,是main了,main位于ZygoteInit
中。
public static void main(String argv[]) {
...
zygoteServer.closeServerSocket();
} catch (Zygote.MethodAndArgsCaller caller) {
caller.run();
} catch (Throwable ex) {
Log.e(TAG, "System zygote died with exception", ex);
zygoteServer.closeServerSocket();
throw ex;
}
}
可以看到,当出现了一个MethodAndArgsCaller
型的异常的时候,main会捕捉到,并且执行他的run方法,这个run方法在MethodAndArgsCaller
中,他是一个静态类。
public static class MethodAndArgsCaller extends Exception
implements Runnable {
...
public void run() {
try {
mMethod.invoke(null, new Object[] { mArgs });
} catch (IllegalAccessException ex) {
throw new RuntimeException(ex);
} catch (InvocationTargetException ex) {
...
}
}
}
调用了invoke
方法后,ActivityThread里面的main方法就会被动态调用,应用程序的进程就进入了这一main方法中。
经过上面的种种操作,我们创建了应用程序的进程并且运行了ActivityThread。
小结
简单来说就是要想创建应用程序的进程,需要AMS发送请求,传入主线程名,这一请求必须符合Zygote的abi
,并且将请求参数写在ZygoteState
中。
然后Zygote读取参数,fork自身去生成新的进程,如果返回的pid为0,那么就完成了应用程序的进程的创建。
然后通过反射将主线程名转换为主线程的类,获得其main方法,通过抛出一个异常清除掉之前的堆栈帧,在main方法里面接受这个异常,并执行run方法以激活main方法,使得应用程序的进程进入到main方法中得以运行。
写了一早上,呼~,准备吃饭啦,还有什么不懂的可以评论区提问哦。
本文取材自《Android进阶解密》,资源在微信公众号【小松漫步】,一起交流学习吧。