詳解Android應用main函數的調用

Android常識,App主線程初始化了Looper,調用prepare的地方是ActivityThread.main函數。問題來了,App的main函數在哪兒調用,下面我們來一起學習一下吧

啓動App進程

Activity啓動過程的一環是調用ActivityStackSupervisor.startSpecificActivityLocked,如果App所在進程還不存在,首先調用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函數有多個重載,看最長的那個,最重要是調用了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);

第一個參數是android.app.ActivityThread,執行的目標,後文用到再說。

Process.start簡單地調用了startViaZygote,封裝一些參數,再調用zygoteSendArgsAndGetResult。顧名思義,接下來進程的啓動工作交給Zygote。

Zygote

Zygote翻譯過來意思是“受精卵”,這也是Zygote的主要工作——孵化進程。概括Zygote的主要工作有以下三點,ZygoteInit的main函數也清晰地體現了。Zygote的啓動和其他作用另文分析,這次關注Zygote對Socket的監聽。

1.registerZygoteSocket:啓動Socket的Server端

2.preload:預加載資源

3.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執行runSelectLoop,無限循環等待處理進程啓動的請求。

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,實質是對Linux的LocalSocket的封裝。與記憶中socket綁定需要IP和端口不同,LocalSocket使用FileDescriptor文件描述符,它可以表示文件,也可以表示socket。

runSelectLoop維護了兩個列表,分別保存文件描述符FileDescriptor和ZygoteConnection,兩者一一對應。index=0的FileDescriptor表示ServerSocket,因此index=0的ZygoteConnection用null填充。

在每次循環中,判斷fds裏哪個可讀:

  • 當i=0時,表示有新的client,調用acceptCommandPeer創建ZygoteConnection並保存
  • 當i>0時,表示已建立連接的socket中有新的命令,調用runOnce函數執行

fork進程

runOnce函數非常長,我們只關注進程創建部分。

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 {
//...
}
}

首先調用了forkAndSpecialize函數,創建進程返回一個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調用native的fork函數,調用之前會執行VM_HOOKS.preFork(),調用之後執行VM_HOOKS.postForkCommon()。

VM_HOOKS.preFork()的功能是停止Zygote的4個Daemon子線程的運行,確保Zygote是單線程,提升fork效率。當線程停止之後初始化gc堆。VM_HOOKS.postForkCommon()可以看作是逆操作,關於虛擬機更加深入的內容暫不討論。

native private static int nativeForkSystemServer(int uid, int gid, int[] gids, int debugFlags,
int[][] rlimits, long permittedCapabilities, long effectiveCapabilities);

nativeForkSystemServer是一個native函數,對應com_android_internal_os_Zygote.cpp的com_android_internal_os_Zygote_nativeForkAndSpecialize,繼續調用了ForkAndSpecializeCommon,最核心一句則是調用fork函數。

pid_t pid = fork();

簡單回憶fork函數作用,它複製當前進程,屬性和當前進程相同,使用copy on write(寫時複製)。執行函數後,新進程已經創建,返回的pid=0;對於被複制的進程,返回新進程的pid;出現錯誤時,返回-1。

因此,執行forkAndSpecialize函數後,runOnce後續的代碼分別在兩個進程中執行,判斷當前的pid,區分是在當前進程還是新進程。

  • pid == 0:新進程,調用handleChildProc
  • pid != 0:當前進程,調用handleParentProc

handleParentProc函數是當前進程進行清理的過程,比較簡單。我們重點來看新進程開展工作的handleChildProc函數。

新進程的初始化

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

第一種的目的不太懂,先掛起,後續分析。

第二種RuntimeInit.zygoteInit,繼續調用applicationInit,離我們的目標越來越近了,最終調用到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);
}

在這裏使用反射獲得需要被執行的類和函數,目標函數當然就是main,目標類來自ActivityManagerService.startProcessLocked裏的變量entryPoint,前面有說過。

entryPoint = "android.app.ActivityThread"

最後一句執行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方法實現了反射的調用。什麼時候觸發執行,爲什麼要這樣設計?

既然MethodAndArgsCaller是異常,拋出它肯定某個地方會接收,回顧一路的調用鏈:

  • ZytoteInit.main
  • ZytoteInit.runSelectLoop
  • ZygoteConnection.runOnce
  • ZygoteConnection.handleChildProc
  • RuntimeInit.zygoteInit
  • RuntimeInit.applicationInit
  • RuntimeInit.invokeStaticMain

看前面的ZytoteInit.main函數,catch了MethodAndArgsCaller異常,直接調用了run函數。註釋裏解釋了爲什麼要這樣做:

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.

函數在虛擬機是保存在棧中,每調用一個函數,就將函數相關數據壓入棧;執行完函數,將函數從棧中彈出。因此,棧底的就是main函數。

在上面的研究中,新進程創建後,經歷一系列函數的調用纔到main函數,如果直接調用main函數,調用鏈中關於初始化的函數會一直存在。爲了清理這部分函數,使用了拋出異常的方式,沒有捕獲異常的函數會馬上結束,ZytoteInit.main之上的函數都會結束,達到清理的目的。

最後補充一點,從handleChildProc函數開始,一系列過程調用了ActivityThread的main函數,這不是啓動App獨有的,後續研究啓動SystemServer進程時,你會發現邏輯都是一樣。

以上就是本文的全部內容,希望對大家的學習有所幫助,也希望大家多多支持神馬文庫。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章