Android系統(tǒng)中戒幔,啟動一個app首先要保證該app所在的進程已經(jīng)被啟動射窒。本文就介紹下app進程搪柑,即應用程序進程的啟動過程裙椭。這里順便提一句,通常所說的啟動應用程序滓走,指的是其根Activity的啟動,和應用程序進程的啟動是兩個概念。
- 了解Android系統(tǒng)啟動過程或者Zygote進程的同學都會知道鲤竹,應用程序進程的創(chuàng)建是通過Zygote進程fork其自身而產(chǎn)生的。①
- 了解Activity的啟動過程的朋友昔榴,又知道在Activity的啟動過程中辛藻,會有一個檢查Activity所在進程是否已經(jīng)啟動,否則就會先創(chuàng)建并啟動其所在進程互订,再啟動Activity吱肌。②
知道了上面兩個過程,再來理解app進程啟動過程仰禽,就相對容易了氮墨。先讓大家有個大概的流程認識:
在Android系統(tǒng)的啟動過程中,Zygote進程的Java框架層中創(chuàng)建一個Server端的Socket吐葵。這個Socket用來等待AMS發(fā)送請求规揪,讓Zygote進程創(chuàng)建新的app進程。Zygote進程接收到請求后温峭,通過fork自身創(chuàng)建app進程猛铅。
具體的創(chuàng)建和啟動過程步驟比較多,我們把上述過程分為兩個部分來分別講解诚镰。
AMS發(fā)送啟動app進程的請求
先來看一下AMS發(fā)送啟動app進程請求過程的時序圖
AMS如果想要啟動app進程奕坟,就需要向Zygote進程發(fā)送創(chuàng)建app進程的請求,AMS會通過調(diào)用startProcessLocked方法來開始這一過程清笨。下面展示相關(guān)的重要代碼
private final void startProcessLocked(ProcessRecord app, String hostingType,
String hostingNameStr, String abiOverride, String entryPoint, String[] entryPointArgs) {
...
try {
...
//獲取要創(chuàng)建的app進程的用戶id
int uid = app.uid;
...
//對用戶組gids進行創(chuàng)建和賦值
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";
...
//調(diào)用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);
}
關(guān)于uid、gid和進程
在Linux中,每個UID標識一個用戶腌歉,每個用戶又至少屬于一個組蛙酪,每個組都有一個唯一標識的GID。每個進程都擁有真實的用戶翘盖、組(UID桂塞、GID)。在Android系統(tǒng)中馍驯,一個用戶的UID表示一個app阁危。app在安裝時被分配了用戶UID,其在設備上存續(xù)期間汰瘫,用戶UID保持不變狂打。對于普通的用戶程序,GID和UID相同混弥。詳細介紹可以參考Android 安全機制(1)uid 趴乡、 gid 與 pid
entryPoint 希望讀者能夠留意這個變量值,它關(guān)系到app進程創(chuàng)建后的初始化過程蝗拿,后面會提到晾捏。
接下來查看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);
}
...
}
一目了然,其內(nèi)部直接調(diào)用了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);
}
}
它又調(diào)用了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);
}
}
這個方法前面一段是創(chuàng)建字符串列表argsForZygote萤捆,并將app進程的啟動參數(shù)放在argsForZygote中裙品。方法最后調(diào)用了zygoteSendArgsAndGetResult方法。需要注意的是zygoteSendArgsAndGetResult方法的第一個參數(shù)openZygoteSocketIfNeeded俗或,稍后會講到市怎。第二個參數(shù)就是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進程的啟動參數(shù)argsForZygote寫入到zygoteState中辛慰,zygoteState是ZygoteProcess的靜態(tài)內(nèi)部類区匠,用于表示與Zygote進程的通信狀態(tài)。現(xiàn)在再回過頭去看帅腌,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方法中會創(chuàng)建name為"zygote"的Server端Socket。在上面的代碼中溺职,我們也看到了調(diào)用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方法里面,調(diào)用了LocalSocket的connect方法状原。而這個LocalSocket就是Android里面的Socket實現(xiàn)聋呢,和java里面的Socket類一樣苗踪,實現(xiàn)了Closeable接口颠区。
至此,我們已經(jīng)可以看出通铲,AMS和Zygote進程建立Socket連接的過程和基于Java的Socket連接過程是很相似的毕莱。
Zygote接收請求并創(chuàng)建app進程
同樣,我們先看一下這部分的時序圖我們先從颅夺,Zygote進程啟動過程中會創(chuàng)建一個Server端的Socket朋截,這個Socket用來等待AMS發(fā)送請求講起。
這個過程是在ZygoteInit的main方法中執(zhí)行的吧黄。
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);
...
//創(chuàng)建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方法創(chuàng)建了一個Server段的Socket,這個name為"zygote"的Socket用來等待AMS請求Zygote拗慨,以創(chuàng)建新的app進程廓八。最后調(diào)用zygoteServer的runSelectLoop方法來等待或者說監(jiān)聽AMS請求創(chuàng)建新的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)的循環(huán)剧蹂,不停的遍歷ArrayList<ZygoteConnection>類型的peers列表,執(zhí)行其中每個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進程的啟動參數(shù)
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 {
//當前代碼是在子進程中執(zhí)行的
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);
}
}
上面這段代碼中宠叼,首先調(diào)用readArgumentList方法獲取app進程的啟動參數(shù),并封裝到Arguments類型的parsedArgs參數(shù)中其爵,然后會調(diào)用Zygote的forkAndSpecialize方法來創(chuàng)建app進程冒冬,參數(shù)就是parsedArgs中的app進程啟動參數(shù),然后返回pid摩渺。forkAndSpecialize方法主要是通過fork當前進程简烤,也就是Zygote進程來創(chuàng)建一個子進程的。如果pid為0证逻,那接下去的代碼就是在新創(chuàng)建的子進程中執(zhí)行乐埠。這時會調(diào)用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) {
//關(guān)閉LocalSocket
closeSocket();
...
if (!isZygote) {
return ZygoteInit.zygoteInit(parsedArgs.targetSdkVersion, parsedArgs.remainingArgs,
null /* classLoader */);
...
}
在handleChildProc方法中,需要重點關(guān)注的代碼就是調(diào)用了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();
//創(chuàng)建Binder線程池
ZygoteInit.nativeZygoteInit();
return RuntimeInit.applicationInit(targetSdkVersion, argv, classLoader);
}
zygoteInit方法最后調(diào)用了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方法最后調(diào)用了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方法的工作是伤疙,根據(jù)類名className,調(diào)用其指定類的main方法辆影。這個className指的就是本文開頭提到過的android.app.ActivityThread徒像。所以findStaticMain方法的實際作用就是調(diào)用進程中的ActivityThread類的main方法。
然后蛙讥,看到上面代碼的最后一行锯蛀,創(chuàng)建了一個MethodAndArgsCaller對象并返回。MethodAndArgsCaller實現(xiàn)了Runnable接口次慢,是一個可執(zhí)行的任務旁涤。到這里,我們要根據(jù)方法調(diào)用往回看迫像,最終會看到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方法,調(diào)用mMethod的invoke方法后闻妓,ActivityThread的main方法就會被動態(tài)調(diào)用菌羽,app進程就進入了ActivityThread的main方法中,講到這里由缆,app進程的啟動過程就算完成了注祖。
總結(jié)
本文主要從源碼分析的角度,介紹app進程的啟動過程犁功,這對于理解應用程序的啟動機制以及相關(guān)的優(yōu)化方面氓轰,都是很有意義的。面試的時候浸卦,也會經(jīng)常被問到署鸡。個人認為其中比較重要的幾個關(guān)鍵點是:
- AMS與Zygote進程之間的Socket通信,傳遞app進程的啟動參數(shù)
- Zygote進程通過fork自身限嫌,創(chuàng)建app進程
- app進程的初始化靴庆,創(chuàng)建Binder線程池,啟動ActivityThread線程
本文參考:
《Android進階解密》第三章
Android 安全機制(1)uid 怒医、 gid 與 pid