android進階——深刻理解應用程序的進程是如何啓動的

應用程序的進程啓動java

簡介

當咱們打開android手機的時候,不知道你是否想過app是如何啓動的呢?android

接下來,我將從源碼角度進行解析,固然,本文做爲上篇,是介紹應用程序的進程啓動過程,而不是應用程序的啓動過程,他們的區別就是煮飯前要準備鍋具,沒有鍋具就沒法煮飯,本文就是準備鍋具的,可是也不簡單哦。c++

文章將從兩個方面介紹,一個AMS發送請求,一個是Zygote接受請求。web

AMS就是Activity Manager System,管理Activity的,而Zygote就是建立進程的一個進程,因此AMS要想建立進程必須從Zygote那裏進行申請,一系列的故事,就此展開。微信

AMS發送請求

image.png

從圖中能夠簡略看出,首先AMS本身調用了本身的startProcessLocked方法,會建立相應的用戶id,用戶id組,以及對應的應用程序進程的主線程名——android.app.ActivityThreadapp

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起到比對的重要做用。socket

startProcessLocked方法內部會調用Process的start方法,而這個start方法只幹了一件事,就是調用ZygoteProcessstart方法。ide

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方法中。oop

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中,從圖中咱們雖然將ZygoteStateZygoteProcess作了並列處理,可是ZygoteStateZygoteProcess的一個靜態內部類,上面提到的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接受請求

依然是時序圖,甚至說,有了時序圖,根本就不須要看後面的文章啦。

image.png

看起來比上一個圖稍微複雜些,不過能夠發現仍是頗有規律的,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行代碼處,會執行RuntimeInitapplicationInit方法。

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方法中得以運行。

寫了一早上,呼~,準備吃飯啦,還有什麼不懂的能夠評論區提問哦。

本文取材自《Android進階解密》,資源在微信公衆號【小松漫步】,一塊兒交流學習吧。

相關文章
相關標籤/搜索