應(yīng)用程序的進(jìn)程啟動(dòng)
簡(jiǎn)介
當(dāng)我們打開android手機(jī)的時(shí)候逮光,不知道你是否想過(guò)app是如何啟動(dòng)的呢?
接下來(lái),我將從源碼角度進(jìn)行解析,當(dāng)然筑悴,本文作為上篇装盯,是介紹應(yīng)用程序的進(jìn)程啟動(dòng)過(guò)程,而不是應(yīng)用程序的啟動(dòng)過(guò)程绑谣,他們的區(qū)別就是煮飯前要準(zhǔn)備鍋具壤玫,沒(méi)有鍋具就無(wú)法煮飯班缎,本文就是準(zhǔn)備鍋具的,但是也不簡(jiǎn)單哦苛败。
文章將從兩個(gè)方面介紹,一個(gè)AMS發(fā)送請(qǐng)求,一個(gè)是Zygote接受請(qǐng)求绩衷。
AMS就是Activity Manager System低缩,管理Activity的,而Zygote就是創(chuàng)建進(jìn)程的一個(gè)進(jìn)程慨仿,所以AMS要想創(chuàng)建進(jìn)程必須從Zygote那里進(jìn)行申請(qǐng),一系列的故事,就此展開如筛。
AMS發(fā)送請(qǐng)求
從圖中可以簡(jiǎn)略看出做粤,首先AMS自己調(diào)用了自己的startProcessLocked
方法,會(huì)創(chuàng)建相應(yīng)的用戶id,用戶id組刚夺,以及對(duì)應(yīng)的應(yīng)用程序進(jìn)程的主線程名——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中有一個(gè)參數(shù)名為requireAbi
邦邦,在下面會(huì)改成abi
起到比對(duì)的重要作用。
startProcessLocked
方法內(nèi)部會(huì)調(diào)用Process的start
方法,而這個(gè)start方法只干了一件事,就是調(diào)用ZygoteProcess
的start
方法捉偏。
ZygoteProcess
的作用在于保持與Zygote通信的狀態(tài)讹躯,在其start
方法中锉罐,會(huì)調(diào)用StartViaZygote
方法馆揉,這里的出現(xiàn)了下面很重要的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
方法會(huì)維護(hù)一個(gè)名為argsForZygote
的列表抖拦,顧名思義升酣,所有能夠啟動(dòng)應(yīng)用程序的進(jìn)程的參數(shù),都會(huì)保存在這里态罪。然后這個(gè)列表作為一個(gè)參數(shù)噩茄,被傳入到ZygoteSendArgsAndGetResult
方法中,這個(gè)方法也是在StartViaZygote
方法中被調(diào)用.另外要注意的是复颈,openZygoteSocketIfNeeded
方法也是作為參數(shù)傳入了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<String> argsForZygote = new ArrayList<String>();
argsForZygote.add("--runtime-args");
argsForZygote.add("--setuid=" + uid);
argsForZygote.add("--setgid=" + gid);
...
synchronized(mLock) {
return zygoteSendArgsAndGetResult(openZygoteSocketIfNeeded(abi), argsForZygote);
}
}
ZygoteSendArgsAndGetResult
方法作用是將傳入的參數(shù)沥割,也就是argsForZygote
中保存的數(shù)據(jù)寫入到ZygoteState
中,從圖中我們雖然將ZygoteState
和ZygoteProcess
做了并列處理凿菩,但是ZygoteState
是ZygoteProcess
的一個(gè)靜態(tài)內(nèi)部類机杜,上面提到的ZygoteProcess
的作用在于保持與Zygote通信的狀態(tài),其功能的完成就是在ZygoteState
中衅谷。
private static Process.ProcessStartResult zygoteSendArgsAndGetResult(
ZygoteState zygoteState, ArrayList<String> 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
方法的第一個(gè)參數(shù)變成了ZygoteState
,也就是說(shuō)获黔,openZygoteSocketIfNeeded
方法的返回值是ZygoteState
蚀苛。
openZygoteSocketIfNeeded
方法也處于ZygoteProcess
中,就寫在StartViaZygote
下面玷氏,可以看到堵未,在第6行與Zygote進(jìn)程建立了連接。
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,比對(duì)其是否與需要的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呢渗蟹?因?yàn)閆ygote是最先創(chuàng)建的進(jìn)程,如果沒(méi)有Zygote耻陕,我們的應(yīng)用程序的進(jìn)程也無(wú)從談起拙徽,而Zygote為了創(chuàng)建其他進(jìn)程,他會(huì)設(shè)置一個(gè)Socket監(jiān)聽诗宣,就是通過(guò)這個(gè)Socket膘怕,完成與Zygote的連接。
而對(duì)于主模式和輔助模式召庞,這里不細(xì)講岛心,就是有兩次連接并匹配abi
的機(jī)會(huì)。
要匹配abi
的原因在于篮灼,Zygote雖然可以fork自己不斷創(chuàng)建進(jìn)程忘古,但是畢竟是有限的,而且需要資源诅诱,只有啟動(dòng)某個(gè)應(yīng)用程序所必須的進(jìn)程才有被創(chuàng)建的意義髓堪,這時(shí)候就在一開始的AMS中給設(shè)置一個(gè)abi
,如果與后來(lái)的Zygote匹配娘荡,證明是需要被創(chuàng)建的干旁。
當(dāng)匹配完成時(shí),就會(huì)返回一個(gè)primaryZygoteState
炮沐,此時(shí)AMS發(fā)送請(qǐng)求這一流程就結(jié)束了争群,接下來(lái),就是Zygote如何接受請(qǐng)求并創(chuàng)建應(yīng)用程序的進(jìn)程的過(guò)程了大年。
Zygote接受請(qǐng)求
依然是時(shí)序圖换薄,甚至說(shuō)玉雾,有了時(shí)序圖,根本就不需要看后面的文章啦轻要。
看起來(lái)比上一個(gè)圖稍微復(fù)雜些复旬,不過(guò)可以發(fā)現(xiàn)還是很有規(guī)律的,Zygote先到ZygoteServer
再回來(lái)冲泥,再到ZygoteConnection
赢底,再回來(lái)……
所以,后面幾個(gè)方法都在ZygoteInit
中被調(diào)用柏蘑,而且時(shí)間先后就是圖中所示。
在AMS發(fā)送請(qǐng)求完成后粹庞,Zygote會(huì)連接到一個(gè)ZygoteState
咳焚,而這個(gè)ZygoteState
在之前的ZygoteSendArgsAndGetResult
方法中被寫入了argsForZygote
所保存的數(shù)據(jù),這些數(shù)據(jù)就是請(qǐng)求的具體參數(shù)庞溜。
現(xiàn)在Zygote將會(huì)收到這些參數(shù)革半,并在ZygoteInit
中進(jìn)行相應(yīng)操作。
ZygoteInit
首先會(huì)執(zhí)行其main方法流码。
public static void main(String argv[]) {
...
try {
...
//設(shè)置Socket監(jiān)聽
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);//啟動(dòng)SystemServer進(jìn)程
}
Log.i(TAG, "Accepting command socket connections");
zygoteServer.runSelectLoop(abiList);//等待AMS請(qǐng)求
zygoteServer.closeServerSocket();
...
}
這里最后一個(gè)注釋又官,就是一切都準(zhǔn)備好了,只等AMS發(fā)來(lái)請(qǐng)求漫试,現(xiàn)在我們看看RunSelectLoop
方法是如何做的六敬,猜測(cè)既然是等待,那肯定有一個(gè)死循環(huán)while驾荣,這個(gè)函數(shù)就在ZygoteServer
之中外构。
void runSelectLoop(String abiList) throws Zygote.MethodAndArgsCaller {
ArrayList<FileDescriptor> fds = new ArrayList<FileDescriptor>();
ArrayList<ZygoteConnection> peers = new ArrayList<ZygoteConnection>();
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 {
//一個(gè)連接一個(gè)連接的運(yùn)行
boolean done = peers.get(i).runOnce(this);
if (done) {
peers.remove(i);
fds.remove(i);
}
}
}
}
}
果然有一個(gè)while(true),哈哈播掷,獎(jiǎng)勵(lì)自己晚上早睡十分鐘审编。
可以看到這里維護(hù)了一個(gè)ZygoteConnection
型的列表,每當(dāng)連接不夠時(shí)歧匈,就會(huì)增加垒酬,然后一個(gè)一個(gè)連接的進(jìn)行執(zhí)行,執(zhí)行玩一個(gè)連接件炉,就移除一個(gè)勘究。
runOnce
方法的作用就是讀取argsForZygote
所保存的數(shù)據(jù),完成交接妻率,這個(gè)方法顯然乱顾,在ZygoteConnection
中。
boolean runOnce(ZygoteServer zygoteServer) throws Zygote.MethodAndArgsCaller {
String args[];
Arguments parsedArgs = null;
FileDescriptor[] descriptors;
try {
args = readArgumentList(); //讀取之前寫入的數(shù)據(jù)
descriptors = mSocket.getAncillaryFileDescriptors();
} catch (IOException ex) {
Log.w(TAG, "IOException on command socket " + ex.getMessage());
closeSocket();
return true;
}
...
try {
parsedArgs = new Arguments(args);
...
//創(chuàng)建應(yīng)用程序進(jìn)程
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) {//當(dāng)前邏輯運(yùn)行在子程序中
zygoteServer.closeServerSocket();
IoUtils.closeQuietly(serverPipeFd);
serverPipeFd = null;
handleChildProc(parsedArgs, descriptors, childPipeFd, newStderr);
return true;
}
...
}
}
如果在18行處的pid返回值為0宫静,那么恭喜走净,此時(shí)的子進(jìn)程已經(jīng)創(chuàng)建完成券时,各種id和參數(shù)都傳進(jìn)去了,此時(shí)就會(huì)調(diào)用handleChildProc
方法來(lái)處理出現(xiàn)的應(yīng)用程序的進(jìn)程(還是要提醒伏伯,本文創(chuàng)建的是應(yīng)用程序的進(jìn)程橘洞,而非應(yīng)用程序本身)。
handleChildProc
方法回調(diào)了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 */);
}
}
現(xiàn)在我們回到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);
}
注意到這個(gè)方法是一個(gè)靜態(tài)的final方法,在12行代碼處候引,會(huì)執(zhí)行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);
}
同樣在最后一行,調(diào)用了invokeStaticMain
方法澄干,這里傳入了三個(gè)參數(shù)逛揩,第一個(gè)參數(shù)args.startClass
就是本文一開始說(shuō)的那個(gè)主線程的名字android.app.MainThread
。
終于我們到了最后一站麸俘!現(xiàn)在我們進(jìn)入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
,也就是主線程名字从媚,通過(guò)第6行的反射獲取到對(duì)應(yīng)的類逞泄,命名為c1,然后再通過(guò)15行代碼獲取主方法main静檬,并將main傳入到MethodAndArgsCaller
方法中炭懊,當(dāng)做異常拋出。
為什么這里要用拋出異常的方法呢拂檩?
聯(lián)想到這是最后一步侮腹,拋出異常的方式將會(huì)清除設(shè)置過(guò)程中需要的堆棧幀,僅僅留下一個(gè)main方法稻励,使得main方法看起來(lái)像一個(gè)入口方法父阻,畢竟,前面那么多的工作望抽,那么多類的流血犧牲加矛,最后都是為了成就他。當(dāng)他出現(xiàn)時(shí)煤篙,前面的東西斟览,也就沒(méi)有必要存在了。
現(xiàn)在異常拋出了辑奈,誰(shuí)來(lái)接受呢苛茂?當(dāng)然已烤,是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;
}
}
可以看到胯究,當(dāng)出現(xiàn)了一個(gè)MethodAndArgsCaller
型的異常的時(shí)候,main會(huì)捕捉到躁绸,并且執(zhí)行他的run方法裕循,這個(gè)run方法在MethodAndArgsCaller
中,他是一個(gè)靜態(tài)類净刮。
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) {
...
}
}
}
調(diào)用了invoke
方法后剥哑,ActivityThread里面的main方法就會(huì)被動(dòng)態(tài)調(diào)用,應(yīng)用程序的進(jìn)程就進(jìn)入了這一main方法中淹父。
經(jīng)過(guò)上面的種種操作星持,我們創(chuàng)建了應(yīng)用程序的進(jìn)程并且運(yùn)行了ActivityThread。
小結(jié)
簡(jiǎn)單來(lái)說(shuō)就是要想創(chuàng)建應(yīng)用程序的進(jìn)程弹灭,需要AMS發(fā)送請(qǐng)求,傳入主線程名揪垄,這一請(qǐng)求必須符合Zygote的abi
穷吮,并且將請(qǐng)求參數(shù)寫在ZygoteState
中。
然后Zygote讀取參數(shù)饥努,fork自身去生成新的進(jìn)程捡鱼,如果返回的pid為0,那么就完成了應(yīng)用程序的進(jìn)程的創(chuàng)建酷愧。
然后通過(guò)反射將主線程名轉(zhuǎn)換為主線程的類驾诈,獲得其main方法,通過(guò)拋出一個(gè)異常清除掉之前的堆棧幀溶浴,在main方法里面接受這個(gè)異常乍迄,并執(zhí)行run方法以激活main方法,使得應(yīng)用程序的進(jìn)程進(jìn)入到main方法中得以運(yùn)行士败。
寫了一早上闯两,呼~,準(zhǔn)備吃飯啦谅将,還有什么不懂的可以評(píng)論區(qū)提問(wèn)哦漾狼。
本文取材自《Android進(jìn)階解密》,資源在微信公眾號(hào)【小松漫步】饥臂,一起交流學(xué)習(xí)吧逊躁。