Android 應用程序啟動過程
Android應用程序啟動流程
注意,這裡講的是應用程序 的啟動流程,不是應用的啟動流程
本文承接部分Android系統啟動流程的內容,建議有慾望的童鞋先看看:傳送門
一:簡介
想要啟動一個應用程式,首先要保證這個應用所需要的應用程式程序 已經啟動。
AMS在啟動應用程式時會檢查這個應用所需要的應用程式程序是否存在,如果不存在就會請求Zygote程序啟動一個新的應用程式程序。這個流程用到的通訊方式,就是我們在Android系統啟動流程中提到過的,Zygote Server端的Socket (這個socketName就是zygote)。
Android系統啟動流程一文中提到過,Zygote在註冊好了Socket之後,會呼叫一個方法:zygoteServer.runSelectLoop(),這樣就會進入一個迴圈,等待AMS請求Zygote來建立新的應用程式程序。Zygote程序通過fork 自身建立應用程式程序,這樣的應用程式程序就會獲得Zygote程序在啟動時建立的java虛擬機器例項 。當然,在應用程式程序建立過程中除了獲取虛擬機器例項外,還會建立Binder執行緒池 以及Looper 訊息佇列迴圈,這樣執行在應用程序中的應用程式就可以方便地使用Binder進行程序間通訊以及處理訊息了。
二:應用程式程序的啟動過程
1.AMS傳送指令
AMS想要啟動應用程式程序,就需要向Zygote程序傳送建立應用程序的請求,AMS會通過呼叫
startProcessLocked方法向Zygote傳送程序請求。
frameworks/base/services/core/java/com/android/server/am/ActivityManagerService.java:
private final void startProcessLocked(ProcessRecord app,String hostingType,String hostingNameStr,String abiOverride,String entryPoint,String entryPointArgs){ ... //獲取要建立的應用程式程序的使用者ID int uid=app.uid; ... //判斷是否隔離 if(!app.isolated){ ... //對gids進行建立和賦值 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)); } ... //準備反射ActivityThread if(entryPoint==null) entryPoint="android.app.ActivityThread"; ... //啟動應用程式程序,引數過長請忽略 startResult=Process.start(entryPoint,app.processName,uid,uid,gids,debugFlags,mountExternal,app.info.targetSdkVersion,seInfo,requiredAbi,instructionSet,app.info.dataDir,invokeWith,entryPointArgs); ... }
該方法主要做了三件事,取得了uid,建立並賦值了gids,呼叫Process的start方法並傳入ActivityThread等啟動引數,準備啟動程序。
接下來看看Process的start方法。
frameworks/base/core/java/android/os/ZygoteProcess.java:
//引數過長請忽略那些不太重要的 public final Process.ProcessStartResult start(final String processClass,final String niceName,int uid,int gid,int[] gids,int debugFlags,int mountExternal,int targetSdkVersion,String seInfo,String abi,String instructionSet,String appDataDir,String invokeWith,String[] zygoteArgs){ try{ //呼叫了startViaZygote方法 return startViaZygote(processClass,niceName,uid,gid,gids,debugFlags,mountExternal,targetSdkVersion,seInfo,abi,instructionSet,appDataDir,invokeWith,zygoteArgs); }catch(ZygoteStartFailedEx ex){ ... throw new RuntimeException("Starting VM process through Zygote failed",ex) } } ... //引數過長請忽略那些不太重要的 private Process.ProcessStartResult startViaZygote(final String processClass,final String niceName,final int uid,final int gid,final int[] gids, int debugFlags,int mountExternal,int targetSdkVersion,String seInfo,String abi,String instructionSet,String appDataDir,String invokeWith,String[] extraArgs){ ... synchronized(mLock){ //這兩個方法,zygoteSendArgsAndGetResult以及openZygoteSocketifNeeded才是重點 return zygoteSendArgsAndGetResult(openZygoteSocketifNeeded(abi),argsForZygote) } }
調來調去沒有什麼新鮮的,接下來就有兩個方法,zygoteSendArgsAndGetResult方法,和openZygoteSocketifNeeded方法,這兩個才是重點。
frameworks/base/core/java/android/os/ZygoteProcess.java:
private static Process.ProcessStartResult zygoteSendArgsAndGetResult(ZygoteState zygoteState,ArrayList<String> args){ 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())); write.newLine(); for(int i = 0; i < sz; i++){ String arg=args.get(i); writer.write(arg); writer.newLine(); } writer.flush(); Process.ProcessStartResult result=new Process.ProcessStartResult(); result.pid=inputStream.readInt(); result.usingWrapper=inputStream.readBoolean(); if(result.pid<0){ throw new ZygoteStartFailedEx("fork() failed"); } return result; }else{ zygoteState.close(); throw new ZygoteStartFailedEx(ex); } }
從上面的讀寫操作,我們彷彿已經看到了Socket通訊的過程。接下來ZygoteProcess中應該就有與Zygote建立Socket連結的部分了。
frameworks/base/core/java/android/os/ZygoteProcess.java:
private ZygoteState openZygoteSocketIfNeeded(String abi){ //如果與Zygote未建立連線或者連線已斷開 if(primaryZygoteState == null || primaryZygoteState.isClosed()){ try{ //嘗試與Zygote程序建立連線 primaryZygoteState = ZygoteState.connect(mSocket); }catch(IOException ioe){ throw new ZygoteStartFailedEx("Error connecting to primary zygote",ioe); } } //連線Zygote主模式返回的ZygoteState是否與啟動應用程式程序所需要的ABI匹配 if(primaryZygoteState.matches(abi)){ return primaryZygoteState; } //如果不匹配,則嘗試連線Zygote的輔模式 if(secondaryZygoteState == null || secondaryZygoteState.isClosed()){ try{ //嘗試與Zygote程序再次建立連線 secondaryZygoteState = ZygoteState.connect(mSecondarySocket); }catch(IOException ioe){ throw new ZygoteStartFailedEx("Error connecting to Secondary zygote",ioe); } } //連線Zygote輔模式返回的ZygoteState是否與啟動應用程序所需要的ABI匹配 if(secondaryZygoteState.matched(abi)){ return secondaryZygoteState; } throw new ZygoteStartFailedEx("Unsupported zygote ABI:"+abi); }
的確,在這段程式碼中我們看到了AMS嘗試與Zygote建立Socket通訊的程式碼。
這裡有兩個概念:
1.ABI :abi全稱Application binary interface(應用程式二進位制介面),主要針對各種手機CPU架構不同,作出的一套適配規則。大家可以瞭解下詳情:傳送門。
2.Zygote輔模式 :在上一篇文章《Android系統啟動流程》 介紹過,Zygote啟動指令碼分為4種:
1.init.zygote32.rc
2.init.zygote32_64.rc
3.init.zygote64.rc
4.init.zygote64_32.rc
對於採用的是init.zygote32_64.rc或者init.zygote64_32.rc的Zygote,Socket的name為“zygote”的為主模式,name為“zygote_secondary”為輔模式。
到此為止,AMS程序試圖與Zygote建立連線的程式碼已經展示完成。
2.Zygote 接收請求
在Socket連線成功並匹配ABI後會返回ZygoteState型別物件,應用程序的啟動引數會寫入ZygoteState中,這樣Zygote程序就會收到一個建立新的應用程式的程序請求。我們回到ZygoteInit的main方法中。
frameworks/base/core/java/com/android/internal/os/ZygoteInit.java
public static void main(String argv[]){ ... //建立一個Server端的Socket,socketName的值為“zygote” zygoteServer.registerServerSocket(socketName); ... if(startSystemServer){ //啟動SystemServer程序 startSystemServer(abiList,socketName,zygoteServer); } ... //等待AMS請求 zygoteServer.runSelectLoop(abiList); ... }
我們又回到了Android系統啟動流程中,當Zygote啟動後,會註冊一個Server端的Socket,並且呼叫runSelectLoop方法來等待AMS的請求。下面來看看runSelectLoop方法。
frameworks/base/core/java/com/android/internal/os/ZygoteServer.java:
void runSelectLoop(String abiList){ ... 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{ //AMS的請求過來,最終會走到這裡 boolean done = peers.get(i).runOnce(this); if(done){ peers.remove(i); fds.remove(i); } } } } }
當AMS的請求過來,最終會呼叫ZygoteConnection的runOnce方法來處理請求資料。
frameworks/base/core/java/com/android/internal/os/ZygoteConnection.java:
boolean runOnce(ZygoteServer zygoteServer){ ... //獲取應用程式程序的啟動引數 args = readArgumentList(); ... //對啟動引數的封裝 parsedArgs = new Argument(args); ... //關鍵程式碼,Zygote fork出來子程序 pid = Zygote.forkAndSpecialize(parsedArgs.uid,parsedArgs.gid,parsedArgs.gids,parsedArgs.debugFlags,rlimits,parsedArgs.mountExternal,parsedArgs.seInfo,parsedArgs.appDataDir); try{ //注意:當前程式碼邏輯執行在新建立的子程序中了 if(pid==0){ //第一步當然是關閉Zygote與AMS通訊的Socket,這對於子程序無意義 zygoteServer.closeServerSocket(); IoUtils.closeQuietly(serverPipeFd); //處理應用程式程序 handleChildProc(parsedArgs,descriptors,childPipeFd,newStderr); return true; }else{ ... }finally{ ... } } }
我們可以在程式碼中看見,最終在ZygoteConnection中的runOnce方法,Zygote 的forkAndSpecialize方法建立了一個子程序,當pid等於0時,證明當前程式碼邏輯運行於新建立的子程序也就是應用程式程序中了,然後呼叫了handleChildProc來進行下一步處理。
由於呼叫關係繁多,這裡不做handleChildProc的程式碼展示了,最後在該方法中,會呼叫ZygoteInit的zygoteInit方法,如下圖所示。
frameworks/base/core/java/com/android/internal/os/ZygoteInit.java:
public static final void zygoteInit(int targetSdkVersion,String[] argv,ClassLoader classLoader){ ... RuntimeInit.redirectLogStreams(); RuntimeInit.commonInit(); //Zygote的nativeZygoteInit方法會啟動Binder執行緒池 ZygoteInit.nativeZygoteInit(); //應用程式程序初始化 RuntimeInit.applicationInit(targetSdkVersion,argv,classLoader); }
ZygoteiInit的nativeZygoteInit方法最後會啟動Binder執行緒池,這部分一會再說,先繼續探究RuntimeInit的applicationInit方法。
frameworks/base/core/java/com/android/internal/os/RuntimeInit.java:
protected static void applicationInit(int targetSdkVersion,String[] argv,ClassLoader classLoader){ ... final Arguments args; //包裝引數 args=Arguments(argv); ... //args.startClass 就是 android.app.ActivityThread invokeStaticMain(args.startClass,args.startArgs,classLoader); } ... private static void invokeStaticMain(String className,String[] argv,ClassLoader classLoader){ Class<?> cl; try{ //獲得android.app.ActivityThread類 cl = Class.forName(className,true,classLoader); }catch(ClassNotFoundException ex){ ... } Method m; try{ //獲取ActivityThread的main方法 m = cl.getMethod("main",new Class[]{String[].class}); }catch(NoSuchMethodException ex){ ... } ... //又來這套... throw new Zygote.MethodAndArgsCaller(m,argv); }
在invokeStaticMain方法中,通過反射的方式找到了ActivityThread類以及它的main方法。在最後丟擲異常:throw new Zygote.MethodAndArgsCaller(m,arg),該異常會被Zygote的main方法捕獲,至於這裡為什麼採用了丟擲異常的方式而不是直接呼叫ActivityThread的main方法,原理和Zygote處理SystemServer程序是一毛一樣。這種丟擲異常的處理會清除所有的設定過程需要的堆疊幀,並讓ActivityThread的main方法看起來像是應用程式程序的入口方法(其實不是,在main方法呼叫前已經做過很多的事情了,比如開啟Binder執行緒池等等)。
既然啟動SystemServer以及應用程式程序都用到了這種“奇葩”方式,我們來補齊這部分的程式碼知識。
frameworks/base/core/java/com/android/internal/os/ZygoteInit.java:
public static void main(String argv[]){ try{ ... closeServerSocket(); }catch(MethodAndArgsCaller caller){ //在這裡捕獲異常,並且呼叫MethodAndArgsCaller的run方法 caller.run(); }catch(RuntimeException ex){ ... } }
MethodAndArgsCaller是Zygote的靜態內部類。
frameworks/base/core/java/com/android/internal/os/Zygote.java:
public static class MethodAndArgsCaller extends Exception implements Runnable{ private final Method mMethod; private final String[] mArgs; public MethodAndArgsCaller(Method method,String[] args){ mMethod=method; mArgs=args; } //Runnable的run public void run(){ try{ //最終反射呼叫main方法,因為是靜態方法,所以第一個引數是null mMethod.invoke(null,new Object[]{mArgs}); }catch(IllegalAccessException ex){ ... } } }
完事,最終呼叫ActivityThread的main方法。
三:Binder執行緒池啟動
剛剛提到過,在ZygoteInit中,初始化應用程式程序之前,會呼叫nativeZygoteInit方法來初始化Binder執行緒池。很明顯,nativeZygoteInit從名字上來看就是一個JNI方法。
private static final native void nativeZygoteInit();
它所對應的函式是com_android_internal_os_ZygoteInit_nativeZygoteInit,如下。
frameworks/base/core/jni/AndroidRuntime.cpp:
const JNINativeMethod method[]={ { "nativeZygoteInit", "()V", (void*) com_android_internal_os_ZygoteInit_nativeZygoteInit }, }; ... static void com_android_internal_os_ZygoteInit_nativeZygoteInit(JNIEnv* env,jobject clazz){ //這個gCurRuntime就是AndroidRuntime型別的指標 gCurRuntime->onZygoteInit(); } ... static AndoridRuntime* gCurRuntime = NULL; ... AndroidRuntime:AndroidRuntime(char* argBlockStart,const size_t argBlockLength):mExitWithoutCleanup(false),mArgBlockStart(argBlockStart),mArgBlockLength(argBlockLength){ ... gCurRuntime = this; }
AppRuntime 繼承自 AndroidRuntime,AppRuntime在建立時就會呼叫AndroidRuntime的建構函式,gCurRuntime就會被初始化,它指向的是AppRuntime。接下來看看onZygoteInit函式,AppRuntime在app_main.cpp中的實現。
frameworks/base/cmds/app_process/app_main.cpp:
virtual void onZygoteInit(){ sp<ProcessState> proc = ProcessState::self(); proc->startThreadPoll(); }
最後呼叫了ProcessState的startThreadPoll函式來啟動執行緒池。
frameworks/native/libs/binder/ProcessState.cpp:
AutoMutex _l(mLock); //確保Binder執行緒池只會被啟動一次 if(!mThreadPollStarted){ mThreadPollStarted = true; //建立執行緒池中的第一個執行緒,也就是該執行緒池的主執行緒 spawnPolledThread(true); } ... void ProcessState::spawnPooledThread(bool isMain){ if(mThreadPollStarted){ String8 name = makeBinderThreadName(); ... sp<Thread> t = new PollThread(isMain); //呼叫run方法來啟動新執行緒 t->run(name.string()) } } ... class PollThread:public Thread{ ... protected:virtual bool threadLoop(){ IPCThreadState::self()->joinThreadPoll(mIsMain); return false; } const bool mIsMain; };
在最後,呼叫了IPCThreadState的joinThreadPool函式,將當前執行緒註冊到BInder驅動程式中,這樣我們建立的執行緒就加入了Binder執行緒池中,新建立的應用程式程序就支援Binder間的通訊了。我們只需要建立當前程序的Binder物件,並將它註冊到ServiceManager中就可以實現Binder程序間通訊。
四:訊息迴圈建立
在Zygote建立完畢應用程式程序,會通過拋異常的方式啟動ActivityThread的main方式,而後ActivityThread在main方法中,會開啟訊息迴圈,也就是Looper。
fragmeworks/base/core/java/android/app/ActivityThread.java
public static void main(String[] args){ //建立主執行緒Looper Looper.prepareMainLooper(); ... //Looper開始工作 Looper.loop(); }
這也就是為什麼,我們在主執行緒中,可以不用呼叫Looper的prepare方法,但是仍然可以使用Handler的原因了,ActivityThread已經幫我們在主執行緒做了這些事情。
對於Handler、Looper、MessageQueue的關係,感興趣的童鞋可以參考:傳送門
結尾
整個Android應用程序啟動過程介紹完畢,內容不多但十分重要。開發人員瞭解自己所開發的應用的程序是如何建立的是十分必要的。
本文大量參考《Android 進階解密》一書,同時建議對Android底層有興趣的童鞋搞一本看看,裡面講解的必然比本文精闢不少。
本文純手打,歡迎各位點亮愛心,給個小小的贊以資鼓勵,謝謝