源碼Android 7.1.1
Android常識骡和,App主線程初始化了Looper估脆,調(diào)用prepare的地方是ActivityThread.main函數(shù)件相。問題來了韵卤,App的main函數(shù)在哪兒調(diào)用,本文嘗試尋找答案着撩。
啟動App進程
Activity啟動過程的一環(huán)是調(diào)用ActivityStackSupervisor.startSpecificActivityLocked诅福,如果App所在進程還不存在,首先調(diào)用AMS的startProcessLocked:
void startSpecificActivityLocked(ActivityRecord r,
boolean andResume, boolean checkConfig) {
// Is this activity's application already running?
ProcessRecord app = mService.getProcessRecordLocked(r.processName,
r.info.applicationInfo.uid, true);
r.task.stack.setLaunchTime(r);
if (app != null && app.thread != null) {
//...
}
mService.startProcessLocked(r.processName, r.info.applicationInfo, true, 0,
"activity", r.intent.getComponent(), false, false, true);
}
startProcessLocked函數(shù)有多個重載睹酌,看最長的那個权谁,最重要是調(diào)用了Process.start剩檀。
if (entryPoint == null) entryPoint = "android.app.ActivityThread";
Process.ProcessStartResult startResult = Process.start(entryPoint,
app.processName, uid, uid, gids, debugFlags, mountExternal,
app.info.targetSdkVersion, app.info.seinfo, requiredAbi, instructionSet,
app.info.dataDir, entryPointArgs);
第一個參數(shù)是android.app.ActivityThread憋沿,執(zhí)行的目標,后文用到再說沪猴。
Process.start簡單地調(diào)用了startViaZygote辐啄,封裝一些參數(shù)采章,再調(diào)用zygoteSendArgsAndGetResult。顧名思義壶辜,接下來進程的啟動工作交給Zygote悯舟。
Zygote
Zygote翻譯過來意思是“受精卵”,這也是Zygote的主要工作——孵化進程砸民。概括Zygote的主要工作有以下三點抵怎,ZygoteInit的main函數(shù)也清晰地體現(xiàn)了。Zygote的啟動和其他作用另文分析岭参,這次關(guān)注Zygote對Socket的監(jiān)聽反惕。
- registerZygoteSocket:啟動Socket的Server端
- preload:預(yù)加載資源
- startSystemServer:啟動system_server進程
public static void main(String argv[]) {
// Mark zygote start. This ensures that thread creation will throw
// an error.
ZygoteHooks.startZygoteNoThreadCreation();
try {
//...
registerZygoteSocket(socketName);
//...
preload();
//...
if (startSystemServer) {
startSystemServer(abiList, socketName);
}
//...
runSelectLoop(abiList);
//...
} catch (MethodAndArgsCaller caller) {
caller.run();
} catch (Throwable ex) {
//...
}
}
最后Zygote執(zhí)行runSelectLoop,無限循環(huán)等待處理進程啟動的請求演侯。
private static void runSelectLoop(String abiList) throws MethodAndArgsCaller {
ArrayList<FileDescriptor> fds = new ArrayList<FileDescriptor>();
ArrayList<ZygoteConnection> peers = new ArrayList<ZygoteConnection>();
fds.add(sServerSocket.getFileDescriptor());
peers.add(null);
while (true) {
StructPollfd[] pollFds = new StructPollfd[fds.size()];
for (int i = 0; i < pollFds.length; ++i) {
pollFds[i] = new StructPollfd();
pollFds[i].fd = fds.get(i);
pollFds[i].events = (short) POLLIN;
}
try {
Os.poll(pollFds, -1);
} catch (ErrnoException ex) {
throw new RuntimeException("poll failed", ex);
}
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();
if (done) {
peers.remove(i);
fds.remove(i);
}
}
}
}
}
與Zygote通信使用的IPC方式是socket姿染,類型是LocalSocket,實質(zhì)是對Linux的LocalSocket的封裝秒际。與記憶中socket綁定需要IP和端口不同悬赏,LocalSocket使用FileDescriptor文件描述符,它可以表示文件娄徊,也可以表示socket闽颇。
runSelectLoop維護了兩個列表,分別保存文件描述符FileDescriptor和ZygoteConnection寄锐,兩者一一對應(yīng)进萄。index=0的FileDescriptor表示ServerSocket,因此index=0的ZygoteConnection用null填充锐峭。
在每次循環(huán)中中鼠,判斷fds里哪個可讀:
- 當i=0時,表示有新的client沿癞,調(diào)用acceptCommandPeer創(chuàng)建ZygoteConnection并保存
- 當i>0時援雇,表示已建立連接的socket中有新的命令,調(diào)用runOnce函數(shù)執(zhí)行
fork進程
runOnce函數(shù)非常長椎扬,我們只關(guān)注進程創(chuàng)建部分惫搏。
boolean runOnce() throws ZygoteInit.MethodAndArgsCaller {
//...
try {
//...
pid = Zygote.forkAndSpecialize(parsedArgs.uid, parsedArgs.gid, parsedArgs.gids,
parsedArgs.debugFlags, rlimits, parsedArgs.mountExternal, parsedArgs.seInfo,
parsedArgs.niceName, fdsToClose, parsedArgs.instructionSet,
parsedArgs.appDataDir);
} catch (ErrnoException ex) {
//...
}
try {
if (pid == 0) {
// in child
IoUtils.closeQuietly(serverPipeFd);
serverPipeFd = null;
handleChildProc(parsedArgs, descriptors, childPipeFd, newStderr);
// should never get here, the child is expected to either
// throw ZygoteInit.MethodAndArgsCaller or exec().
return true;
} else {
// in parent...pid of < 0 means failure
IoUtils.closeQuietly(childPipeFd);
childPipeFd = null;
return handleParentProc(pid, descriptors, serverPipeFd, parsedArgs);
}
} finally {
//...
}
}
首先調(diào)用了forkAndSpecialize函數(shù),創(chuàng)建進程返回一個pid蚕涤。
public static int forkAndSpecialize(int uid, int gid, int[] gids, int debugFlags,
int[][] rlimits, int mountExternal, String seInfo, String niceName, int[] fdsToClose,
String instructionSet, String appDataDir) {
VM_HOOKS.preFork();
int pid = nativeForkAndSpecialize(
uid, gid, gids, debugFlags, rlimits, mountExternal, seInfo, niceName, fdsToClose,
instructionSet, appDataDir);
// Enable tracing as soon as possible for the child process.
if (pid == 0) {
Trace.setTracingEnabled(true);
// Note that this event ends at the end of handleChildProc,
Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "PostFork");
}
VM_HOOKS.postForkCommon();
return pid;
}
在forkAndSpecialize中核心就是利用JNI調(diào)用native的fork函數(shù)筐赔,調(diào)用之前會執(zhí)行VM_HOOKS.preFork(),調(diào)用之后執(zhí)行VM_HOOKS.postForkCommon()揖铜。
VM_HOOKS.preFork()的功能是停止Zygote的4個Daemon子線程的運行茴丰,確保Zygote是單線程,提升fork效率。當線程停止之后初始化gc堆贿肩。VM_HOOKS.postForkCommon()可以看作是逆操作峦椰,關(guān)于虛擬機更加深入的內(nèi)容暫不討論。
native private static int nativeForkSystemServer(int uid, int gid, int[] gids, int debugFlags,
int[][] rlimits, long permittedCapabilities, long effectiveCapabilities);
nativeForkSystemServer是一個native函數(shù)汰规,對應(yīng)com_android_internal_os_Zygote.cpp的com_android_internal_os_Zygote_nativeForkAndSpecialize汤功,繼續(xù)調(diào)用了ForkAndSpecializeCommon,最核心一句則是調(diào)用fork函數(shù)溜哮。
pid_t pid = fork();
簡單回憶fork函數(shù)作用滔金,它復(fù)制當前進程,屬性和當前進程相同茂嗓,使用copy on write(寫時復(fù)制)鹦蠕。執(zhí)行函數(shù)后,新進程已經(jīng)創(chuàng)建在抛,返回的pid=0钟病;對于被復(fù)制的進程,返回新進程的pid刚梭;出現(xiàn)錯誤時肠阱,返回-1。如下圖:
因此朴读,執(zhí)行forkAndSpecialize函數(shù)后屹徘,runOnce后續(xù)的代碼分別在兩個進程中執(zhí)行,判斷當前的pid衅金,區(qū)分是在當前進程還是新進程噪伊。
- pid == 0:新進程,調(diào)用handleChildProc
- pid != 0:當前進程氮唯,調(diào)用handleParentProc
handleParentProc函數(shù)是當前進程進行清理的過程鉴吹,比較簡單。我們重點來看新進程開展工作的handleChildProc函數(shù)惩琉。
新進程的初始化
private void handleChildProc(Arguments parsedArgs,
FileDescriptor[] descriptors, FileDescriptor pipeFd, PrintStream newStderr)
throws ZygoteInit.MethodAndArgsCaller {
//...
if (parsedArgs.invokeWith != null) {
WrapperInit.execApplication(parsedArgs.invokeWith,
parsedArgs.niceName, parsedArgs.targetSdkVersion,
VMRuntime.getCurrentInstructionSet(),
pipeFd, parsedArgs.remainingArgs);
} else {
RuntimeInit.zygoteInit(parsedArgs.targetSdkVersion,
parsedArgs.remainingArgs, null /* classLoader */);
}
}
兩種啟動方式:
- WrapperInit.execApplication
- RuntimeInit.zygoteInit
第一種的目的不太懂豆励,先掛起,后續(xù)分析瞒渠。
第二種RuntimeInit.zygoteInit良蒸,繼續(xù)調(diào)用applicationInit,離我們的目標越來越近了伍玖,最終調(diào)用到invokeStaticMain嫩痰。
private static void invokeStaticMain(String className, String[] argv, ClassLoader classLoader)
throws ZygoteInit.MethodAndArgsCaller {
Class<?> cl;
try {
cl = Class.forName(className, true, classLoader);
} catch (ClassNotFoundException ex) {
//...
}
Method m;
try {
m = cl.getMethod("main", new Class[] { String[].class });
} catch (NoSuchMethodException ex) {
//...
}
int modifiers = m.getModifiers();
if (! (Modifier.isStatic(modifiers) && Modifier.isPublic(modifiers))) {
throw new RuntimeException(
"Main method is not public and static on " + className);
}
throw new ZygoteInit.MethodAndArgsCaller(m, argv);
}
在這里使用反射獲得需要被執(zhí)行的類和函數(shù),目標函數(shù)當然就是main窍箍,目標類來自ActivityManagerService.startProcessLocked里的變量entryPoint串纺,前面有說過丽旅。
entryPoint = "android.app.ActivityThread"
最后一句執(zhí)行throw new ZygoteInit.MethodAndArgsCaller(m, argv),讓人有些費解造垛。
public static class MethodAndArgsCaller extends Exception
implements Runnable {
//...
public void run() {
try {
mMethod.invoke(null, new Object[] { mArgs });
} catch (IllegalAccessException ex) {
//...
}
}
}
通過上面的分析,容易知道MethodAndArgsCaller就是App的主線程晰搀,里面的run方法實現(xiàn)了反射的調(diào)用五辽。什么時候觸發(fā)執(zhí)行,為什么要這樣設(shè)計外恕?
既然MethodAndArgsCaller是異常杆逗,拋出它肯定某個地方會接收,回顧一路的調(diào)用鏈:
- ZytoteInit.main
- ZytoteInit.runSelectLoop
- ZygoteConnection.runOnce
- ZygoteConnection.handleChildProc
- RuntimeInit.zygoteInit
- RuntimeInit.applicationInit
- RuntimeInit.invokeStaticMain
看前面的ZytoteInit.main函數(shù)鳞疲,catch了MethodAndArgsCaller異常罪郊,直接調(diào)用了run函數(shù)。注釋里解釋了為什么要這樣做:
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.
函數(shù)在虛擬機是保存在棧中尚洽,每調(diào)用一個函數(shù)悔橄,就將函數(shù)相關(guān)數(shù)據(jù)壓入棧;執(zhí)行完函數(shù)腺毫,將函數(shù)從棧中彈出癣疟。因此,棧底的就是main函數(shù)潮酒。
在上面的研究中睛挚,新進程創(chuàng)建后,經(jīng)歷一系列函數(shù)的調(diào)用才到main函數(shù)急黎,如果直接調(diào)用main函數(shù)扎狱,調(diào)用鏈中關(guān)于初始化的函數(shù)會一直存在。為了清理這部分函數(shù)勃教,使用了拋出異常的方式淤击,沒有捕獲異常的函數(shù)會馬上結(jié)束,ZytoteInit.main之上的函數(shù)都會結(jié)束故源,達到清理的目的遭贸。
最后補充一點,從handleChildProc函數(shù)開始心软,一系列過程調(diào)用了ActivityThread的main函數(shù)壕吹,這不是啟動App獨有的,后續(xù)研究啟動SystemServer進程時删铃,你會發(fā)現(xiàn)邏輯都是一樣耳贬。