美文网首页Android开发Android开发经验谈Android技术知识
Android应用程序进程启动过程-源码分析

Android应用程序进程启动过程-源码分析

作者: zackyG | 来源:发表于2020-04-03 21:29 被阅读0次

    Android系统中,启动一个app首先要保证该app所在的进程已经被启动。本文就介绍下app进程,即应用程序进程的启动过程。这里顺便提一句,通常所说的启动应用程序,指的是其根Activity的启动,和应用程序进程的启动是两个概念。

    • 了解Android系统启动过程或者Zygote进程的同学都会知道,应用程序进程的创建是通过Zygote进程fork其自身而产生的。①
    • 了解Activity的启动过程的朋友,又知道在Activity的启动过程中,会有一个检查Activity所在进程是否已经启动,否则就会先创建并启动其所在进程,再启动Activity。②
      知道了上面两个过程,再来理解app进程启动过程,就相对容易了。先让大家有个大概的流程认识:

    在Android系统的启动过程中,Zygote进程的Java框架层中创建一个Server端的Socket。这个Socket用来等待AMS发送请求,让Zygote进程创建新的app进程。Zygote进程接收到请求后,通过fork自身创建app进程。

    具体的创建和启动过程步骤比较多,我们把上述过程分为两个部分来分别讲解。

    AMS发送启动app进程的请求

    先来看一下AMS发送启动app进程请求过程的时序图


    image.png

    AMS如果想要启动app进程,就需要向Zygote进程发送创建app进程的请求,AMS会通过调用startProcessLocked方法来开始这一过程。下面展示相关的重要代码

    private final void startProcessLocked(ProcessRecord app, String hostingType,
                String hostingNameStr, String abiOverride, String entryPoint, String[] entryPointArgs) {
        ...
        try {
                ...
                //获取要创建的app进程的用户id
                int uid = app.uid;
                ...
                //对用户组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));
                ...
                // Start the process.  It will either succeed and return a result containing
                // the PID of the new process, or else throw a RuntimeException.
                //这里的entryPoint的值就是app进程主线程的类名。
                if (entryPoint == null) entryPoint = "android.app.ActivityThread";
                ...
                //调用Process.start方法,将上面得到的entryPoint,uid,gids传进去
                startResult = Process.start(entryPoint,
                            app.processName, uid, uid, gids, debugFlags, mountExternal,
                            app.info.targetSdkVersion, seInfo, requiredAbi, instructionSet,
                            app.info.dataDir, invokeWith, entryPointArgs);
    }
    

    关于uid、gid和进程
    在Linux中,每个UID标识一个用户,每个用户又至少属于一个组,每个组都有一个唯一标识的GID。每个进程都拥有真实的用户、组(UID、GID)。在Android系统中,一个用户的UID表示一个app。app在安装时被分配了用户UID,其在设备上存续期间,用户UID保持不变。对于普通的用户程序,GID和UID相同。详细介绍可以参考Android 安全机制(1)uid 、 gid 与 pid

    entryPoint 希望读者能够留意这个变量值,它关系到app进程创建后的初始化过程,后面会提到。

    接下来查看Process的start方法,这里需要注意的是,此处的Process类不是java/lang/Process.java的Process类,而是android/os/Process.java的Process类

    package android.os;
    /**
     * Tools for managing OS processes.
     */
    public class Process {
            ...
            public static final ProcessStartResult start(final String processClass,
                                      final String niceName,
                                      int uid, int gid, int[] gids,
                                      int runtimeFlags, int mountExternal,
                                      int targetSdkVersion,
                                      String seInfo,
                                      String abi,
                                      String instructionSet,
                                      String appDataDir,
                                      String invokeWith,
                                      String[] zygoteArgs) {
            return zygoteProcess.start(processClass, niceName, uid, gid, gids,
                        runtimeFlags, mountExternal, targetSdkVersion, seInfo,
                        abi, instructionSet, appDataDir, invokeWith, zygoteArgs);
        }
            ...
    }
    

    一目了然,其内部直接调用了ZygoteProcess的start方法

    public final Process.ProcessStartResult start(final String processClass,
                                                      final String niceName,
                                                      int uid, int gid, int[] gids,
                                                      int runtimeFlags, int mountExternal,
                                                      int targetSdkVersion,
                                                      String seInfo,
                                                      String abi,
                                                      String instructionSet,
                                                      String appDataDir,
                                                      String invokeWith,
                                                      String[] zygoteArgs) {
            try {
                return startViaZygote(processClass, niceName, uid, gid, gids,
                        runtimeFlags, mountExternal, targetSdkVersion, seInfo,
                        abi, instructionSet, appDataDir, invokeWith, false /* startChildZygote */,
                        zygoteArgs);
            } catch (ZygoteStartFailedEx ex) {
                Log.e(LOG_TAG,
                        "Starting VM process through Zygote failed");
                throw new RuntimeException(
                        "Starting VM process through Zygote failed", ex);
            }
        }
    

    它又调用了startViaZygote方法

    /**
    * Starts a new process via the zygote mechanism.
    */
    private Process.ProcessStartResult startViaZygote(final String processClass,
                                                          final String niceName,
                                                          final int uid, final int gid,
                                                          final int[] gids,
                                                          int runtimeFlags, int mountExternal,
                                                          int targetSdkVersion,
                                                          String seInfo,
                                                          String abi,
                                                          String instructionSet,
                                                          String appDataDir,
                                                          String invokeWith,
                                                          boolean startChildZygote,
                                                          String[] extraArgs)
                                                          throws ZygoteStartFailedEx {
            ArrayList<String> argsForZygote = new ArrayList<String>();
    
            // --runtime-args, --setuid=, --setgid=,
            // and --setgroups= must go first
            argsForZygote.add("--runtime-args");
            argsForZygote.add("--setuid=" + uid);
            argsForZygote.add("--setgid=" + gid);
            argsForZygote.add("--runtime-flags=" + runtimeFlags);
            if (mountExternal == Zygote.MOUNT_EXTERNAL_DEFAULT) {
                argsForZygote.add("--mount-external-default");
            } else if (mountExternal == Zygote.MOUNT_EXTERNAL_READ) {
                argsForZygote.add("--mount-external-read");
            } else if (mountExternal == Zygote.MOUNT_EXTERNAL_WRITE) {
                argsForZygote.add("--mount-external-write");
            }
            argsForZygote.add("--target-sdk-version=" + targetSdkVersion);
    
            // --setgroups is a comma-separated list
            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());
            }
            if (niceName != null) {
                argsForZygote.add("--nice-name=" + niceName);
            }
            if (seInfo != null) {
                argsForZygote.add("--seinfo=" + seInfo);
            }
            if (instructionSet != null) {
                argsForZygote.add("--instruction-set=" + instructionSet);
            }
            if (appDataDir != null) {
                argsForZygote.add("--app-data-dir=" + appDataDir);
            }
            if (invokeWith != null) {
                argsForZygote.add("--invoke-with");
                argsForZygote.add(invokeWith);
            }
            if (startChildZygote) {
                argsForZygote.add("--start-child-zygote");
            }
            argsForZygote.add(processClass);
            if (extraArgs != null) {
                for (String arg : extraArgs) {
                    argsForZygote.add(arg);
                }
            }
            synchronized(mLock) {
                return zygoteSendArgsAndGetResult(openZygoteSocketIfNeeded(abi), argsForZygote);
            }
        }
    

    这个方法前面一段是创建字符串列表argsForZygote,并将app进程的启动参数放在argsForZygote中。方法最后调用了zygoteSendArgsAndGetResult方法。需要注意的是zygoteSendArgsAndGetResult方法的第一个参数openZygoteSocketIfNeeded,稍后会讲到。第二个参数就是argsForZygote。先看看zygoteSendArgsAndGetResult方法的代码

    /**
         * Sends an argument list to the zygote process, which starts a new child
         * and returns the child's pid. Please note: the present implementation
         * replaces newlines in the argument list with spaces.
         *
         * @throws ZygoteStartFailedEx if process start failed for any reason
         */
    @GuardedBy("mLock")
        private static Process.ProcessStartResult zygoteSendArgsAndGetResult(
                ZygoteState zygoteState, ArrayList<String> args)
                throws ZygoteStartFailedEx {
            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()));
                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();
    
                if (result.pid < 0) {
                    throw new ZygoteStartFailedEx("fork() failed");
                }
                return result;
            } catch (IOException ex) {
                zygoteState.close();
                throw new ZygoteStartFailedEx(ex);
            }
        }
    

    zygoteSendArgsAndGetResult方法的主要作用就是将传入的app进程的启动参数argsForZygote写入到zygoteState中,zygoteState是ZygoteProcess的静态内部类,用于表示与Zygote进程的通信状态。现在再回过头去看,startViaZygote的return语句,我们可以知道,这个zygoteState对象是openZygoteSocketIfNeeded方法返回的。

    /**
         * Tries to open socket to Zygote process if not already open. If
         * already open, does nothing.  May block and retry.  Requires that mLock be held.
         */
        @GuardedBy("mLock")
        private ZygoteState openZygoteSocketIfNeeded(String abi) throws ZygoteStartFailedEx {
            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);
                }
                maybeSetApiBlacklistExemptions(primaryZygoteState, false);
                 maybeSetHiddenApiAccessLogSampleRate(primaryZygoteState);
            }
            if (primaryZygoteState.matches(abi)) {
                return primaryZygoteState;
            }
            // The primary zygote didn't match. Try the secondary.
            if (secondaryZygoteState == null || secondaryZygoteState.isClosed()) {
                try {
                    secondaryZygoteState = ZygoteState.connect(mSecondarySocket);
                } catch (IOException ioe) {
                    throw new ZygoteStartFailedEx("Error connecting to secondary zygote", ioe);
                }
                maybeSetApiBlacklistExemptions(secondaryZygoteState, false);        maybeSetHiddenApiAccessLogSampleRate(secondaryZygoteState);
            }
            if (secondaryZygoteState.matches(abi)) {
                return secondaryZygoteState;
            }
            throw new ZygoteStartFailedEx("Unsupported zygote ABI: " + abi);
        }
    

    在Zygote进程启动过程中,Zygote的main方法中会创建name为"zygote"的Server端Socket。在上面的代码中,我们也看到了调用ZygoteState的connect方法。这个方法就是用来和Zygote进程建立Socket连接的。其中的mSocket,是一个LocalSocketAddress对象,表示和Zygote进程通信的Socket名称。在这里指的就是Zygote进程里的Socket名称。
    我们再来看看ZygoteState类,就能更容易理解这个Socket通信过程。

    public static class ZygoteState {
            final LocalSocket socket;
            final DataInputStream inputStream;
            final BufferedWriter writer;
            final List<String> abiList;
            ...
            public static ZygoteState connect(LocalSocketAddress address) throws IOException {
                DataInputStream zygoteInputStream = null;
                BufferedWriter zygoteWriter = null;
                final LocalSocket zygoteSocket = new LocalSocket();
    
                try {
                    zygoteSocket.connect(address);
    
                    zygoteInputStream = new DataInputStream(zygoteSocket.getInputStream());
    
                    zygoteWriter = new BufferedWriter(new OutputStreamWriter(
                            zygoteSocket.getOutputStream()), 256);
                } catch (IOException ex) {
                    try {
                        zygoteSocket.close();
                    } catch (IOException ignore) {
                    }
    
                    throw ex;
                }
    
                String abiListString = getAbiList(zygoteWriter, zygoteInputStream);
                Log.i("Zygote", "Process: zygote socket " + address.getNamespace() + "/"
                        + address.getName() + " opened, supported ABIS: " + abiListString);
    
                return new ZygoteState(zygoteSocket, zygoteInputStream, zygoteWriter,
                        Arrays.asList(abiListString.split(",")));
            }
            ...
    }
    

    可以看到在ZygoteState的connect方法里面,调用了LocalSocket的connect方法。而这个LocalSocket就是Android里面的Socket实现,和java里面的Socket类一样,实现了Closeable接口。
    至此,我们已经可以看出,AMS和Zygote进程建立Socket连接的过程和基于Java的Socket连接过程是很相似的。

    Zygote接收请求并创建app进程
    同样,我们先看一下这部分的时序图 image.png

    我们先从,Zygote进程启动过程中会创建一个Server端的Socket,这个Socket用来等待AMS发送请求讲起。
    这个过程是在ZygoteInit的main方法中执行的。

    public static void main(String argv[]) {
            ...
            String socketName = "zygote";
            ...
            zygoteServer.registerServerSocketFromEnv(socketName);
            if (!enableLazyPreload) {
                    bootTimingsTraceLog.traceBegin("ZygotePreload");
                    EventLog.writeEvent(LOG_BOOT_PROGRESS_PRELOAD_START,
                        SystemClock.uptimeMillis());
                    //预加载资源
                    preload(bootTimingsTraceLog);
              ...
              //创建SystemServer进程
              if (startSystemServer) {
                    Runnable r = forkSystemServer(abiList, socketName, zygoteServer);
              ...
              // The select loop returns early in the child process after a fork and
                // loops forever in the zygote.
                //等待AMS的请求
                caller = zygoteServer.runSelectLoop(abiList);
              ...  
    }
    

    这段代码中,首先通过registerServerSocketFromEnv方法创建了一个Server段的Socket,这个name为"zygote"的Socket用来等待AMS请求Zygote,以创建新的app进程。最后调用zygoteServer的runSelectLoop方法来等待或者说监听AMS请求创建新的app进程。接下来看看zygoteServer的runSelectLoop方法

    /**
         * Runs the zygote process's select loop. Accepts new connections as
         * they happen, and reads commands from connections one spawn-request's
         * worth at a time.
         */
        Runnable runSelectLoop(String abiList) {
            ArrayList<FileDescriptor> fds = new ArrayList<FileDescriptor>();
            ArrayList<ZygoteConnection> peers = new ArrayList<ZygoteConnection>();
            ...
            while (true) {
                StructPollfd[] pollFds = new StructPollfd[fds.size()];
                for (int i = 0; i < pollFds.length; ++i) {
                ...
                ZygoteConnection connection = peers.get(i);
                            final Runnable command = connection.processOneCommand(this);
                ...
    }
    

    runSelectLoop方法中,通过一个while(true)的循环,不停的遍历ArrayList<ZygoteConnection>类型的peers列表,执行其中每个ZygoteConnection对象的processOneCommand方法。我们接着看ZygoteConnection的processOneCommand方法

    /**
         * Reads one start command from the command socket. If successful, a child is forked and a
         * {@code Runnable} that calls the childs main method (or equivalent) is returned in the child
         * process. {@code null} is always returned in the parent process (the zygote).
         *
         * If the client closes the socket, an {@code EOF} condition is set, which callers can test
         * for by calling {@code ZygoteConnection.isClosedByPeer}.
         */
        Runnable processOneCommand(ZygoteServer zygoteServer) {
            ...
            //读取app进程的启动参数
            args = readArgumentList();
            ...
            parsedArgs = new Arguments(args);
            ...
            pid = Zygote.forkAndSpecialize(parsedArgs.uid, parsedArgs.gid, parsedArgs.gids,
                    parsedArgs.runtimeFlags, rlimits, parsedArgs.mountExternal, parsedArgs.seInfo,
                    parsedArgs.niceName, fdsToClose, fdsToIgnore, parsedArgs.startChildZygote,
                    parsedArgs.instructionSet, parsedArgs.appDataDir);
           try {
                //当前代码是在子进程中执行的
                if (pid == 0) {
                    // in child
                    zygoteServer.setForkChild();
    
                    zygoteServer.closeServerSocket();
                    IoUtils.closeQuietly(serverPipeFd);
                    serverPipeFd = null;
    
                    return handleChildProc(parsedArgs, descriptors, childPipeFd,
                            parsedArgs.startChildZygote);
                } else {
                    // In the parent. A pid < 0 indicates a failure and will be handled in
                    // handleParentProc.
                    IoUtils.closeQuietly(childPipeFd);
                    childPipeFd = null;
                    handleParentProc(pid, descriptors, serverPipeFd);
                    return null;
                }
            } finally {
                IoUtils.closeQuietly(childPipeFd);
                IoUtils.closeQuietly(serverPipeFd);
            }
        }
    

    上面这段代码中,首先调用readArgumentList方法获取app进程的启动参数,并封装到Arguments类型的parsedArgs参数中,然后会调用Zygote的forkAndSpecialize方法来创建app进程,参数就是parsedArgs中的app进程启动参数,然后返回pid。forkAndSpecialize方法主要是通过fork当前进程,也就是Zygote进程来创建一个子进程的。如果pid为0,那接下去的代码就是在新创建的子进程中执行。这时会调用handleChildProc来处理App进程。

       /**
         * Handles post-fork setup of child proc, closing sockets as appropriate,
         * reopen stdio as appropriate, and ultimately throwing MethodAndArgsCaller
         * if successful or returning if failed.
         */
    private Runnable handleChildProc(Arguments parsedArgs, FileDescriptor[] descriptors,
                FileDescriptor pipeFd, boolean isZygote) {
            //关闭LocalSocket
            closeSocket();
            ...
            if (!isZygote) {
                    return ZygoteInit.zygoteInit(parsedArgs.targetSdkVersion, parsedArgs.remainingArgs,
                            null /* classLoader */);
            ...
    }
    

    在handleChildProc方法中,需要重点关注的代码就是调用了ZygoteInit的zogoteInit方法。

    /**
         * The main function called when started through the zygote process. This
         * could be unified with main(), if the native code in nativeFinishInit()
         * were rationalized with Zygote startup.
         *
         */
        public static final Runnable zygoteInit(int targetSdkVersion, String[] argv, ClassLoader classLoader) {
            if (RuntimeInit.DEBUG) {
                Slog.d(RuntimeInit.TAG, "RuntimeInit: Starting application from zygote");
            }
    
            Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "ZygoteInit");
            RuntimeInit.redirectLogStreams();
    
            RuntimeInit.commonInit();
            //创建Binder线程池
            ZygoteInit.nativeZygoteInit();
            return RuntimeInit.applicationInit(targetSdkVersion, argv, classLoader);
        }
    

    zygoteInit方法最后调用了RuntimeInit的applicationInit方法。下面是applicationInit方法的代码

    protected static Runnable applicationInit(int targetSdkVersion, String[] argv,
                ClassLoader classLoader) {
            nativeSetExitWithoutCleanup(true);
            VMRuntime.getRuntime().setTargetHeapUtilization(0.75f);
            VMRuntime.getRuntime().setTargetSdkVersion(targetSdkVersion);
            final Arguments args = new Arguments(argv);
            Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
            // Remaining arguments are passed to the start class's static main
            return findStaticMain(args.startClass, args.startArgs, classLoader);
        }
    

    RuntimeInit的ApplicationInit方法最后调用了findStaticMain方法,

    /**
         * Invokes a static "main(argv[]) method on class "className".
         * Converts various failing exceptions into RuntimeExceptions, with
         * the assumption that they will then cause the VM instance to exit.
         */
        protected static Runnable findStaticMain(String className, String[] argv,
                ClassLoader classLoader) {
            Class<?> cl;
    
            try {
                cl = Class.forName(className, true, classLoader);
            } 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 });
            } 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);
            }
    
            int modifiers = m.getModifiers();
            if (! (Modifier.isStatic(modifiers) && Modifier.isPublic(modifiers))) {
                throw new RuntimeException(
                        "Main method is not public and static on " + className);
            }
    
            /*
             * This throw gets caught in ZygoteInit.main(), which responds
             * by invoking the exception's run() method. This arrangement
             * clears up all the stack frames that were required in setting
             * up the process.
             */
            return new MethodAndArgsCaller(m, argv);
        }
    

    源码中给出的注释,findStaticMain方法的工作是,根据类名className,调用其指定类的main方法。这个className指的就是本文开头提到过的android.app.ActivityThread。所以findStaticMain方法的实际作用就是调用进程中的ActivityThread类的main方法。
    然后,看到上面代码的最后一行,创建了一个MethodAndArgsCaller对象并返回。MethodAndArgsCaller实现了Runnable接口,是一个可执行的任务。到这里,我们要根据方法调用往回看,最终会看到MethodAndArgsCaller对象的run方法是在ZygoteInit的main方法里面

    public static void main(String argv[]) {
            ...
            caller = zygoteServer.runSelectLoop(abiList);
            ...
            // We're in the child process and have exited the select loop. Proceed to execute the
            // command.
            if (caller != null) {
                caller.run();
            }
    }
    

    接下来,我们来看看MethodAndArgsCaller的run方法

    public void run() {
                try {
                    mMethod.invoke(null, new Object[] { mArgs });
                } catch (IllegalAccessException ex) {
                    throw new RuntimeException(ex);
                } catch (InvocationTargetException ex) {
                    Throwable cause = ex.getCause();
                    if (cause instanceof RuntimeException) {
                        throw (RuntimeException) cause;
                    } else if (cause instanceof Error) {
                        throw (Error) cause;
                    }
                    throw new RuntimeException(ex);
                }
            }
    

    这里的mMethod方法就是ActivityThrea的main方法,调用mMethod的invoke方法后,ActivityThread的main方法就会被动态调用,app进程就进入了ActivityThread的main方法中,讲到这里,app进程的启动过程就算完成了。

    总结

    本文主要从源码分析的角度,介绍app进程的启动过程,这对于理解应用程序的启动机制以及相关的优化方面,都是很有意义的。面试的时候,也会经常被问到。个人认为其中比较重要的几个关键点是:

    • AMS与Zygote进程之间的Socket通信,传递app进程的启动参数
    • Zygote进程通过fork自身,创建app进程
    • app进程的初始化,创建Binder线程池,启动ActivityThread线程

    本文参考:
    《Android进阶解密》第三章
    Android 安全机制(1)uid 、 gid 与 pid

    相关文章

      网友评论

        本文标题:Android应用程序进程启动过程-源码分析

        本文链接:https://www.haomeiwen.com/subject/kklzuhtx.html