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底層有興趣的童鞋搞一本看看,里面講解的必然比本文精辟不少。

本文純手打,歡迎各位點亮愛心,給個小小的贊以資鼓勵,謝謝

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。