1. 程式人生 > >Android應用程式程序啟動過程(前篇)

Android應用程式程序啟動過程(前篇)

前言

在此前我講過Android系統的啟動流程,系統啟動後,我們就比較關心應用程式是如何啟動的,這一篇我們來一起學習Android7.0 應用程式程序啟動過程,需要注意的是“應用程式程序啟動過程”,而不是應用程式啟動過程。關於應用程式啟動過程,我會在後續系列的文章中講到。希望閱讀這篇文章前先閱讀本文列出的相關文章,要不你一臉矇蔽,就別怪我了。

1.應用程式程序概述

要想啟動一個應用程式,首先要保證這個應用程式所需要的應用程式程序已經被啟動。ActivityManagerService在啟動應用程式時會檢查這個應用程式需要的應用程式程序是否存在,不存在就會請求Zygote程序將需要的應用程式程序啟動。在

Android系統啟動流程(二)解析Zygote程序啟動過程這篇文章中,我提到了Zygote的Java框架層中,會建立一個Server端的Socket,這個Socket用來等待ActivityManagerService來請求Zygote來建立新的應用程式程序的。我們知道Zygote程序通過fock自身建立的應用程式程序,這樣應用程式程式程序就會獲得Zygote程序在啟動時建立的虛擬機器例項。當然,在應用程式建立過程中除了獲取虛擬機器例項,還可以獲得Binder執行緒池和訊息迴圈,這樣執行在應用程序中應用程式就可以方便的使用Binder進行程序間通訊以及訊息處理機制了。關於Binder執行緒池和訊息迴圈是如何啟動或者建立的會在下一篇文章給出答案。先給出應用程式程序啟動過程的時序圖,然後對每一個步驟進行詳細分析,如下圖所示。
Android應用程序啟動過程.png

2.應用程式程序建立過程

傳送建立應用程式程序請求

ActivityManagerService會通過呼叫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) {
        ...
try { try { final int userId = UserHandle.getUserId(app.uid); AppGlobals.getPackageManager().checkPackageStartable(app.info.packageName, userId); } catch (RemoteException e) { throw e.rethrowAsRuntimeException(); } int uid = app.uid;//1 int[] gids = null; int mountExternal = Zygote.MOUNT_EXTERNAL_NONE; if (!app.isolated) { ... /** * 2 對gids進行建立和賦值 */ if (ArrayUtils.isEmpty(permGids)) { gids = new int[2]; } else { gids = new int[permGids.length + 2]; System.arraycopy(permGids, 0, gids, 2, permGids.length); } gids[0] = UserHandle.getSharedAppGid(UserHandle.getAppId(uid)); gids[1] = UserHandle.getUserGid(UserHandle.getUserId(uid)); } ... if (entryPoint == null) entryPoint = "android.app.ActivityThread";//3 Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "Start proc: " + app.processName); checkTime(startTime, "startProcess: asking zygote to start proc"); /** * 4 */ 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); ... } catch (RuntimeException e) { ... } } ... }

在註釋1處的達到建立應用程式程序的使用者ID,在註釋2處對使用者組ID:gids進行建立和賦值。註釋3處如果entryPoint 為null則賦值為”android.app.ActivityThread”。在註釋4處呼叫Process的start函式,將此前得到的應用程式程序使用者ID和使用者組ID傳進去,第一個引數entryPoint我們得知是”android.app.ActivityThread”,後文會再次提到它。接下來我們來檢視Process的start函式,如下所示。
frameworks/base/core/java/android/os/Process.java

public static final 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[] zygoteArgs) {
    try {
        return startViaZygote(processClass, niceName, uid, gid, gids,
                debugFlags, mountExternal, targetSdkVersion, seInfo,
                abi, instructionSet, appDataDir, zygoteArgs);
    } catch (ZygoteStartFailedEx ex) {
      ...
    }
}

start函式中只調用了startViaZygote函式:
frameworks/base/core/java/android/os/Process.java

   private static 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[] extraArgs)
                                  throws ZygoteStartFailedEx {
        synchronized(Process.class) {
        /**
        * 1
        */
            ArrayList<String> argsForZygote = new ArrayList<String>();
            argsForZygote.add("--runtime-args");
            argsForZygote.add("--setuid=" + uid);
            argsForZygote.add("--setgid=" + gid);
          ...
            if (gids != null && gids.length > 0) {
                StringBuilder sb = new StringBuilder();
                sb.append("--setgroups=");

                int sz = gids.length;
                for (int i = 0; i < sz; i++) {
                    if (i != 0) {
                        sb.append(',');
                    }
                    sb.append(gids[i]);
                }

                argsForZygote.add(sb.toString());
            }
         ...
            argsForZygote.add(processClass);
            if (extraArgs != null) {
                for (String arg : extraArgs) {
                    argsForZygote.add(arg);
                }
            }
            return zygoteSendArgsAndGetResult(openZygoteSocketIfNeeded(abi), argsForZygote);
        }
    }

在註釋1處建立了字串列表argsForZygote ,並將啟動應用程序的啟動引數儲存在argsForZygote中,函式的最後會呼叫zygoteSendArgsAndGetResult函式,需要注意的是,zygoteSendArgsAndGetResult函式中第一個引數中呼叫了openZygoteSocketIfNeeded函式,而第二個引數是儲存應用程序的啟動引數的argsForZygote。zygoteSendArgsAndGetResult函式如下所示。
frameworks/base/core/java/android/os/Process.java

  private static 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();
            int sz = args.size();
            for (int i = 0; i < sz; i++) {
                String arg = args.get(i);
                if (arg.indexOf('\n') >= 0) {
                    throw new ZygoteStartFailedEx(
                            "embedded newlines not allowed");
                }
                writer.write(arg);
                writer.newLine();
            }
            writer.flush();
            // Should there be a timeout on this?
            ProcessStartResult result = new ProcessStartResult();
            result.pid = inputStream.readInt();
            if (result.pid < 0) {
                throw new ZygoteStartFailedEx("fork() failed");
            }
            result.usingWrapper = inputStream.readBoolean();
            return result;
        } catch (IOException ex) {
            zygoteState.close();
            throw new ZygoteStartFailedEx(ex);
        }
    }

zygoteSendArgsAndGetResult函式主要做的就是將傳入的應用程序的啟動引數argsForZygote,寫入到ZygoteState中,結合上文我們知道ZygoteState其實是由openZygoteSocketIfNeeded函式返回的,那麼我們接著來看openZygoteSocketIfNeeded函式,程式碼如下所示。
frameworks/base/core/java/android/os/Process.java

private static ZygoteState openZygoteSocketIfNeeded(String abi) throws ZygoteStartFailedEx {
    if (primaryZygoteState == null || primaryZygoteState.isClosed()) {
        try {
            primaryZygoteState = ZygoteState.connect(ZYGOTE_SOCKET);//1
        } catch (IOException ioe) {
            throw new ZygoteStartFailedEx("Error connecting to primary zygote", ioe);
        }
    }
    if (primaryZygoteState.matches(abi)) {//2
        return primaryZygoteState;
    }
    // The primary zygote didn't match. Try the secondary.
    if (secondaryZygoteState == null || secondaryZygoteState.isClosed()) {
        try {
        secondaryZygoteState = ZygoteState.connect(SECONDARY_ZYGOTE_SOCKET);//3
        } catch (IOException ioe) {
            throw new ZygoteStartFailedEx("Error connecting to secondary zygote", ioe);
        }
    }

    if (secondaryZygoteState.matches(abi)) {
        return secondaryZygoteState;
    }

    throw new ZygoteStartFailedEx("Unsupported zygote ABI: " + abi);

在講到Zygote程序啟動過程時我們得知,在Zygote的main函式中會建立name為“zygote”的Server端Socket。在註釋1處會呼叫ZygoteState的connect函式與名稱為ZYGOTE_SOCKET的Socket建立連線,這裡ZYGOTE_SOCKET的值為“zygote”。註釋2處如果連線name為“zygote”的Socket返回的primaryZygoteState與當前的abi不匹配,則會在註釋3處連線name為“zygote_secondary”的Socket。這兩個Socket區別就是:name為”zygote”的Socket是執行在64位Zygote程序中的,而name為“zygote_secondary”的Socket則執行在32位Zygote程序中。既然應用程式程序是通過Zygote程序fock產生的,當要連線Zygote中的Socket時,也需要保證位數的一致。

接收請求並建立應用程式程序

Socket進行連線成功並匹配abi後會返回ZygoteState型別物件,我們在分析zygoteSendArgsAndGetResult函式中講過,會將應用程序的啟動引數argsForZygote寫入到ZygoteState中,這樣Zygote程序就會收到一個建立新的應用程式程序的請求,我們回到ZygoteInit的main函式,如下所示。

frameworks/base/core/java/com/android/internal/os/ZygoteInit.java

public static void main(String argv[]) {
       ...
        try {
         ...       
            //註冊Zygote用的Socket
            registerZygoteSocket(socketName);//1
           ...
           //預載入類和資源
           preload();//2
           ...
            if (startSystemServer) {
            //啟動SystemServer程序
                startSystemServer(abiList, socketName);//3
            }
            Log.i(TAG, "Accepting command socket connections");
            //等待客戶端請求
            runSelectLoop(abiList);//4
            closeServerSocket();
        } catch (MethodAndArgsCaller caller) {
            caller.run();
        } catch (RuntimeException ex) {
            Log.e(TAG, "Zygote died with exception", ex);
            closeServerSocket();
            throw ex;
        }
    }

這些內容在Android系統啟動流程(二)解析Zygote程序啟動過程講過,但為了更好的理解我再講一遍。註釋1處通過registerZygoteSocket函式來建立一個Server端的Socket,這個name為”zygote”的Socket用來等待ActivityManagerService來請求Zygote來建立新的應用程式程序。註釋2處用來預載入類和資源。註釋3處用來啟動SystemServer程序,這樣系統的關鍵服務也會由SystemServer程序啟動起來。註釋4處呼叫runSelectLoop函式來等待ActivityManagerService的請求。我們就來檢視runSelectLoop函式:

frameworks/base/core/java/com/android/internal/os/ZygoteInit.java

 private static void runSelectLoop(String abiList) throws MethodAndArgsCaller {
        ArrayList<FileDescriptor> fds = new ArrayList<FileDescriptor>();
        ArrayList<ZygoteConnection> peers = new ArrayList<ZygoteConnection>();//2
        fds.add(sServerSocket.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();//1
                    if (done) {
                        peers.remove(i);
                        fds.remove(i);
                    }
                }
            }
        }
    }

當有ActivityManagerService的請求資料到來時會呼叫註釋1處的程式碼,結合註釋2處的程式碼,我們得知註釋1處的程式碼其實是呼叫ZygoteConnection的runOnce函式來處理請求的資料:
frameworks/base/core/java/com/android/internal/os/ZygoteConnection.java

 boolean runOnce() throws ZygoteInit.MethodAndArgsCaller {
        String args[];
        Arguments parsedArgs = null;
        FileDescriptor[] descriptors;
        try {
            args = readArgumentList();//1
            descriptors = mSocket.getAncillaryFileDescriptors();
        } catch (IOException ex) {
            Log.w(TAG, "IOException on command socket " + ex.getMessage());
            closeSocket();
            return true;
        }
...
        try {
            parsedArgs = new Arguments(args);//2
        ...
        /**
        * 3 
        */
            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);
                return true;
            } else {
                // in parent...pid of < 0 means failure
                IoUtils.closeQuietly(childPipeFd);
                childPipeFd = null;
                return handleParentProc(pid, descriptors, serverPipeFd, parsedArgs);
            }
        } finally {
            IoUtils.closeQuietly(childPipeFd);
            IoUtils.closeQuietly(serverPipeFd);
        }
    }

在註釋1處呼叫readArgumentList函式來獲取應用程式程序的啟動引數,並在註釋2處將readArgumentList函式返回的字串封裝到Arguments物件parsedArgs中。註釋3處呼叫Zygote的forkAndSpecialize函式來建立應用程式程序,引數為parsedArgs中儲存的應用程序啟動引數,返回值為pid。forkAndSpecialize函式主要是通過fork當前程序來建立一個子程序的,如果pid等於0,則說明是在新建立的子程序中執行的,就會呼叫handleChildProc函式來啟動這個子程序也就是應用程式程序,如下所示。
frameworks/base/core/java/com/android/internal/os/ZygoteConnection.java

 private void handleChildProc(Arguments parsedArgs,
            FileDescriptor[] descriptors, FileDescriptor pipeFd, PrintStream newStderr)
            throws ZygoteInit.MethodAndArgsCaller {
      ...
            RuntimeInit.zygoteInit(parsedArgs.targetSdkVersion,
                    parsedArgs.remainingArgs, null /* classLoader */);
        }
    }

handleChildProc函式中呼叫了RuntimeInit的zygoteInit函式,如下所示。
frameworks/base/core/java/com/android/internal/os/RuntimeInit.java

  public static final void zygoteInit(int targetSdkVersion, String[] argv, ClassLoader classLoader)
            throws ZygoteInit.MethodAndArgsCaller {
        if (DEBUG) Slog.d(TAG, "RuntimeInit: Starting application from zygote");
        Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "RuntimeInit");
        redirectLogStreams();
        commonInit();
        nativeZygoteInit();//1
        applicationInit(targetSdkVersion, argv, classLoader);//2
    }

註釋1處會在新建立的應用程式程序中建立Binder執行緒池,這個在下一篇文章會詳細介紹。在註釋2處呼叫了applicationInit函式:
frameworks/base/core/java/com/android/internal/os/RuntimeInit.java

  private static void applicationInit(int targetSdkVersion, String[] argv, ClassLoader classLoader)
            throws ZygoteInit.MethodAndArgsCaller {
 ...
        final Arguments args;
        try {
            args = new Arguments(argv);
        } catch (IllegalArgumentException ex) {
            Slog.e(TAG, ex.getMessage());       
            return;
        }
        Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
        invokeStaticMain(args.startClass, args.startArgs, classLoader);//1
    }

在applicationInit中會在註釋1處呼叫invokeStaticMain函式,需要注意的是第一個引數args.startClass,這裡指的就是此篇文章開頭提到的引數:android.app.ActivityThread。接下來我們檢視invokeStaticMain函式,如下所示。
frameworks/base/core/java/com/android/internal/os/RuntimeInit.java

private static void invokeStaticMain(String className, String[] argv, ClassLoader classLoader)
        throws ZygoteInit.MethodAndArgsCaller {
    Class<?> cl;
    try {
        cl = Class.forName(className, true, classLoader);//1
    } 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 });//2
    } catch (NoSuchMethodException ex) {
        throw new RuntimeException(
                "Missing static main on " + className, ex);
    }
    ...
    throw new ZygoteInit.MethodAndArgsCaller(m, argv);//3
}

可以看到註釋1處通過反射來獲得android.app.ActivityThread類,接下來在註釋2處來獲得ActivityThread的main函式,並將main函式傳入到註釋3處的ZygoteInit中的MethodAndArgsCaller類的建構函式中,MethodAndArgsCaller類內部會通過反射呼叫ActivityThread的main函式,這樣應用程式程序就建立完成了。

歡迎關注我的微信公眾號,第一時間獲得部落格更新提醒,以及更多成體系的Android相關原創技術乾貨。
掃一掃下方二維碼或者長按識別二維碼,即可關注。