Android系统中,启动一个app首先要保证该app所在的进程已经被启动。本文就介绍下app进程,即应用程序进程的启动过程。这里顺便提一句,通常所说的启动应用程序,指的是其根Activity的启动,和应用程序进程的启动是两个概念。
- 了解Android系统启动过程或者Zygote进程的同学都会知道,应用程序进程的创建是通过Zygote进程fork其自身而产生的。①
- 了解Activity的启动过程的朋友,又知道在Activity的启动过程中,会有一个检查Activity所在进程是否已经启动,否则就会先创建并启动其所在进程,再启动Activity。②
知道了上面两个过程,再来理解app进程启动过程,就相对容易了。先让大家有个大概的流程认识:
在Android系统的启动过程中,Zygote进程的Java框架层中创建一个Server端的Socket。这个Socket用来等待AMS发送请求,让Zygote进程创建新的app进程。Zygote进程接收到请求后,通过fork自身创建app进程。
具体的创建和启动过程步骤比较多,我们把上述过程分为两个部分来分别讲解。
AMS发送启动app进程的请求
先来看一下AMS发送启动app进程请求过程的时序图
image.png
AMS如果想要启动app进程,就需要向Zygote进程发送创建app进程的请求,AMS会通过调用startProcessLocked方法来开始这一过程。下面展示相关的重要代码
private final void startProcessLocked(ProcessRecord app, String hostingType,
String hostingNameStr, String abiOverride, String entryPoint, String[] entryPointArgs) {
...
try {
...
//获取要创建的app进程的用户id
int uid = app.uid;
...
//对用户组gids进行创建和赋值
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));
...
// Start the process. It will either succeed and return a result containing
// the PID of the new process, or else throw a RuntimeException.
//这里的entryPoint的值就是app进程主线程的类名。
if (entryPoint == null) entryPoint = "android.app.ActivityThread";
...
//调用Process.start方法,将上面得到的entryPoint,uid,gids传进去
startResult = Process.start(entryPoint,
app.processName, uid, uid, gids, debugFlags, mountExternal,
app.info.targetSdkVersion, seInfo, requiredAbi, instructionSet,
app.info.dataDir, invokeWith, entryPointArgs);
}
关于uid、gid和进程
在Linux中,每个UID标识一个用户,每个用户又至少属于一个组,每个组都有一个唯一标识的GID。每个进程都拥有真实的用户、组(UID、GID)。在Android系统中,一个用户的UID表示一个app。app在安装时被分配了用户UID,其在设备上存续期间,用户UID保持不变。对于普通的用户程序,GID和UID相同。详细介绍可以参考Android 安全机制(1)uid 、 gid 与 pid
entryPoint 希望读者能够留意这个变量值,它关系到app进程创建后的初始化过程,后面会提到。
接下来查看Process的start方法,这里需要注意的是,此处的Process类不是java/lang/Process.java的Process类,而是android/os/Process.java的Process类
package android.os;
/**
* Tools for managing OS processes.
*/
public class Process {
...
public static final ProcessStartResult start(final String processClass,
final String niceName,
int uid, int gid, int[] gids,
int runtimeFlags, int mountExternal,
int targetSdkVersion,
String seInfo,
String abi,
String instructionSet,
String appDataDir,
String invokeWith,
String[] zygoteArgs) {
return zygoteProcess.start(processClass, niceName, uid, gid, gids,
runtimeFlags, mountExternal, targetSdkVersion, seInfo,
abi, instructionSet, appDataDir, invokeWith, zygoteArgs);
}
...
}
一目了然,其内部直接调用了ZygoteProcess的start方法
public final Process.ProcessStartResult start(final String processClass,
final String niceName,
int uid, int gid, int[] gids,
int runtimeFlags, int mountExternal,
int targetSdkVersion,
String seInfo,
String abi,
String instructionSet,
String appDataDir,
String invokeWith,
String[] zygoteArgs) {
try {
return startViaZygote(processClass, niceName, uid, gid, gids,
runtimeFlags, mountExternal, targetSdkVersion, seInfo,
abi, instructionSet, appDataDir, invokeWith, false /* startChildZygote */,
zygoteArgs);
} catch (ZygoteStartFailedEx ex) {
Log.e(LOG_TAG,
"Starting VM process through Zygote failed");
throw new RuntimeException(
"Starting VM process through Zygote failed", ex);
}
}
它又调用了startViaZygote方法
/**
* Starts a new process via the zygote mechanism.
*/
private Process.ProcessStartResult startViaZygote(final String processClass,
final String niceName,
final int uid, final int gid,
final int[] gids,
int runtimeFlags, int mountExternal,
int targetSdkVersion,
String seInfo,
String abi,
String instructionSet,
String appDataDir,
String invokeWith,
boolean startChildZygote,
String[] extraArgs)
throws ZygoteStartFailedEx {
ArrayList<String> argsForZygote = new ArrayList<String>();
// --runtime-args, --setuid=, --setgid=,
// and --setgroups= must go first
argsForZygote.add("--runtime-args");
argsForZygote.add("--setuid=" + uid);
argsForZygote.add("--setgid=" + gid);
argsForZygote.add("--runtime-flags=" + runtimeFlags);
if (mountExternal == Zygote.MOUNT_EXTERNAL_DEFAULT) {
argsForZygote.add("--mount-external-default");
} else if (mountExternal == Zygote.MOUNT_EXTERNAL_READ) {
argsForZygote.add("--mount-external-read");
} else if (mountExternal == Zygote.MOUNT_EXTERNAL_WRITE) {
argsForZygote.add("--mount-external-write");
}
argsForZygote.add("--target-sdk-version=" + targetSdkVersion);
// --setgroups is a comma-separated list
if (gids != null && gids.length > 0) {
StringBuilder sb = new StringBuilder();
sb.append("--setgroups=");
int sz = gids.length;
for (int i = 0; i < sz; i++) {
if (i != 0) {
sb.append(',');
}
sb.append(gids[i]);
}
argsForZygote.add(sb.toString());
}
if (niceName != null) {
argsForZygote.add("--nice-name=" + niceName);
}
if (seInfo != null) {
argsForZygote.add("--seinfo=" + seInfo);
}
if (instructionSet != null) {
argsForZygote.add("--instruction-set=" + instructionSet);
}
if (appDataDir != null) {
argsForZygote.add("--app-data-dir=" + appDataDir);
}
if (invokeWith != null) {
argsForZygote.add("--invoke-with");
argsForZygote.add(invokeWith);
}
if (startChildZygote) {
argsForZygote.add("--start-child-zygote");
}
argsForZygote.add(processClass);
if (extraArgs != null) {
for (String arg : extraArgs) {
argsForZygote.add(arg);
}
}
synchronized(mLock) {
return zygoteSendArgsAndGetResult(openZygoteSocketIfNeeded(abi), argsForZygote);
}
}
这个方法前面一段是创建字符串列表argsForZygote,并将app进程的启动参数放在argsForZygote中。方法最后调用了zygoteSendArgsAndGetResult方法。需要注意的是zygoteSendArgsAndGetResult方法的第一个参数openZygoteSocketIfNeeded,稍后会讲到。第二个参数就是argsForZygote。先看看zygoteSendArgsAndGetResult方法的代码
/**
* Sends an argument list to the zygote process, which starts a new child
* and returns the child's pid. Please note: the present implementation
* replaces newlines in the argument list with spaces.
*
* @throws ZygoteStartFailedEx if process start failed for any reason
*/
@GuardedBy("mLock")
private static Process.ProcessStartResult zygoteSendArgsAndGetResult(
ZygoteState zygoteState, ArrayList<String> args)
throws ZygoteStartFailedEx {
try {
int sz = args.size();
for (int i = 0; i < sz; i++) {
if (args.get(i).indexOf('\n') >= 0) {
throw new ZygoteStartFailedEx("embedded newlines not allowed");
}
}
...
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();
if (result.pid < 0) {
throw new ZygoteStartFailedEx("fork() failed");
}
return result;
} catch (IOException ex) {
zygoteState.close();
throw new ZygoteStartFailedEx(ex);
}
}
zygoteSendArgsAndGetResult方法的主要作用就是将传入的app进程的启动参数argsForZygote写入到zygoteState中,zygoteState是ZygoteProcess的静态内部类,用于表示与Zygote进程的通信状态。现在再回过头去看,startViaZygote的return语句,我们可以知道,这个zygoteState对象是openZygoteSocketIfNeeded方法返回的。
/**
* Tries to open socket to Zygote process if not already open. If
* already open, does nothing. May block and retry. Requires that mLock be held.
*/
@GuardedBy("mLock")
private ZygoteState openZygoteSocketIfNeeded(String abi) throws ZygoteStartFailedEx {
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);
}
maybeSetApiBlacklistExemptions(primaryZygoteState, false);
maybeSetHiddenApiAccessLogSampleRate(primaryZygoteState);
}
if (primaryZygoteState.matches(abi)) {
return primaryZygoteState;
}
// The primary zygote didn't match. Try the secondary.
if (secondaryZygoteState == null || secondaryZygoteState.isClosed()) {
try {
secondaryZygoteState = ZygoteState.connect(mSecondarySocket);
} catch (IOException ioe) {
throw new ZygoteStartFailedEx("Error connecting to secondary zygote", ioe);
}
maybeSetApiBlacklistExemptions(secondaryZygoteState, false); maybeSetHiddenApiAccessLogSampleRate(secondaryZygoteState);
}
if (secondaryZygoteState.matches(abi)) {
return secondaryZygoteState;
}
throw new ZygoteStartFailedEx("Unsupported zygote ABI: " + abi);
}
在Zygote进程启动过程中,Zygote的main方法中会创建name为"zygote"的Server端Socket。在上面的代码中,我们也看到了调用ZygoteState的connect方法。这个方法就是用来和Zygote进程建立Socket连接的。其中的mSocket,是一个LocalSocketAddress对象,表示和Zygote进程通信的Socket名称。在这里指的就是Zygote进程里的Socket名称。
我们再来看看ZygoteState类,就能更容易理解这个Socket通信过程。
public static class ZygoteState {
final LocalSocket socket;
final DataInputStream inputStream;
final BufferedWriter writer;
final List<String> abiList;
...
public static ZygoteState connect(LocalSocketAddress address) throws IOException {
DataInputStream zygoteInputStream = null;
BufferedWriter zygoteWriter = null;
final LocalSocket zygoteSocket = new LocalSocket();
try {
zygoteSocket.connect(address);
zygoteInputStream = new DataInputStream(zygoteSocket.getInputStream());
zygoteWriter = new BufferedWriter(new OutputStreamWriter(
zygoteSocket.getOutputStream()), 256);
} catch (IOException ex) {
try {
zygoteSocket.close();
} catch (IOException ignore) {
}
throw ex;
}
String abiListString = getAbiList(zygoteWriter, zygoteInputStream);
Log.i("Zygote", "Process: zygote socket " + address.getNamespace() + "/"
+ address.getName() + " opened, supported ABIS: " + abiListString);
return new ZygoteState(zygoteSocket, zygoteInputStream, zygoteWriter,
Arrays.asList(abiListString.split(",")));
}
...
}
可以看到在ZygoteState的connect方法里面,调用了LocalSocket的connect方法。而这个LocalSocket就是Android里面的Socket实现,和java里面的Socket类一样,实现了Closeable接口。
至此,我们已经可以看出,AMS和Zygote进程建立Socket连接的过程和基于Java的Socket连接过程是很相似的。
Zygote接收请求并创建app进程
同样,我们先看一下这部分的时序图 image.png我们先从,Zygote进程启动过程中会创建一个Server端的Socket,这个Socket用来等待AMS发送请求讲起。
这个过程是在ZygoteInit的main方法中执行的。
public static void main(String argv[]) {
...
String socketName = "zygote";
...
zygoteServer.registerServerSocketFromEnv(socketName);
if (!enableLazyPreload) {
bootTimingsTraceLog.traceBegin("ZygotePreload");
EventLog.writeEvent(LOG_BOOT_PROGRESS_PRELOAD_START,
SystemClock.uptimeMillis());
//预加载资源
preload(bootTimingsTraceLog);
...
//创建SystemServer进程
if (startSystemServer) {
Runnable r = forkSystemServer(abiList, socketName, zygoteServer);
...
// The select loop returns early in the child process after a fork and
// loops forever in the zygote.
//等待AMS的请求
caller = zygoteServer.runSelectLoop(abiList);
...
}
这段代码中,首先通过registerServerSocketFromEnv方法创建了一个Server段的Socket,这个name为"zygote"的Socket用来等待AMS请求Zygote,以创建新的app进程。最后调用zygoteServer的runSelectLoop方法来等待或者说监听AMS请求创建新的app进程。接下来看看zygoteServer的runSelectLoop方法
/**
* Runs the zygote process's select loop. Accepts new connections as
* they happen, and reads commands from connections one spawn-request's
* worth at a time.
*/
Runnable runSelectLoop(String abiList) {
ArrayList<FileDescriptor> fds = new ArrayList<FileDescriptor>();
ArrayList<ZygoteConnection> peers = new ArrayList<ZygoteConnection>();
...
while (true) {
StructPollfd[] pollFds = new StructPollfd[fds.size()];
for (int i = 0; i < pollFds.length; ++i) {
...
ZygoteConnection connection = peers.get(i);
final Runnable command = connection.processOneCommand(this);
...
}
runSelectLoop方法中,通过一个while(true)的循环,不停的遍历ArrayList<ZygoteConnection>类型的peers列表,执行其中每个ZygoteConnection对象的processOneCommand方法。我们接着看ZygoteConnection的processOneCommand方法
/**
* Reads one start command from the command socket. If successful, a child is forked and a
* {@code Runnable} that calls the childs main method (or equivalent) is returned in the child
* process. {@code null} is always returned in the parent process (the zygote).
*
* If the client closes the socket, an {@code EOF} condition is set, which callers can test
* for by calling {@code ZygoteConnection.isClosedByPeer}.
*/
Runnable processOneCommand(ZygoteServer zygoteServer) {
...
//读取app进程的启动参数
args = readArgumentList();
...
parsedArgs = new Arguments(args);
...
pid = Zygote.forkAndSpecialize(parsedArgs.uid, parsedArgs.gid, parsedArgs.gids,
parsedArgs.runtimeFlags, rlimits, parsedArgs.mountExternal, parsedArgs.seInfo,
parsedArgs.niceName, fdsToClose, fdsToIgnore, parsedArgs.startChildZygote,
parsedArgs.instructionSet, parsedArgs.appDataDir);
try {
//当前代码是在子进程中执行的
if (pid == 0) {
// in child
zygoteServer.setForkChild();
zygoteServer.closeServerSocket();
IoUtils.closeQuietly(serverPipeFd);
serverPipeFd = null;
return handleChildProc(parsedArgs, descriptors, childPipeFd,
parsedArgs.startChildZygote);
} else {
// In the parent. A pid < 0 indicates a failure and will be handled in
// handleParentProc.
IoUtils.closeQuietly(childPipeFd);
childPipeFd = null;
handleParentProc(pid, descriptors, serverPipeFd);
return null;
}
} finally {
IoUtils.closeQuietly(childPipeFd);
IoUtils.closeQuietly(serverPipeFd);
}
}
上面这段代码中,首先调用readArgumentList方法获取app进程的启动参数,并封装到Arguments类型的parsedArgs参数中,然后会调用Zygote的forkAndSpecialize方法来创建app进程,参数就是parsedArgs中的app进程启动参数,然后返回pid。forkAndSpecialize方法主要是通过fork当前进程,也就是Zygote进程来创建一个子进程的。如果pid为0,那接下去的代码就是在新创建的子进程中执行。这时会调用handleChildProc来处理App进程。
/**
* Handles post-fork setup of child proc, closing sockets as appropriate,
* reopen stdio as appropriate, and ultimately throwing MethodAndArgsCaller
* if successful or returning if failed.
*/
private Runnable handleChildProc(Arguments parsedArgs, FileDescriptor[] descriptors,
FileDescriptor pipeFd, boolean isZygote) {
//关闭LocalSocket
closeSocket();
...
if (!isZygote) {
return ZygoteInit.zygoteInit(parsedArgs.targetSdkVersion, parsedArgs.remainingArgs,
null /* classLoader */);
...
}
在handleChildProc方法中,需要重点关注的代码就是调用了ZygoteInit的zogoteInit方法。
/**
* The main function called when started through the zygote process. This
* could be unified with main(), if the native code in nativeFinishInit()
* were rationalized with Zygote startup.
*
*/
public static final Runnable zygoteInit(int targetSdkVersion, String[] argv, ClassLoader classLoader) {
if (RuntimeInit.DEBUG) {
Slog.d(RuntimeInit.TAG, "RuntimeInit: Starting application from zygote");
}
Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "ZygoteInit");
RuntimeInit.redirectLogStreams();
RuntimeInit.commonInit();
//创建Binder线程池
ZygoteInit.nativeZygoteInit();
return RuntimeInit.applicationInit(targetSdkVersion, argv, classLoader);
}
zygoteInit方法最后调用了RuntimeInit的applicationInit方法。下面是applicationInit方法的代码
protected static Runnable applicationInit(int targetSdkVersion, String[] argv,
ClassLoader classLoader) {
nativeSetExitWithoutCleanup(true);
VMRuntime.getRuntime().setTargetHeapUtilization(0.75f);
VMRuntime.getRuntime().setTargetSdkVersion(targetSdkVersion);
final Arguments args = new Arguments(argv);
Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
// Remaining arguments are passed to the start class's static main
return findStaticMain(args.startClass, args.startArgs, classLoader);
}
RuntimeInit的ApplicationInit方法最后调用了findStaticMain方法,
/**
* Invokes a static "main(argv[]) method on class "className".
* Converts various failing exceptions into RuntimeExceptions, with
* the assumption that they will then cause the VM instance to exit.
*/
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);
}
源码中给出的注释,findStaticMain方法的工作是,根据类名className,调用其指定类的main方法。这个className指的就是本文开头提到过的android.app.ActivityThread。所以findStaticMain方法的实际作用就是调用进程中的ActivityThread类的main方法。
然后,看到上面代码的最后一行,创建了一个MethodAndArgsCaller对象并返回。MethodAndArgsCaller实现了Runnable接口,是一个可执行的任务。到这里,我们要根据方法调用往回看,最终会看到MethodAndArgsCaller对象的run方法是在ZygoteInit的main方法里面
public static void main(String argv[]) {
...
caller = zygoteServer.runSelectLoop(abiList);
...
// We're in the child process and have exited the select loop. Proceed to execute the
// command.
if (caller != null) {
caller.run();
}
}
接下来,我们来看看MethodAndArgsCaller的run方法
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);
}
}
这里的mMethod方法就是ActivityThrea的main方法,调用mMethod的invoke方法后,ActivityThread的main方法就会被动态调用,app进程就进入了ActivityThread的main方法中,讲到这里,app进程的启动过程就算完成了。
总结
本文主要从源码分析的角度,介绍app进程的启动过程,这对于理解应用程序的启动机制以及相关的优化方面,都是很有意义的。面试的时候,也会经常被问到。个人认为其中比较重要的几个关键点是:
- AMS与Zygote进程之间的Socket通信,传递app进程的启动参数
- Zygote进程通过fork自身,创建app进程
- app进程的初始化,创建Binder线程池,启动ActivityThread线程
本文参考:
《Android进阶解密》第三章
Android 安全机制(1)uid 、 gid 与 pid
网友评论