一、簡介
當我們打開android手機的時候,不知道你是否想過app是如何啓動的呢?
接下來,我將從源碼角度進行解析,當然,本文作爲上篇,是介紹應用程序的進程啓動過程,而不是應用程序的啓動過程,他們的區別就是煮飯前要準備鍋具,沒有鍋具就無法煮飯,本文就是準備鍋具的,但是也不簡單哦。
文章將從兩個方面介紹,一個AMS發送請求,一個是Zygote接受請求。
AMS就是Activity Manager System,管理Activity的,而Zygote就是創建進程的一個進程,所以AMS要想創建進程必須從Zygote那裏進行申請,一系列的故事,就此展開。
二、AMS發送請求
從圖中可以簡略看出,首先AMS自己調用了自己的startProcessLocked
方法,會創建相應的用戶id,用戶id組,以及對應的應用程序進程的主線程名——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中有一個參數名爲requireAbi
,在下面會改成abi
起到比對的重要作用。
startProcessLocked
方法內部會調用Process的start
方法,而這個start方法只幹了一件事,就是調用ZygoteProcess
的start
方法。
ZygoteProcess
的作用在於保持與Zygote通信的狀態,在其start
方法中,會調用StartViaZygote
方法,這裏的出現了下面很重要的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
方法會維護一個名爲argsForZygote
的列表,顧名思義,所有能夠啓動應用程序的進程的參數,都會保存在這裏。然後這個列表作爲一個參數,被傳入到ZygoteSendArgsAndGetResult
方法中,這個方法也是在StartViaZygote
方法中被調用.另外要注意的是,openZygoteSocketIfNeeded
方法也是作爲參數傳入了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
方法作用是將傳入的參數,也就是argsForZygote
中保存的數據寫入到ZygoteState
中,從圖中我們雖然將ZygoteState
和ZygoteProcess
做了並列處理,但是ZygoteState
是ZygoteProcess
的一個靜態內部類,上面提到的ZygoteProcess
的作用在於保持與Zygote通信的狀態,其功能的完成就是在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
方法的第一個參數變成了ZygoteState
,也就是說,openZygoteSocketIfNeeded
方法的返回值是ZygoteState
。
openZygoteSocketIfNeeded
方法也處於ZygoteProcess
中,就寫在StartViaZygote
下面,可以看到,在第6行與Zygote進程建立了連接。
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,比對其是否與需要的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呢?因爲Zygote是最先創建的進程,如果沒有Zygote,我們的應用程序的進程也無從談起,而Zygote爲了創建其他進程,他會設置一個Socket監聽,就是通過這個Socket,完成與Zygote的連接。
而對於主模式和輔助模式,這裏不細講,就是有兩次連接並匹配abi
的機會。
要匹配abi
的原因在於,Zygote雖然可以fork自己不斷創建進程,但是畢竟是有限的,而且需要資源,只有啓動某個應用程序所必須的進程纔有被創建的意義,這時候就在一開始的AMS中給設置一個abi
,如果與後來的Zygote匹配,證明是需要被創建的。
當匹配完成時,就會返回一個primaryZygoteState
,此時AMS發送請求這一流程就結束了,接下來,就是Zygote如何接受請求並創建應用程序的進程的過程了。
三、Zygote接受請求
依然是時序圖,甚至說,有了時序圖,根本就不需要看後面的文章啦。
看起來比上一個圖稍微複雜些,不過可以發現還是很有規律的,Zygote先到ZygoteServer
再回來,再到ZygoteConnection
,再回來……
所以,後面幾個方法都在ZygoteInit
中被調用,而且時間先後就是圖中所示。
在AMS發送請求完成後,Zygote會連接到一個ZygoteState
,而這個ZygoteState
在之前的ZygoteSendArgsAndGetResult
方法中被寫入了argsForZygote
所保存的數據,這些數據就是請求的具體參數。
現在Zygote將會收到這些參數,並在ZygoteInit
中進行相應操作。
ZygoteInit
首先會執行其main方法。
public static void main(String argv[]) {
...
try {
...
//設置Socket監聽
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);//啓動SystemServer進程
}
Log.i(TAG, "Accepting command socket connections");
zygoteServer.runSelectLoop(abiList);//等待AMS請求
zygoteServer.closeServerSocket();
...
}
這裏最後一個註釋,就是一切都準備好了,只等AMS發來請求,現在我們看看RunSelectLoop
方法是如何做的,猜測既然是等待,那肯定有一個死循環while,這個函數就在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 {
//一個連接一個連接的運行
boolean done = peers.get(i).runOnce(this);
if (done) {
peers.remove(i);
fds.remove(i);
}
}
}
}
}
果然有一個while(true),哈哈,獎勵自己晚上早睡十分鐘。
可以看到這裏維護了一個ZygoteConnection
型的列表,每當連接不夠時,就會增加,然後一個一個連接的進行執行,執行玩一個連接,就移除一個。
runOnce
方法的作用就是讀取argsForZygote
所保存的數據,完成交接,這個方法顯然,在ZygoteConnection
中。
boolean runOnce(ZygoteServer zygoteServer) throws Zygote.MethodAndArgsCaller {
String args[];
Arguments parsedArgs = null;
FileDescriptor[] descriptors;
try {
args = readArgumentList(); //讀取之前寫入的數據
descriptors = mSocket.getAncillaryFileDescriptors();
} catch (IOException ex) {
Log.w(TAG, "IOException on command socket " + ex.getMessage());
closeSocket();
return true;
}
...
try {
parsedArgs = new Arguments(args);
...
//創建應用程序進程
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) {//當前邏輯運行在子程序中
zygoteServer.closeServerSocket();
IoUtils.closeQuietly(serverPipeFd);
serverPipeFd = null;
handleChildProc(parsedArgs, descriptors, childPipeFd, newStderr);
return true;
}
...
}
}
如果在18行處的pid返回值爲0,那麼恭喜,此時的子進程已經創建完成,各種id和參數都傳進去了,此時就會調用handleChildProc
方法來處理出現的應用程序的進程(還是要提醒,本文創建的是應用程序的進程,而非應用程序本身)。
handleChildProc
方法回調了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 */);
}
}
現在我們回到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);
}
注意到這個方法是一個靜態的final方法,在12行代碼處,會執行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);
}
同樣在最後一行,調用了invokeStaticMain
方法,這裏傳入了三個參數,第一個參數args.startClass
就是本文一開始說的那個主線程的名字android.app.MainThread
。
終於我們到了最後一站!現在我們進入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
,也就是主線程名字,通過第6行的反射獲取到對應的類,命名爲c1,然後再通過15行代碼獲取主方法main,並將main傳入到MethodAndArgsCaller
方法中,當做異常拋出。
爲什麼這裏要用拋出異常的方法呢?
聯想到這是最後一步,拋出異常的方式將會清除設置過程中需要的堆棧幀,僅僅留下一個main方法,使得main方法看起來像一個入口方法,畢竟,前面那麼多的工作,那麼多類的流血犧牲,最後都是爲了成就他。當他出現時,前面的東西,也就沒有必要存在了。
現在異常拋出了,誰來接受呢?當然,是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;
}
}
可以看到,當出現了一個MethodAndArgsCaller
型的異常的時候,main會捕捉到,並且執行他的run方法,這個run方法在MethodAndArgsCaller
中,他是一個靜態類。
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) {
...
}
}
}
調用了invoke
方法後,ActivityThread裏面的main方法就會被動態調用,應用程序的進程就進入了這一main方法中。
經過上面的種種操作,我們創建了應用程序的進程並且運行了ActivityThread。
小結
簡單來說就是要想創建應用程序的進程,需要AMS發送請求,傳入主線程名,這一請求必須符合Zygote的abi
,並且將請求參數寫在ZygoteState
中。
然後Zygote讀取參數,fork自身去生成新的進程,如果返回的pid爲0,那麼就完成了應用程序的進程的創建。
然後通過反射將主線程名轉換爲主線程的類,獲得其main方法,通過拋出一個異常清除掉之前的堆棧幀,在main方法裏面接受這個異常,並執行run方法以激活main方法,使得應用程序的進程進入到main方法中得以運行。
寫了一早上,呼~,準備喫飯啦,還有什麼不懂的可以評論區提問哦。
作者:神鷹夢澤
鏈接:https://juejin.im/post/5f0165e1e51d453493111cf5
關注我獲取更多知識或者投稿