由于四大组件的启动都涉及到进程的启动,因此我们这章先讲一下进程启动流程,然后再讲四大组件的启动流程。

基础知识

Android应用程序框架层创建的应用程序进程具有两个特点,一是进程的入口函数是ActivityThread.main,二是进程天然支持Binder进程间通信机制;这两个特点都是在进程的初始化过程中实现的。(引用自老罗安卓之旅-Android应用程序进程启动过程的源代码分析)

进程按照重要性可以分为下面五类:

  • 前台进程(Foreground process)
  • 可见进程(Visible process)
  • 服务进程(Service process)
  • 后台进程(Background process)
  • 空进程(Empty process)

进程启动流程

AMS(ActivityMagagerService)启动进程是从其成员函数startProcessLocked开始调用Process.start方法开始的。我们先看一下进程启动的时序图:

1. Process.start方法:

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 {// 请求Zygote进程创建一个应用进程return startViaZygote(processClass, niceName, uid, gid, gids,debugFlags, mountExternal, targetSdkVersion, seInfo,abi, instructionSet, appDataDir, zygoteArgs);} catch (ZygoteStartFailedEx ex) {Log.e(LOG_TAG,"Starting VM process through Zygote failed");throw new RuntimeException("Starting VM process through Zygote failed", ex);}
}

注意:传入的第一个参数是“android.app.ActivityThread”,这是进程初始化要加载的类,这个类加载到进程之后,就会把这个类的静态成员方法main作为进程的入口。然后调用startViaZygote方法。

2. startViaZygote方法:

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) {ArrayList<String> argsForZygote = new ArrayList<String>();// 保存要创建应用程序进程的启动参数到argsForZygote中...// 保存id到argsForZygote中...// 保存其他信息到argsForZygote中...// 请求Zygote进程创建这个应用进程return zygoteSendArgsAndGetResult(openZygoteSocketIfNeeded(abi), argsForZygote);}}

这个方法主要是保存信息到argsForZygote中,然后调用openZygoteSocketIfNeeded,然后根据返回的值调用zygoteSendArgsAndGetResult方法,首先先看openZygoteSocketIfNeeded方法。

3. openZygoteSocketIfNeeded方法:

private static ZygoteState openZygoteSocketIfNeeded(String abi) throws ZygoteStartFailedEx{if (primaryZygoteState == null || primaryZygoteState.isClosed()) {try {// 通过调用ZygoteState.connect方法创建LocalSocket对象,以便将相应参数传入Zygote进程primaryZygoteState = ZygoteState.connect(ZYGOTE_SOCKET);} catch (IOException ioe) {throw new ZygoteStartFailedEx("Error connecting to primary zygote", ioe);}}if (primaryZygoteState.matches(abi)) {return primaryZygoteState;}// The primary zygote didn't match. Try the secondary.if (secondaryZygoteState == null || secondaryZygoteState.isClosed()) {try {// 通过调用ZygoteState.connect方法创建LocalSocket对象,以便将相应参数传入Zygote进程secondaryZygoteState = ZygoteState.connect(SECONDARY_ZYGOTE_SOCKET);} 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);}

通过ZygoteState.connect放创建primaryZygoteState对象,如果第一次创建不成功,创建第二次。connect方法代码如下:

4. ZygoteState.connect方法:

public static ZygoteState connect(String socketAddress) throws IOException{DataInputStream zygoteInputStream = null;BufferedWriter zygoteWriter = null;// 这个Socket由ZygoteInit.java文件中的ZygoteInit类在runSelectLoopMode函数侦听的。final LocalSocket zygoteSocket = new LocalSocket();try {// 开始建立连接,在连接过程中,LocalSocket对象zygoteSocket会在/dev/socket目录下找到// 一个对应的zygote文件,然后将它与自己绑定起来,这就相当于与Zygote进程中的名称为“zygote”// 的Socket建立了连接zygoteSocket.connect(new LocalSocketAddress(socketAddress,LocalSocketAddress.Namespace.RESERVED));// 连接成功以后,首先获取LocalSocket对象zygoteSocket的一个输入流,并且保存在// zygoteInputStream中,以便获得Zygote进程发送过来的通信数据zygoteInputStream = new DataInputStream(zygoteSocket.getInputStream());// 又得到LocalSocket对象zygoteSocket的一个输入流,并且保存在zygoteWriter中,以便// 可以向Zygote进程发送通信数据zygoteWriter = new BufferedWriter(new OutputStreamWriter(zygoteSocket.getOutputStream()), 256);} catch (IOException ex) {...}String abiListString = getAbiList(zygoteWriter, zygoteInputStream);Log.i("Zygote", "Process: zygote socket opened, supported ABIS: " + abiListString);// 创建的LocalSocket对象zygoteSocket会保存在ZygoteState中return new ZygoteState(zygoteSocket, zygoteInputStream, zygoteWriter,Arrays.asList(abiListString.split(",")));}

首先创建一个LocalSocket对象,这个LocalSocket对象是在ZygoteInit中的runSelectLoop函数进行监听的。然后通过connect方法并且传入连接地址连接该Socket,连接以后会获取输入流DataInputStream,以便获得Zygote进程发送过来的通信数据,然后又获取BufferedWriter输入流,以便向Zygote进程发送通信数据。最后会返回一个ZygoteState对象。下面我们看一下LocalSocket.connect方法。

5. LocalSocket.connect方法:

    public void connect(LocalSocketAddress endpoint) throws IOException{synchronized (this) {if (isConnected) {throw new IOException("already connected");}implCreateIfNeeded();impl.connect(endpoint, 0);isConnected = true;isBound = true;}}

如果已经连接,抛出异常,因为连接完成后,会关闭连接,使用时在打开连接。最后调用native方法连接socket,并且改变连接标签。

6. 回到第二步,调用完openZygoteSocketIfNeeded返回参数ZygoteState传入到zygoteSendArgsAndGetResult方法中:

    private static ProcessStartResult zygoteSendArgsAndGetResult(ZygoteState zygoteState, ArrayList<String> args)throws ZygoteStartFailedEx{try {// Throw early if any of the arguments are malformed. This means we can// avoid writing a partial response to the zygote.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();// Zygote进程接收到这些数据之后,就会创建一个新的应用程序进程,并且将这个新创建的应用程序进程// 的PID返回给Activity管理服务AMS// Should there be a timeout on this?ProcessStartResult result = new ProcessStartResult();// Always read the entire result from the input stream to avoid leaving// bytes in the stream for future process starts to accidentally stumble// upon.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);}}

这方法通过Socket流的方式将启动进程的信息发送出去,从步骤4可知,这个Socket的监听是ZygoteInit类中的runSelectLoop方法,我们接着看这个方法。

7. ZygoteInit.runSelectLoop方法:

    private static void runSelectLoop(String abiList) throws MethodAndArgsCaller{ArrayList<FileDescriptor> fds = new ArrayList<FileDescriptor>();ArrayList<ZygoteConnection> peers = new ArrayList<ZygoteConnection>();fds.add(sServerSocket.getFileDescriptor());peers.add(null);while (true) {StructPollfd[] pollFds = new StructPollfd[fds.size()];for (int i = 0; i < pollFds.length; ++i) {pollFds[i] = new StructPollfd();pollFds[i].fd = fds.get(i);pollFds[i].events = (short) POLLIN;}try {Os.poll(pollFds, -1);} catch (ErrnoException ex) {throw new RuntimeException("poll failed", ex);}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();if (done) {peers.remove(i);fds.remove(i);}}}}}

数据通过Socket发送以后,Zygote进程接收到后会调用peers.get(i).runOnce()方法。这个peers.get(i)是获取ZygoteConnection对象,表示一个Socket连接,然后调用它的runOnce方法。

8. ZygoteConnection.runOnce方法:

    boolean runOnce() throws ZygoteInit.MethodAndArgsCaller{String args[];Arguments parsedArgs = null;FileDescriptor[] descriptors;try {// 获得创建应用程序进程需要的启动参数,并且保存在一个Arguments对象parsedArgs中args = readArgumentList();descriptors = mSocket.getAncillaryFileDescriptors();} catch (IOException ex) {Log.w(TAG, "IOException on command socket " + ex.getMessage());closeSocket();return true;}.../** the stderr of the most recent request, if avail */PrintStream newStderr = null;if (descriptors != null && descriptors.length >= 3) {newStderr = new PrintStream(new FileOutputStream(descriptors[2]));}int pid = -1;FileDescriptor childPipeFd = null;FileDescriptor serverPipeFd = null;try {parsedArgs = new Arguments(args);if (parsedArgs.abiListQuery) {return handleAbiListQuery();}...// 调用forkAndSpecialize方法来创建这个应用程序进程,最终通过函数fork在当前进程中创建一个子进程,// 因此,当它的返回值等于0时,就表示是在新创建的子进程中执行的,这时候ZygoteConnection类就会调用// 成员函数handleChildProc来启动这个子进程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) {...} catch (IllegalArgumentException ex) {...} catch (ZygoteSecurityException ex) {...}try {if (pid == 0) {...handleChildProc(parsedArgs, descriptors, childPipeFd, newStderr);return true;} else {...return handleParentProc(pid, descriptors, serverPipeFd, parsedArgs);}} finally {...}}

首先通过Zygote.forkAndSpecialize方法来创建一个新的进程,并且返回其pid。因为我们在分心新建进程,因此我们只分析pid为0的情况,pid为0时会调用handleChildProc方法,

9. handleChildProc方法:

    private void handleChildProc(Arguments parsedArgs,FileDescriptor[] descriptors, FileDescriptor pipeFd, PrintStream newStderr)throws ZygoteInit.MethodAndArgsCaller{...// End of the postFork event.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 {// 初始化运行库以及启动一个Binder线程池RuntimeInit.zygoteInit(parsedArgs.targetSdkVersion,parsedArgs.remainingArgs, null /* classLoader */);}}

由于我们之前加入参数是没有parsedArgs.invokeWith这个参数,因此这里是null,因此会走else里面的代码,执行RuntimeInit.zygoteInit方法。

10. RuntimeInit.zygoteInit方法:

    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();// 然后调用下面Native函数在新创建的应用程序进程中启动一个Binder线程池nativeZygoteInit();applicationInit(targetSdkVersion, argv, classLoader);}

首先调用nativeZygoteInit函数,这是一个native函数,函数的目的是在新创建的应用程序进程中启动一个Binder线程池然后进行进程间通信。然后调用applicationInit函数

11. applicationInit函数:

    private static void applicationInit(int targetSdkVersion, String[] argv, ClassLoader classLoader)throws ZygoteInit.MethodAndArgsCaller{...// Remaining arguments are passed to the start class's static main// 我们知道AMS指定了新创建的应用程序进程的入口函数为ActivityThread类的静态成员函数main。实际是// 通过下面方法进入到ActivityThread类的静态成员函数main中的invokeStaticMain(args.startClass, args.startArgs, classLoader);}

我们在前面讲过args.startClass传入进来的是"android.app.ActivityThread",表示要执行"android.app.ActivityThread"的main函数。

12. invokeStaticMain函数:

    private static void invokeStaticMain(String className, String[] argv, ClassLoader classLoader)throws ZygoteInit.MethodAndArgsCaller{Class<?> cl;try {cl = Class.forName(className, true, classLoader);} catch (ClassNotFoundException ex) {...}Method m;try {// 获取它的静态成员函数main,并且保存在Method对象m中m = cl.getMethod("main", new Class[] { String[].class });} catch (NoSuchMethodException ex) {...} catch (SecurityException ex) {...}.../** 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.* 将这个Method对象封装在一个MethodAndArgsCaller对象中,并且将这个MethodAndArgsCaller对象作为* 一个异常对象抛出来给当前应用程序处理*/throw new ZygoteInit.MethodAndArgsCaller(m, argv);/*** 引用自Android系统源代码情景分析中的Android进程启动分析一文* 新创建的应用程序进程复制了Zygote进程的地址空间,因此,当前新创建的应用程序进程的调用栈与Zygote* 进程的调用堆栈是一致的。Zygote进程最开始执行的是应用程序app_process的入口函数main,接着再调用* ZygoteInit类的静态成员函数main,最后进入到ZygoteInit类的静态成员函数runSelectLoopMode来循环* 等待Activity管理服务AMS发送过来的创建新的应用进程的请求。当Zygote进程收到AMS发送过来的创建新的* 应用程序进程的请求之后,它就会创建一个新的应用程序进程,并且让这个新创建的应用程序进程沿着* ZygoteInit类的静态函数runSelectLoopModel一直执行到RuntimeInit类的静态成员函数* invokeStaticMain。因此,当RuntimeInit类的静态成员函数invokeStaticMain抛出一个类型为* MethodAndArgsCaller的常时,系统就会沿着这个调用过程往后找到一个适合的代码块来捕获它。* 由于ZygoteInit函数main捕获了类型为MethodAndArgsCaller的异常,因此,接下来它就会被调用,以便* 可以处理这里抛出的一个MethodAndArgsCaller异常。因此,抛出这个异常后,会执行ZygoteInit中main* 函数中的catch来捕获异常。**/}

这个就是通过类加载器加载ActivityThread,然后调用起main方法。然后抛出异常,通过ZygoteInit中main函数中的catch来捕获异常。

13. ZygoteInit.main函数:

    public static void main(String argv[]){...} catch (MethodAndArgsCaller caller) {// 捕获MethodAndArgsCaller异常以后会调用MethodAndArgsCaller的run函数// ActivityThread.maincaller.run();} catch (Throwable ex) {...}}

通过步骤12可知抛出的异常是MethodAndArgsCaller异常,因此会执行caller.run方法。

14. MethodAndArgsCaller.run:

        /*** 注释来自Android系统源代码情景分析* 这里开始调用ActivityThread.main方法,为什么要绕这么远呢,前面提到,AMS请求Zygote进程创建的应用* 程序进程的入口函数为ActivityThread的main函数,但是由于新创建的应用程序进程一开始就需要再内部初始* 化运行时库,以及启动Binder线程池,因此,ActivityThread的main函数被调用时,新创建的应用程序进程* 实际上已经执行了相当多的代码,为了使得西创建的应用程序的进程觉得它的入口函数就是ActivityThread类* 的main函数,系统就不能直接调用,而是抛出异常回到ZygoteInit的main函数中,然后间接调用它,这样就* 可以巧妙的利用Java语言的异常处理来清理它前面调用的堆栈了*/public void run(){try {// 调用ActivityThread.mainmMethod.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.invoke方法调用ActivityThread的main方法。

15. ActivityThread.mian方法:

    /*** 启动新的进程时调用Process的start方法会最终调用改函数* 启动新的进程主要做了两件事:* 1.在进程中创建了一个ActivityThread对象,并调用了它的成员函数attach向AMS发送一个启动完成的通知* 2.调用Looper类的静态成员函数prepareMainLooper创建一个消息循环,并且在向AMS发送启动完成通知后,*   使得当前进程进入到这个消息循环中** @param args*/public static void main(String[] args){...// 创建looperLooper.prepareMainLooper();ActivityThread thread = new ActivityThread();// 传入false表示非系统进程启动thread.attach(false);if (sMainThreadHandler == null) {// 获取主线程的HandlersMainThreadHandler = thread.getHandler();}...// 开始无限循环Looper.loop();throw new RuntimeException("Main thread loop unexpectedly exited");}

这里主要是创建该线程的looper,然后创建ActivityThread对象,然后进入消息循环。然后我们就可以启动Activity或者Service了。

Android开发群:192508518

微信公众账号:Code-MX

注:本文原创,转载请注明出处,多谢。

Android系统源码分析--Process启动过程相关推荐

  1. (连载)Android系统源码分析--Android系统启动流程之Linux内核

    > **这是一个连载的博文系列,我将持续为大家提供尽可能透彻的Android源码分析 [github连载地址](https://github.com/foxleezh/AOSP/issues/3 ...

  2. android 系统源码分析

    获得Android源码后,我们来分析源码结构.源码的全部工程分为如下三个部分. ①Core Project:核心工程部分,这是建立Android系统的基础,保存在根目录的各个文件夹中. ②Extern ...

  3. Android系统源码分析--Activity的finish过程

    上一篇我们分析了Activity的启动流程,由于代码量很大,还是没有分析的很详细,但是基本流程都出来了,更详细的东西还是要去看源码.这一章来分析Activity的finish过程,分析一下finish ...

  4. Nimbus三Storm源码分析--Nimbus启动过程

    Nimbus server, 首先从启动命令开始, 同样是使用storm命令"storm nimbus"来启动 看下源码, 此处和上面client不同, jvmtype=" ...

  5. 飞鸽传书源码分析-程序启动过程

    本文章是在飞鸽传书的2.06源码基础上分析 飞鸽传书源码运行流程如下,本篇文章只说明了飞鸽传书的启动过程,对于飞鸽伟书的消息机制及菜单加载等功能都不在本篇文章范围之内. 1. WinMain函数 [c ...

  6. Android系统源码分析/多媒体框架/音频子系统/常用结构体分析-audio.h

    audio_stream_type_t 定义音频流类型,主要是手机系统各类典型的音频流做出属性上的区分,举个例子:电话和媒体2种类型的音频不管从输出的设备(耳机.功放.还是蓝牙)都是存在明显的不同.把 ...

  7. elasticSearch6源码分析(1)启动过程

    1.找到bin目录,下面有elasticSearch的sh文件,查看执行过程 exec \"$JAVA" \$ES_JAVA_OPTS \-Des.path.home=" ...

  8. workerman源码分析之启动过程

    2019独角兽企业重金招聘Python工程师标准>>> http://www.cnblogs.com/CpNice/p/4714182.html 转载于:https://my.osc ...

  9. Android 音频源码分析——AudioTrack设备选择

    Android 音频源码分析--AndroidRecord录音(一) Android 音频源码分析--AndroidRecord录音(二) Android 音频源码分析--AndroidRecord音 ...

  10. Android 音频源码分析——AndroidRecord录音(一)

    Android 音频源码分析--AndroidRecord录音(一) Android 音频源码分析--AndroidRecord录音(二) Android 音频源码分析--AndroidRecord音 ...

最新文章

  1. TNS-01201: Listener cannot find executable /u01/oracle/bin/extproc for SID orcl Listener failed to
  2. 【Linux】【Services】【Package】rpm
  3. 697. Degree of an Array 频率最高元素的最小覆盖子数组
  4. Gym 101606 F-Flipping Coins(概率dp)
  5. 【特征工程】(未完成)特征选择
  6. DIV+CSS实战(四)
  7. objective-C Blocks 讲解
  8. apache camel 相关配置_Apache Camel的Java编程入门指南
  9. 蚂蚁借呗和京东金条全面对比,哪个更划算?
  10. d3.js 入门指南
  11. 操作数据库(对战小游戏)
  12. 解决 MyEclipse build workspace 慢,validation javascript 更慢的问题
  13. CentOS7.7安装MySQL5.6并配置环境变量(详细版)
  14. JS学习笔记4-JavaScript 注释
  15. linux arm桌面程序,Electron 从零创建一个 Windows/OS X/Linux 的桌面可执行程序
  16. [转载] Python max() 方法
  17. js中的相等与不等运算
  18. IPSEC 003 ---- IPSEC携手IKE,珠联璧合显神威
  19. 判断当前页面是否在微信浏览器中打开
  20. FX3SA三菱PLC使用软件GX Works2编写程序(梯形图等)

热门文章

  1. 《JavaScript语言精粹(修订版)》试读
  2. Yahoo!用户体验与设计前副总裁推荐——《设计模式》
  3. 探寻成功之路 企业共同关心
  4. 19【13】DIN:深度兴趣网络
  5. 面向对象之Python的链表实现(二)循环链表
  6. Django2 SQLite3迁移到MySQL数据库
  7. php 顺序排序,PHP顺序排序
  8. Kotlin — 适用于移动端跨平台
  9. Flutter学习 — 使用WebSockets
  10. Lunix服务器上项目迁移命令