【凯子哥带你学Framework】Activity启动过程全解析

目录(?)[+]

It’s right time to learn Android’s Framework !

前言

  • 一个App是怎么启动起来的?
  • App的程序入口到底是哪里?
  • Launcher到底是什么神奇的东西?
  • 听说还有个AMS的东西,它是做什么的?
  • Binder是什么?他是如何进行IPC通信的?
  • Activity生命周期到底是什么时候调用的?被谁调用的?
  • 等等…

你是不是还有很多类似的疑问一直没有解决?没关系,这篇文章将结合源码以及大量的优秀文章,站在巨人的肩膀上,更加通俗的来试着解释一些问题。但是毕竟源码繁多、经验有限,文中不免会出现一些纰漏甚至是错误,还恳请大家指出,互相学习。

学习目标

  1. 了解从手机开机第一个zygote进程创建,到点击桌面上的图标,进入一个App的完整流程,并且从源码的角度了解到一个Activity的生命周期是怎么回事
  2. 了解到ActivityManagerServices(即AMS)、ActivityStack、ActivityThread、Instrumentation等Android framework中非常重要的基础类的作用,及相互间的关系
  3. 了解AMS与ActivityThread之间利用Binder进行IPC通信的过程,了解AMS和ActivityThread在控制Activity生命周期起到的作用和相互之间的配合
  4. 了解与Activity相关的framework层的其他琐碎问题

写作方式

这篇文章我决定采用一问一答的方式进行。

其实在这之前,我试过把每个流程的代码调用过程,用粘贴源代码的方式写在文章里,但是写完一部分之后,发现由于代码量太大,整篇文章和老太太的裹脚布一样——又臭又长,虽然每个重要的操作可以显示出详细调用过程,但是太关注于细节反而导致从整体上不能很好的把握。所以在原来的基础之上进行了修改,对关键的几个步骤进行重点介绍,力求语言简洁,重点突出,从而让大家在更高的层次上对framework层有个认识,然后结合后面我给出的参考资料,大家就可以更加快速,更加高效的了解这一块的整体架构。

主要对象功能介绍

我们下面的文章将围绕着这几个类进行介绍。可能你第一次看的时候,印象不深,不过没关系,当你跟随者我读完这篇文章的时候,我会在最后再次列出这些对象的功能,相信那时候你会对这些类更加的熟悉和深刻。

  • ActivityManagerServices,简称AMS,服务端对象,负责系统中所有Activity的生命周期
  • ActivityThread,App的真正入口。当开启App之后,会调用main()开始运行,开启消息循环队列,这就是传说中的UI线程或者叫主线程。与ActivityManagerServices配合,一起完成Activity的管理工作
  • ApplicationThread,用来实现ActivityManagerService与ActivityThread之间的交互。在ActivityManagerService需要管理相关Application中的Activity的生命周期时,通过ApplicationThread的代理对象与ActivityThread通讯。
  • ApplicationThreadProxy,是ApplicationThread在服务器端的代理,负责和客户端的ApplicationThread通讯。AMS就是通过该代理与ActivityThread进行通信的。
  • Instrumentation,每一个应用程序只有一个Instrumentation对象,每个Activity内都有一个对该对象的引用。Instrumentation可以理解为应用进程的管家,ActivityThread要创建或暂停某个Activity时,都需要通过Instrumentation来进行具体的操作。
  • ActivityStack,Activity在AMS的栈管理,用来记录已经启动的Activity的先后关系,状态信息等。通过ActivityStack决定是否需要启动新的进程。
  • ActivityRecord,ActivityStack的管理对象,每个Activity在AMS对应一个ActivityRecord,来记录Activity的状态以及其他的管理信息。其实就是服务器端的Activity对象的映像。
  • TaskRecord,AMS抽象出来的一个“任务”的概念,是记录ActivityRecord的栈,一个“Task”包含若干个ActivityRecord。AMS用TaskRecord确保Activity启动和退出的顺序。如果你清楚Activity的4种launchMode,那么对这个概念应该不陌生。

主要流程介绍

下面将按照App启动过程的先后顺序,一问一答,来解释一些事情。

让我们开始吧!

zygote是什么?有什么作用?

首先,你觉得这个单词眼熟不?当你的程序Crash的时候,打印的红色log下面通常带有这一个单词。

zygote意为“受精卵“。Android是基于Linux系统的,而在Linux中,所有的进程都是由init进程直接或者是间接fork出来的,zygote进程也不例外。

在Android系统里面,zygote是一个进程的名字。Android是基于Linux System的,当你的手机开机的时候,Linux的内核加载完成之后就会启动一个叫“init“的进程。在Linux System里面,所有的进程都是由init进程fork出来的,我们的zygote进程也不例外。

我们都知道,每一个App其实都是

  • 一个单独的dalvik虚拟机
  • 一个单独的进程

所以当系统里面的第一个zygote进程运行之后,在这之后再开启App,就相当于开启一个新的进程。而为了实现资源共用和更快的启动速度,Android系统开启新进程的方式,是通过fork第一个zygote进程实现的。所以说,除了第一个zygote进程,其他应用所在的进程都是zygote的子进程,这下你明白为什么这个进程叫“受精卵”了吧?因为就像是一个受精卵一样,它能快速的分裂,并且产生遗传物质一样的细胞!

SystemServer是什么?有什么作用?它与zygote的关系是什么?

首先我要告诉你的是,SystemServer也是一个进程,而且是由zygote进程fork出来的。

知道了SystemServer的本质,我们对它就不算太陌生了,这个进程是Android Framework里面两大非常重要的进程之一——另外一个进程就是上面的zygote进程。

为什么说SystemServer非常重要呢?因为系统里面重要的服务都是在这个进程里面开启的,比如
ActivityManagerService、PackageManagerService、WindowManagerService等等,看着是不是都挺眼熟的?

那么这些系统服务是怎么开启起来的呢?

在zygote开启的时候,会调用ZygoteInit.main()进行初始化

<code class="hljs java has-numbering"><span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">void</span> <span class="hljs-title">main</span>(String argv[]) {

     ...ignore some code...

    <span class="hljs-comment">//在加载首个zygote的时候,会传入初始化参数,使得startSystemServer = true</span>
     <span class="hljs-keyword">boolean</span> startSystemServer = <span class="hljs-keyword">false</span>;
     <span class="hljs-keyword">for</span> (<span class="hljs-keyword">int</span> i = <span class="hljs-number">1</span>; i < argv.length; i++) {
                <span class="hljs-keyword">if</span> (<span class="hljs-string">"start-system-server"</span>.equals(argv[i])) {
                    startSystemServer = <span class="hljs-keyword">true</span>;
                } <span class="hljs-keyword">else</span> <span class="hljs-keyword">if</span> (argv[i].startsWith(ABI_LIST_ARG)) {
                    abiList = argv[i].substring(ABI_LIST_ARG.length());
                } <span class="hljs-keyword">else</span> <span class="hljs-keyword">if</span> (argv[i].startsWith(SOCKET_NAME_ARG)) {
                    socketName = argv[i].substring(SOCKET_NAME_ARG.length());
                } <span class="hljs-keyword">else</span> {
                    <span class="hljs-keyword">throw</span> <span class="hljs-keyword">new</span> RuntimeException(<span class="hljs-string">"Unknown command line argument: "</span> + argv[i]);
                }
            }

            ...ignore some code...

         <span class="hljs-comment">//开始fork我们的SystemServer进程</span>
     <span class="hljs-keyword">if</span> (startSystemServer) {
                startSystemServer(abiList, socketName);
         }

     ...ignore some code...

}</code><ul style="display: block;" class="pre-numbering"><li>1</li><li>2</li><li>3</li><li>4</li><li>5</li><li>6</li><li>7</li><li>8</li><li>9</li><li>10</li><li>11</li><li>12</li><li>13</li><li>14</li><li>15</li><li>16</li><li>17</li><li>18</li><li>19</li><li>20</li><li>21</li><li>22</li><li>23</li><li>24</li><li>25</li><li>26</li><li>27</li><li>28</li></ul>

我们看下startSystemServer()做了些什么

<code class="hljs java has-numbering">    <span class="hljs-javadoc">/**留着这个注释,就是为了说明SystemServer确实是被fork出来的
     * Prepare the arguments and fork for the system server process.
     */</span>
    <span class="hljs-keyword">private</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">boolean</span> <span class="hljs-title">startSystemServer</span>(String abiList, String socketName)
            <span class="hljs-keyword">throws</span> MethodAndArgsCaller, RuntimeException {

         ...ignore some code...

        <span class="hljs-comment">//留着这段注释,就是为了说明上面ZygoteInit.main(String argv[])里面的argv就是通过这种方式传递进来的</span>
        <span class="hljs-comment">/* Hardcoded command line to start the system server */</span>
        String args[] = {
            <span class="hljs-string">"--setuid=1000"</span>,
            <span class="hljs-string">"--setgid=1000"</span>,
            <span class="hljs-string">"--setgroups=1001,1002,1003,1004,1005,1006,1007,1008,1009,1010,1018,1032,3001,3002,3003,3006,3007"</span>,
            <span class="hljs-string">"--capabilities="</span> + capabilities + <span class="hljs-string">","</span> + capabilities,
            <span class="hljs-string">"--runtime-init"</span>,
            <span class="hljs-string">"--nice-name=system_server"</span>,
            <span class="hljs-string">"com.android.server.SystemServer"</span>,
        };

        <span class="hljs-keyword">int</span> pid;
        <span class="hljs-keyword">try</span> {
            parsedArgs = <span class="hljs-keyword">new</span> ZygoteConnection.Arguments(args);
            ZygoteConnection.applyDebuggerSystemProperty(parsedArgs);
            ZygoteConnection.applyInvokeWithSystemProperty(parsedArgs);

        <span class="hljs-comment">//确实是fuck出来的吧,我没骗你吧~不对,是fork出来的 -_-|||</span>
            <span class="hljs-comment">/* Request to fork the system server process */</span>
            pid = Zygote.forkSystemServer(
                    parsedArgs.uid, parsedArgs.gid,
                    parsedArgs.gids,
                    parsedArgs.debugFlags,
                    <span class="hljs-keyword">null</span>,
                    parsedArgs.permittedCapabilities,
                    parsedArgs.effectiveCapabilities);
        } <span class="hljs-keyword">catch</span> (IllegalArgumentException ex) {
            <span class="hljs-keyword">throw</span> <span class="hljs-keyword">new</span> RuntimeException(ex);
        }

        <span class="hljs-comment">/* For child process */</span>
        <span class="hljs-keyword">if</span> (pid == <span class="hljs-number">0</span>) {
            <span class="hljs-keyword">if</span> (hasSecondZygote(abiList)) {
                waitForSecondaryZygote(socketName);
            }

            handleSystemServerProcess(parsedArgs);
        }

        <span class="hljs-keyword">return</span> <span class="hljs-keyword">true</span>;
    }</code><ul style="display: block;" class="pre-numbering"><li>1</li><li>2</li><li>3</li><li>4</li><li>5</li><li>6</li><li>7</li><li>8</li><li>9</li><li>10</li><li>11</li><li>12</li><li>13</li><li>14</li><li>15</li><li>16</li><li>17</li><li>18</li><li>19</li><li>20</li><li>21</li><li>22</li><li>23</li><li>24</li><li>25</li><li>26</li><li>27</li><li>28</li><li>29</li><li>30</li><li>31</li><li>32</li><li>33</li><li>34</li><li>35</li><li>36</li><li>37</li><li>38</li><li>39</li><li>40</li><li>41</li><li>42</li><li>43</li><li>44</li><li>45</li><li>46</li><li>47</li><li>48</li><li>49</li><li>50</li></ul>

ActivityManagerService是什么?什么时候初始化的?有什么作用?

ActivityManagerService,简称AMS,服务端对象,负责系统中所有Activity的生命周期。

ActivityManagerService进行初始化的时机很明确,就是在SystemServer进程开启的时候,就会初始化ActivityManagerService。从下面的代码中可以看到

<code class="hljs cs has-numbering"><span class="hljs-keyword">public</span> final <span class="hljs-keyword">class</span> SystemServer {

    <span class="hljs-comment">//zygote的主入口</span>
    <span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">void</span> <span class="hljs-title">main</span>(String[] args) {
        <span class="hljs-keyword">new</span> SystemServer().run();
    }

    <span class="hljs-keyword">public</span> <span class="hljs-title">SystemServer</span>() {
        <span class="hljs-comment">// Check for factory test mode.</span>
        mFactoryTestMode = FactoryTest.getMode();
    }

    <span class="hljs-keyword">private</span> <span class="hljs-keyword">void</span> <span class="hljs-title">run</span>() {

        ...ignore some code...

        <span class="hljs-comment">//加载本地系统服务库,并进行初始化 </span>
        System.loadLibrary(<span class="hljs-string">"android_servers"</span>);
        nativeInit();

        <span class="hljs-comment">// 创建系统上下文</span>
        createSystemContext();

        <span class="hljs-comment">//初始化SystemServiceManager对象,下面的系统服务开启都需要调用SystemServiceManager.startService(Class<T>),这个方法通过反射来启动对应的服务</span>
        mSystemServiceManager = <span class="hljs-keyword">new</span> SystemServiceManager(mSystemContext);

        <span class="hljs-comment">//开启服务</span>
        <span class="hljs-keyword">try</span> {
            startBootstrapServices();
            startCoreServices();
            startOtherServices();
        } <span class="hljs-keyword">catch</span> (Throwable ex) {
            Slog.e(<span class="hljs-string">"System"</span>, <span class="hljs-string">"******************************************"</span>);
            Slog.e(<span class="hljs-string">"System"</span>, <span class="hljs-string">"************ Failure starting system services"</span>, ex);
            <span class="hljs-keyword">throw</span> ex;
        }

        ...ignore some code...

    }

    <span class="hljs-comment">//初始化系统上下文对象mSystemContext,并设置默认的主题,mSystemContext实际上是一个ContextImpl对象。调用ActivityThread.systemMain()的时候,会调用ActivityThread.attach(true),而在attach()里面,则创建了Application对象,并调用了Application.onCreate()。</span>
    <span class="hljs-keyword">private</span> <span class="hljs-keyword">void</span> <span class="hljs-title">createSystemContext</span>() {
        ActivityThread activityThread = ActivityThread.systemMain();
        mSystemContext = activityThread.getSystemContext();
        mSystemContext.setTheme(android.R.style.Theme_DeviceDefault_Light_DarkActionBar);
    }

    <span class="hljs-comment">//在这里开启了几个核心的服务,因为这些服务之间相互依赖,所以都放在了这个方法里面。</span>
    <span class="hljs-keyword">private</span> <span class="hljs-keyword">void</span> <span class="hljs-title">startBootstrapServices</span>() {

        ...ignore some code...

        <span class="hljs-comment">//初始化ActivityManagerService</span>
        mActivityManagerService = mSystemServiceManager.startService(
                ActivityManagerService.Lifecycle.class).getService();
        mActivityManagerService.setSystemServiceManager(mSystemServiceManager);

        <span class="hljs-comment">//初始化PowerManagerService,因为其他服务需要依赖这个Service,因此需要尽快的初始化</span>
        mPowerManagerService = mSystemServiceManager.startService(PowerManagerService.class);

        <span class="hljs-comment">// 现在电源管理已经开启,ActivityManagerService负责电源管理功能</span>
        mActivityManagerService.initPowerManagement();

        <span class="hljs-comment">// 初始化DisplayManagerService</span>
        mDisplayManagerService = mSystemServiceManager.startService(DisplayManagerService.class);

    <span class="hljs-comment">//初始化PackageManagerService</span>
    mPackageManagerService = PackageManagerService.main(mSystemContext, mInstaller,
       mFactoryTestMode != FactoryTest.FACTORY_TEST_OFF, mOnlyCore);

    ...ignore some code...

    }

}
</code><ul style="display: block;" class="pre-numbering"><li>1</li><li>2</li><li>3</li><li>4</li><li>5</li><li>6</li><li>7</li><li>8</li><li>9</li><li>10</li><li>11</li><li>12</li><li>13</li><li>14</li><li>15</li><li>16</li><li>17</li><li>18</li><li>19</li><li>20</li><li>21</li><li>22</li><li>23</li><li>24</li><li>25</li><li>26</li><li>27</li><li>28</li><li>29</li><li>30</li><li>31</li><li>32</li><li>33</li><li>34</li><li>35</li><li>36</li><li>37</li><li>38</li><li>39</li><li>40</li><li>41</li><li>42</li><li>43</li><li>44</li><li>45</li><li>46</li><li>47</li><li>48</li><li>49</li><li>50</li><li>51</li><li>52</li><li>53</li><li>54</li><li>55</li><li>56</li><li>57</li><li>58</li><li>59</li><li>60</li><li>61</li><li>62</li><li>63</li><li>64</li><li>65</li><li>66</li><li>67</li><li>68</li><li>69</li><li>70</li><li>71</li><li>72</li><li>73</li><li>74</li><li>75</li><li>76</li><li>77</li></ul>

经过上面这些步骤,我们的ActivityManagerService对象已经创建好了,并且完成了成员变量初始化。而且在这之前,调用createSystemContext()创建系统上下文的时候,也已经完成了mSystemContext和ActivityThread的创建。注意,这是系统进程开启时的流程,在这之后,会开启系统的Launcher程序,完成系统界面的加载与显示。

你是否会好奇,我为什么说AMS是服务端对象?下面我给你介绍下Android系统里面的服务器和客户端的概念。

其实服务器客户端的概念不仅仅存在于Web开发中,在Android的框架设计中,使用的也是这一种模式。服务器端指的就是所有App共用的系统服务,比如我们这里提到的ActivityManagerService,和前面提到的PackageManagerService、WindowManagerService等等,这些基础的系统服务是被所有的App公用的,当某个App想实现某个操作的时候,要告诉这些系统服务,比如你想打开一个App,那么我们知道了包名和MainActivity类名之后就可以打开

<code class="hljs avrasm has-numbering">Intent intent = new Intent(Intent<span class="hljs-preprocessor">.ACTION</span>_MAIN)<span class="hljs-comment">;  </span>
intent<span class="hljs-preprocessor">.addCategory</span>(Intent<span class="hljs-preprocessor">.CATEGORY</span>_LAUNCHER)<span class="hljs-comment">;              </span>
ComponentName cn = new ComponentName(packageName, className)<span class="hljs-comment">;              </span>
intent<span class="hljs-preprocessor">.setComponent</span>(cn)<span class="hljs-comment">;  </span>
startActivity(intent)<span class="hljs-comment">; </span></code><ul style="display: block;" class="pre-numbering"><li>1</li><li>2</li><li>3</li><li>4</li><li>5</li></ul>

但是,我们的App通过调用startActivity()并不能直接打开另外一个App,这个方法会通过一系列的调用,最后还是告诉AMS说:“我要打开这个App,我知道他的住址和名字,你帮我打开吧!”所以是AMS来通知zygote进程来fork一个新进程,来开启我们的目标App的。这就像是浏览器想要打开一个超链接一样,浏览器把网页地址发送给服务器,然后还是服务器把需要的资源文件发送给客户端的。

知道了Android Framework的客户端服务器架构之后,我们还需要了解一件事情,那就是我们的App和AMS(SystemServer进程)还有zygote进程分属于三个独立的进程,他们之间如何通信呢?

App与AMS通过Binder进行IPC通信,AMS(SystemServer进程)与zygote通过Socket进行IPC通信。

那么AMS有什么用呢?在前面我们知道了,如果想打开一个App的话,需要AMS去通知zygote进程,除此之外,其实所有的Activity的开启、暂停、关闭都需要AMS来控制,所以我们说,AMS负责系统中所有Activity的生命周期。

在Android系统中,任何一个Activity的启动都是由AMS和应用程序进程(主要是ActivityThread)相互配合来完成的。AMS服务统一调度系统中所有进程的Activity启动,而每个Activity的启动过程则由其所属的进程具体来完成。

这样说你可能还是觉得比较抽象,没关系,下面有一部分是专门来介绍AMS与ActivityThread如何一起合作控制Activity的生命周期的。

Launcher是什么?什么时候启动的?

当我们点击手机桌面上的图标的时候,App就由Launcher开始启动了。但是,你有没有思考过Launcher到底是一个什么东西?

Launcher本质上也是一个应用程序,和我们的App一样,也是继承自Activity

packages/apps/Launcher2/src/com/android/launcher2/Launcher.java

<code class="hljs java has-numbering"><span class="hljs-keyword">public</span> <span class="hljs-keyword">final</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">Launcher</span> <span class="hljs-keyword">extends</span> <span class="hljs-title">Activity</span>
        <span class="hljs-keyword">implements</span> <span class="hljs-title">View</span>.<span class="hljs-title">OnClickListener</span>, <span class="hljs-title">OnLongClickListener</span>, <span class="hljs-title">LauncherModel</span>.<span class="hljs-title">Callbacks</span>,
                   <span class="hljs-title">View</span>.<span class="hljs-title">OnTouchListener</span> {</span>
                   }</code><ul style="display: block;" class="pre-numbering"><li>1</li><li>2</li><li>3</li><li>4</li></ul>

Launcher实现了点击、长按等回调接口,来接收用户的输入。既然是普通的App,那么我们的开发经验在这里就仍然适用,比如,我们点击图标的时候,是怎么开启的应用呢?如果让你,你怎么做这个功能呢?捕捉图标点击事件,然后startActivity()发送对应的Intent请求呗!是的,Launcher也是这么做的,就是这么easy!

那么到底是处理的哪个对象的点击事件呢?既然Launcher是App,并且有界面,那么肯定有布局文件呀,是的,我找到了布局文件launcher.xml

<code class="hljs xml has-numbering"><span class="hljs-tag"><<span class="hljs-title">FrameLayout
</span>    <span class="hljs-attribute">xmlns:android</span>=<span class="hljs-value">"http://schemas.android.com/apk/res/android"</span>
    <span class="hljs-attribute">xmlns:launcher</span>=<span class="hljs-value">"http://schemas.android.com/apk/res/com.android.launcher"</span>
    <span class="hljs-attribute">android:id</span>=<span class="hljs-value">"@+id/launcher"</span>></span>

    <span class="hljs-tag"><<span class="hljs-title">com.android.launcher2.DragLayer
</span>        <span class="hljs-attribute">android:id</span>=<span class="hljs-value">"@+id/drag_layer"</span>
        <span class="hljs-attribute">android:layout_width</span>=<span class="hljs-value">"match_parent"</span>
        <span class="hljs-attribute">android:layout_height</span>=<span class="hljs-value">"match_parent"</span>
        <span class="hljs-attribute">android:fitsSystemWindows</span>=<span class="hljs-value">"true"</span>></span>

        <span class="hljs-comment"><!-- Keep these behind the workspace so that they are not visible when
             we go into AllApps --></span>
        <span class="hljs-tag"><<span class="hljs-title">include
</span>            <span class="hljs-attribute">android:id</span>=<span class="hljs-value">"@+id/dock_divider"</span>
            <span class="hljs-attribute">layout</span>=<span class="hljs-value">"@layout/workspace_divider"</span>
            <span class="hljs-attribute">android:layout_marginBottom</span>=<span class="hljs-value">"@dimen/button_bar_height"</span>
            <span class="hljs-attribute">android:layout_gravity</span>=<span class="hljs-value">"bottom"</span> /></span>

        <span class="hljs-tag"><<span class="hljs-title">include
</span>            <span class="hljs-attribute">android:id</span>=<span class="hljs-value">"@+id/paged_view_indicator"</span>
            <span class="hljs-attribute">layout</span>=<span class="hljs-value">"@layout/scroll_indicator"</span>
            <span class="hljs-attribute">android:layout_gravity</span>=<span class="hljs-value">"bottom"</span>
            <span class="hljs-attribute">android:layout_marginBottom</span>=<span class="hljs-value">"@dimen/button_bar_height"</span> /></span>

        <span class="hljs-comment"><!-- The workspace contains 5 screens of cells --></span>
        <span class="hljs-tag"><<span class="hljs-title">com.android.launcher2.Workspace
</span>            <span class="hljs-attribute">android:id</span>=<span class="hljs-value">"@+id/workspace"</span>
            <span class="hljs-attribute">android:layout_width</span>=<span class="hljs-value">"match_parent"</span>
            <span class="hljs-attribute">android:layout_height</span>=<span class="hljs-value">"match_parent"</span>
            <span class="hljs-attribute">android:paddingStart</span>=<span class="hljs-value">"@dimen/workspace_left_padding"</span>
            <span class="hljs-attribute">android:paddingEnd</span>=<span class="hljs-value">"@dimen/workspace_right_padding"</span>
            <span class="hljs-attribute">android:paddingTop</span>=<span class="hljs-value">"@dimen/workspace_top_padding"</span>
            <span class="hljs-attribute">android:paddingBottom</span>=<span class="hljs-value">"@dimen/workspace_bottom_padding"</span>
            <span class="hljs-attribute">launcher:defaultScreen</span>=<span class="hljs-value">"2"</span>
            <span class="hljs-attribute">launcher:cellCountX</span>=<span class="hljs-value">"@integer/cell_count_x"</span>
            <span class="hljs-attribute">launcher:cellCountY</span>=<span class="hljs-value">"@integer/cell_count_y"</span>
            <span class="hljs-attribute">launcher:pageSpacing</span>=<span class="hljs-value">"@dimen/workspace_page_spacing"</span>
            <span class="hljs-attribute">launcher:scrollIndicatorPaddingLeft</span>=<span class="hljs-value">"@dimen/workspace_divider_padding_left"</span>
            <span class="hljs-attribute">launcher:scrollIndicatorPaddingRight</span>=<span class="hljs-value">"@dimen/workspace_divider_padding_right"</span>></span>

            <span class="hljs-tag"><<span class="hljs-title">include</span> <span class="hljs-attribute">android:id</span>=<span class="hljs-value">"@+id/cell1"</span> <span class="hljs-attribute">layout</span>=<span class="hljs-value">"@layout/workspace_screen"</span> /></span>
            <span class="hljs-tag"><<span class="hljs-title">include</span> <span class="hljs-attribute">android:id</span>=<span class="hljs-value">"@+id/cell2"</span> <span class="hljs-attribute">layout</span>=<span class="hljs-value">"@layout/workspace_screen"</span> /></span>
            <span class="hljs-tag"><<span class="hljs-title">include</span> <span class="hljs-attribute">android:id</span>=<span class="hljs-value">"@+id/cell3"</span> <span class="hljs-attribute">layout</span>=<span class="hljs-value">"@layout/workspace_screen"</span> /></span>
            <span class="hljs-tag"><<span class="hljs-title">include</span> <span class="hljs-attribute">android:id</span>=<span class="hljs-value">"@+id/cell4"</span> <span class="hljs-attribute">layout</span>=<span class="hljs-value">"@layout/workspace_screen"</span> /></span>
            <span class="hljs-tag"><<span class="hljs-title">include</span> <span class="hljs-attribute">android:id</span>=<span class="hljs-value">"@+id/cell5"</span> <span class="hljs-attribute">layout</span>=<span class="hljs-value">"@layout/workspace_screen"</span> /></span>
        <span class="hljs-tag"></<span class="hljs-title">com.android.launcher2.Workspace</span>></span>

    ...ignore some code...

    <span class="hljs-tag"></<span class="hljs-title">com.android.launcher2.DragLayer</span>></span>
<span class="hljs-tag"></<span class="hljs-title">FrameLayout</span>></span></code><ul style="display: block;" class="pre-numbering"><li>1</li><li>2</li><li>3</li><li>4</li><li>5</li><li>6</li><li>7</li><li>8</li><li>9</li><li>10</li><li>11</li><li>12</li><li>13</li><li>14</li><li>15</li><li>16</li><li>17</li><li>18</li><li>19</li><li>20</li><li>21</li><li>22</li><li>23</li><li>24</li><li>25</li><li>26</li><li>27</li><li>28</li><li>29</li><li>30</li><li>31</li><li>32</li><li>33</li><li>34</li><li>35</li><li>36</li><li>37</li><li>38</li><li>39</li><li>40</li><li>41</li><li>42</li><li>43</li><li>44</li><li>45</li><li>46</li><li>47</li><li>48</li><li>49</li><li>50</li><li>51</li><li>52</li></ul>

为了方便查看,我删除了很多代码,从上面这些我们应该可以看出一些东西来:Launcher大量使用标签来实现界面的复用,而且定义了很多的自定义控件实现界面效果,dock_divider从布局的参数声明上可以猜出,是底部操作栏和上面图标布局的分割线,而paged_view_indicator则是页面指示器,和App首次进入的引导页下面的界面引导是一样的道理。当然,我们最关心的是Workspace这个布局,因为注释里面说在这里面包含了5个屏幕的单元格,想必你也猜到了,这个就是在首页存放我们图标的那五个界面(不同的ROM会做不同的DIY,数量不固定)。

接下来,我们应该打开workspace_screen布局,看看里面有什么东东。

workspace_screen.xml

<code class="hljs perl has-numbering"><com.android.launcher2.CellLayout
    xmlns:android=<span class="hljs-string">"http://schemas.android.com/apk/res/android"</span>
    xmlns:launcher=<span class="hljs-string">"http://schemas.android.com/apk/res/com.android.launcher"</span>
    android:layout_width=<span class="hljs-string">"wrap_content"</span>
    android:layout_height=<span class="hljs-string">"wrap_content"</span>
    android:paddingStart=<span class="hljs-string">"<span class="hljs-variable">@dimen</span>/cell_layout_left_padding"</span>
    android:paddingEnd=<span class="hljs-string">"<span class="hljs-variable">@dimen</span>/cell_layout_right_padding"</span>
    android:paddingTop=<span class="hljs-string">"<span class="hljs-variable">@dimen</span>/cell_layout_top_padding"</span>
    android:paddingBottom=<span class="hljs-string">"<span class="hljs-variable">@dimen</span>/cell_layout_bottom_padding"</span>
    android:hapticFeedbackEnabled=<span class="hljs-string">"false"</span>
    launcher:cellWidth=<span class="hljs-string">"<span class="hljs-variable">@dimen</span>/workspace_cell_width"</span>
    launcher:cellHeight=<span class="hljs-string">"<span class="hljs-variable">@dimen</span>/workspace_cell_height"</span>
    launcher:widthGap=<span class="hljs-string">"<span class="hljs-variable">@dimen</span>/workspace_width_gap"</span>
    launcher:heightGap=<span class="hljs-string">"<span class="hljs-variable">@dimen</span>/workspace_height_gap"</span>
    launcher:maxGap=<span class="hljs-string">"<span class="hljs-variable">@dimen</span>/workspace_max_gap"</span> /></code><ul style="display: block;" class="pre-numbering"><li>1</li><li>2</li><li>3</li><li>4</li><li>5</li><li>6</li><li>7</li><li>8</li><li>9</li><li>10</li><li>11</li><li>12</li><li>13</li><li>14</li><li>15</li></ul>

里面就一个CellLayout,也是一个自定义布局,那么我们就可以猜到了,既然可以存放图标,那么这个自定义的布局很有可能是继承自ViewGroup或者是其子类,实际上,CellLayout确实是继承自ViewGroup。在CellLayout里面,只放了一个子View,那就是ShortcutAndWidgetContainer。从名字也可以看出来,ShortcutAndWidgetContainer这个类就是用来存放快捷图标Widget小部件的,那么里面放的是什么对象呢?

在桌面上的图标,使用的是BubbleTextView对象,这个对象在TextView的基础之上,添加了一些特效,比如你长按移动图标的时候,图标位置会出现一个背景(不同版本的效果不同),所以我们找到BubbleTextView对象的点击事件,就可以找到Launcher如何开启一个App了。

除了在桌面上有图标之外,在程序列表中点击图标,也可以开启对应的程序。这里的图标使用的不是BubbleTextView对象,而是PagedViewIcon对象,我们如果找到它的点击事件,就也可以找到Launcher如何开启一个App。

其实说这么多,和今天的主题隔着十万八千里,上面这些东西,你有兴趣就看,没兴趣就直接跳过,不知道不影响这篇文章阅读。

BubbleTextView的点击事件在哪里呢?我来告诉你:在Launcher.onClick(View v)里面。

<code class="hljs java has-numbering">   <span class="hljs-javadoc">/**
     * Launches the intent referred by the clicked shortcut
     */</span>
    <span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title">onClick</span>(View v) {

          ...ignore some code...

         Object tag = v.getTag();
        <span class="hljs-keyword">if</span> (tag <span class="hljs-keyword">instanceof</span> ShortcutInfo) {
            <span class="hljs-comment">// Open shortcut</span>
            <span class="hljs-keyword">final</span> Intent intent = ((ShortcutInfo) tag).intent;
            <span class="hljs-keyword">int</span>[] pos = <span class="hljs-keyword">new</span> <span class="hljs-keyword">int</span>[<span class="hljs-number">2</span>];
            v.getLocationOnScreen(pos);
            intent.setSourceBounds(<span class="hljs-keyword">new</span> Rect(pos[<span class="hljs-number">0</span>], pos[<span class="hljs-number">1</span>],
                    pos[<span class="hljs-number">0</span>] + v.getWidth(), pos[<span class="hljs-number">1</span>] + v.getHeight()));
        <span class="hljs-comment">//开始开启Activity咯~</span>
            <span class="hljs-keyword">boolean</span> success = startActivitySafely(v, intent, tag);

            <span class="hljs-keyword">if</span> (success && v <span class="hljs-keyword">instanceof</span> BubbleTextView) {
                mWaitingForResume = (BubbleTextView) v;
                mWaitingForResume.setStayPressed(<span class="hljs-keyword">true</span>);
            }
        } <span class="hljs-keyword">else</span> <span class="hljs-keyword">if</span> (tag <span class="hljs-keyword">instanceof</span> FolderInfo) {
            <span class="hljs-comment">//如果点击的是图标文件夹,就打开文件夹</span>
            <span class="hljs-keyword">if</span> (v <span class="hljs-keyword">instanceof</span> FolderIcon) {
                FolderIcon fi = (FolderIcon) v;
                handleFolderClick(fi);
            }
        } <span class="hljs-keyword">else</span> <span class="hljs-keyword">if</span> (v == mAllAppsButton) {
        ...ignore some code...
        }
    }</code><ul style="display: block;" class="pre-numbering"><li>1</li><li>2</li><li>3</li><li>4</li><li>5</li><li>6</li><li>7</li><li>8</li><li>9</li><li>10</li><li>11</li><li>12</li><li>13</li><li>14</li><li>15</li><li>16</li><li>17</li><li>18</li><li>19</li><li>20</li><li>21</li><li>22</li><li>23</li><li>24</li><li>25</li><li>26</li><li>27</li><li>28</li><li>29</li><li>30</li><li>31</li><li>32</li></ul>

从上面的代码我们可以看到,在桌面上点击快捷图标的时候,会调用

<code class="hljs scss has-numbering"><span class="hljs-function">startActivitySafely(v, intent, tag)</span>;</code><ul style="display: block;" class="pre-numbering"><li>1</li></ul>

那么从程序列表界面,点击图标的时候会发生什么呢?实际上,程序列表界面使用的是AppsCustomizePagedView对象,所以我在这个类里面找到了onClick(View v)。

com.android.launcher2.AppsCustomizePagedView.java

<code class="hljs java has-numbering"><span class="hljs-javadoc">/**
 * The Apps/Customize page that displays all the applications, widgets, and shortcuts.
 */</span>
<span class="hljs-keyword">public</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">AppsCustomizePagedView</span> <span class="hljs-keyword">extends</span> <span class="hljs-title">PagedViewWithDraggableItems</span> <span class="hljs-keyword">implements</span>
        <span class="hljs-title">View</span>.<span class="hljs-title">OnClickListener</span>, <span class="hljs-title">View</span>.<span class="hljs-title">OnKeyListener</span>, <span class="hljs-title">DragSource</span>,
        <span class="hljs-title">PagedViewIcon</span>.<span class="hljs-title">PressedCallback</span>, <span class="hljs-title">PagedViewWidget</span>.<span class="hljs-title">ShortPressListener</span>,
        <span class="hljs-title">LauncherTransitionable</span> {</span>

     <span class="hljs-annotation">@Override</span>
    <span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title">onClick</span>(View v) {

         ...ignore some code...

        <span class="hljs-keyword">if</span> (v <span class="hljs-keyword">instanceof</span> PagedViewIcon) {
            mLauncher.updateWallpaperVisibility(<span class="hljs-keyword">true</span>);
            mLauncher.startActivitySafely(v, appInfo.intent, appInfo);
        } <span class="hljs-keyword">else</span> <span class="hljs-keyword">if</span> (v <span class="hljs-keyword">instanceof</span> PagedViewWidget) {
                 ...ignore some code..
         }
     }      
   }</code><ul style="display: block;" class="pre-numbering"><li>1</li><li>2</li><li>3</li><li>4</li><li>5</li><li>6</li><li>7</li><li>8</li><li>9</li><li>10</li><li>11</li><li>12</li><li>13</li><li>14</li><li>15</li><li>16</li><li>17</li><li>18</li><li>19</li><li>20</li><li>21</li></ul>

可以看到,调用的是

<code class="hljs avrasm has-numbering">mLauncher<span class="hljs-preprocessor">.startActivitySafely</span>(v, appInfo<span class="hljs-preprocessor">.intent</span>, appInfo)<span class="hljs-comment">;</span></code><ul style="display: block;" class="pre-numbering"><li>1</li></ul>

和上面一样!这叫什么?这叫殊途同归!

所以咱们现在又明白了一件事情:不管从哪里点击图标,调用的都是Launcher.startActivitySafely()。

  • 下面我们就可以一步步的来看一下Launcher.startActivitySafely()到底做了什么事情。
<code class="hljs lasso has-numbering"> boolean startActivitySafely(View v, Intent intent, Object <span class="hljs-built_in">tag</span>) {
        boolean success <span class="hljs-subst">=</span> <span class="hljs-literal">false</span>;
        try {
            success <span class="hljs-subst">=</span> startActivity(v, intent, <span class="hljs-built_in">tag</span>);
        } catch (ActivityNotFoundException e) {
            Toast<span class="hljs-built_in">.</span>makeText(this, R<span class="hljs-built_in">.</span><span class="hljs-built_in">string</span><span class="hljs-built_in">.</span>activity_not_found, Toast<span class="hljs-built_in">.</span>LENGTH_SHORT)<span class="hljs-built_in">.</span>show();
            <span class="hljs-keyword">Log</span><span class="hljs-built_in">.</span>e(<span class="hljs-built_in">TAG</span>, <span class="hljs-string">"Unable to launch. tag="</span> <span class="hljs-subst">+</span> <span class="hljs-built_in">tag</span> <span class="hljs-subst">+</span> <span class="hljs-string">" intent="</span> <span class="hljs-subst">+</span> intent, e);
        }
        <span class="hljs-keyword">return</span> success;
    }</code><ul style="display: block;" class="pre-numbering"><li>1</li><li>2</li><li>3</li><li>4</li><li>5</li><li>6</li><li>7</li><li>8</li><li>9</li><li>10</li></ul>

调用了startActivity(v, intent, tag)

<code class="hljs avrasm has-numbering"> boolean startActivity(View v, Intent intent, Object tag) {

        intent<span class="hljs-preprocessor">.addFlags</span>(Intent<span class="hljs-preprocessor">.FLAG</span>_ACTIVITY_NEW_TASK)<span class="hljs-comment">;</span>
        try {
            boolean useLaunchAnimation = (v != null) &&
                    !intent<span class="hljs-preprocessor">.hasExtra</span>(INTENT_EXTRA_IGNORE_LAUNCH_ANIMATION)<span class="hljs-comment">;</span>

            if (useLaunchAnimation) {
                if (user == null || user<span class="hljs-preprocessor">.equals</span>(android<span class="hljs-preprocessor">.os</span><span class="hljs-preprocessor">.Process</span><span class="hljs-preprocessor">.myUserHandle</span>())) {
                    startActivity(intent, opts<span class="hljs-preprocessor">.toBundle</span>())<span class="hljs-comment">;</span>
                } else {
                    launcherApps<span class="hljs-preprocessor">.startMainActivity</span>(intent<span class="hljs-preprocessor">.getComponent</span>(), user,
                            intent<span class="hljs-preprocessor">.getSourceBounds</span>(),
                            opts<span class="hljs-preprocessor">.toBundle</span>())<span class="hljs-comment">;</span>
                }
            } else {
                if (user == null || user<span class="hljs-preprocessor">.equals</span>(android<span class="hljs-preprocessor">.os</span><span class="hljs-preprocessor">.Process</span><span class="hljs-preprocessor">.myUserHandle</span>())) {
                    startActivity(intent)<span class="hljs-comment">;</span>
                } else {
                    launcherApps<span class="hljs-preprocessor">.startMainActivity</span>(intent<span class="hljs-preprocessor">.getComponent</span>(), user,
                            intent<span class="hljs-preprocessor">.getSourceBounds</span>(), null)<span class="hljs-comment">;</span>
                }
            }
            return true<span class="hljs-comment">;</span>
        } catch (SecurityException e) {
        ...
        }
        return false<span class="hljs-comment">;</span>
    }</code><ul style="display: block;" class="pre-numbering"><li>1</li><li>2</li><li>3</li><li>4</li><li>5</li><li>6</li><li>7</li><li>8</li><li>9</li><li>10</li><li>11</li><li>12</li><li>13</li><li>14</li><li>15</li><li>16</li><li>17</li><li>18</li><li>19</li><li>20</li><li>21</li><li>22</li><li>23</li><li>24</li><li>25</li><li>26</li><li>27</li><li>28</li><li>29</li></ul>

这里会调用Activity.startActivity(intent, opts.toBundle()),这个方法熟悉吗?这就是我们经常用到的Activity.startActivity(Intent)的重载函数。而且由于设置了

<code class="hljs avrasm has-numbering"> intent<span class="hljs-preprocessor">.addFlags</span>(Intent<span class="hljs-preprocessor">.FLAG</span>_ACTIVITY_NEW_TASK)<span class="hljs-comment">;</span></code><ul style="display: block;" class="pre-numbering"><li>1</li></ul>

所以这个Activity会添加到一个新的Task栈中,而且,startActivity()调用的其实是startActivityForResult()这个方法。

<code class="hljs java has-numbering"><span class="hljs-annotation">@Override</span>
    <span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title">startActivity</span>(Intent intent, @Nullable Bundle options) {
        <span class="hljs-keyword">if</span> (options != <span class="hljs-keyword">null</span>) {
            startActivityForResult(intent, -<span class="hljs-number">1</span>, options);
        } <span class="hljs-keyword">else</span> {
            <span class="hljs-comment">// Note we want to go through this call for compatibility with</span>
            <span class="hljs-comment">// applications that may have overridden the method.</span>
            startActivityForResult(intent, -<span class="hljs-number">1</span>);
        }
    }</code><ul style="display: block;" class="pre-numbering"><li>1</li><li>2</li><li>3</li><li>4</li><li>5</li><li>6</li><li>7</li><li>8</li><li>9</li><li>10</li></ul>

所以我们现在明确了,Launcher中开启一个App,其实和我们在Activity中直接startActivity()基本一样,都是调用了Activity.startActivityForResult()。

Instrumentation是什么?和ActivityThread是什么关系?

还记得前面说过的Instrumentation对象吗?每个Activity都持有Instrumentation对象的一个引用,但是整个进程只会存在一个Instrumentation对象。当startActivityForResult()调用之后,实际上还是调用了mInstrumentation.execStartActivity()

<code class="hljs cs has-numbering"><span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title">startActivityForResult</span>(Intent intent, <span class="hljs-keyword">int</span> requestCode, @Nullable Bundle options) {
        <span class="hljs-keyword">if</span> (mParent == <span class="hljs-keyword">null</span>) {
            Instrumentation.ActivityResult ar =
                mInstrumentation.execStartActivity(
                    <span class="hljs-keyword">this</span>, mMainThread.getApplicationThread(), mToken, <span class="hljs-keyword">this</span>,
                    intent, requestCode, options);
            <span class="hljs-keyword">if</span> (ar != <span class="hljs-keyword">null</span>) {
                mMainThread.sendActivityResult(
                    mToken, mEmbeddedID, requestCode, ar.getResultCode(),
                    ar.getResultData());
            }
            ...ignore some code...    
        } <span class="hljs-keyword">else</span> {
            <span class="hljs-keyword">if</span> (options != <span class="hljs-keyword">null</span>) {
                 <span class="hljs-comment">//当现在的Activity有父Activity的时候会调用,但是在startActivityFromChild()内部实际还是调用的mInstrumentation.execStartActivity()</span>
                mParent.startActivityFromChild(<span class="hljs-keyword">this</span>, intent, requestCode, options);
            } <span class="hljs-keyword">else</span> {
                mParent.startActivityFromChild(<span class="hljs-keyword">this</span>, intent, requestCode);
            }
        }
         ...ignore some code...    
    }</code><ul style="display: block;" class="pre-numbering"><li>1</li><li>2</li><li>3</li><li>4</li><li>5</li><li>6</li><li>7</li><li>8</li><li>9</li><li>10</li><li>11</li><li>12</li><li>13</li><li>14</li><li>15</li><li>16</li><li>17</li><li>18</li><li>19</li><li>20</li><li>21</li><li>22</li></ul>

下面是mInstrumentation.execStartActivity()的实现

<code class="hljs cs has-numbering"> <span class="hljs-keyword">public</span> ActivityResult <span class="hljs-title">execStartActivity</span>(
            Context who, IBinder contextThread, IBinder token, Activity target,
            Intent intent, <span class="hljs-keyword">int</span> requestCode, Bundle options) {
        IApplicationThread whoThread = (IApplicationThread) contextThread;
            ...ignore some code...
      <span class="hljs-keyword">try</span> {
            intent.migrateExtraStreamToClipData();
            intent.prepareToLeaveProcess();
            <span class="hljs-keyword">int</span> result = ActivityManagerNative.getDefault()
                .startActivity(whoThread, who.getBasePackageName(), intent,
                        intent.resolveTypeIfNeeded(who.getContentResolver()),
                        token, target != <span class="hljs-keyword">null</span> ? target.mEmbeddedID : <span class="hljs-keyword">null</span>,
                        requestCode, <span class="hljs-number">0</span>, <span class="hljs-keyword">null</span>, options);
            checkStartActivityResult(result, intent);
        } <span class="hljs-keyword">catch</span> (RemoteException e) {
        }
        <span class="hljs-keyword">return</span> <span class="hljs-keyword">null</span>;
    }</code><ul style="display: block;" class="pre-numbering"><li>1</li><li>2</li><li>3</li><li>4</li><li>5</li><li>6</li><li>7</li><li>8</li><li>9</li><li>10</li><li>11</li><li>12</li><li>13</li><li>14</li><li>15</li><li>16</li><li>17</li><li>18</li></ul>

所以当我们在程序中调用startActivity()的 时候,实际上调用的是Instrumentation的相关的方法。

Instrumentation意为“仪器”,我们先看一下这个类里面包含哪些方法吧

我们可以看到,这个类里面的方法大多数和Application和Activity有关,是的,这个类就是完成对Application和Activity初始化和生命周期的工具类。比如说,我单独挑一个callActivityOnCreate()让你看看

<code class="hljs cs has-numbering"><span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title">callActivityOnCreate</span>(Activity activity, Bundle icicle) {
        prePerformCreate(activity);
        activity.performCreate(icicle);
        postPerformCreate(activity);
    }</code><ul style="display: block;" class="pre-numbering"><li>1</li><li>2</li><li>3</li><li>4</li><li>5</li></ul>

对activity.performCreate(icicle);这一行代码熟悉吗?这一行里面就调用了传说中的Activity的入口函数onCreate(),不信?接着往下看

Activity.performCreate()

<code class="hljs scss has-numbering">final void <span class="hljs-function">performCreate(Bundle icicle)</span> {
        <span class="hljs-function">onCreate(icicle)</span>;
        mActivityTransitionState<span class="hljs-class">.readState</span>(icicle);
        <span class="hljs-function">performCreateCommon()</span>;
    }</code><ul style="display: block;" class="pre-numbering"><li>1</li><li>2</li><li>3</li><li>4</li><li>5</li></ul>

没骗你吧,onCreate在这里调用了吧。但是有一件事情必须说清楚,那就是这个Instrumentation类这么重要,为啥我在开发的过程中,没有发现他的踪迹呢?

是的,Instrumentation这个类很重要,对Activity生命周期方法的调用根本就离不开他,他可以说是一个大管家,但是,这个大管家比较害羞,是一个女的,管内不管外,是老板娘~

那么你可能要问了,老板是谁呀?
老板当然是大名鼎鼎的ActivityThread了!

ActivityThread你都没听说过?那你肯定听说过传说中的UI线程吧?是的,这就是UI线程。我们前面说过,App和AMS是通过Binder传递信息的,那么ActivityThread就是专门与AMS的外交工作的。

AMS说:“ActivityThread,你给我暂停一个Activity!”
ActivityThread就说:“没问题!”然后转身和Instrumentation说:“老婆,AMS让暂停一个Activity,我这里忙着呢,你快去帮我把这事办了把~”
于是,Instrumentation就去把事儿搞定了。

所以说,AMS是董事会,负责指挥和调度的,ActivityThread是老板,虽然说家里的事自己说了算,但是需要听从AMS的指挥,而Instrumentation则是老板娘,负责家里的大事小事,但是一般不抛头露面,听一家之主ActivityThread的安排。

如何理解AMS和ActivityThread之间的Binder通信?

前面我们说到,在调用startActivity()的时候,实际上调用的是

<code class="hljs avrasm has-numbering">mInstrumentation<span class="hljs-preprocessor">.execStartActivity</span>()</code><ul style="display: block;" class="pre-numbering"><li>1</li></ul>

但是到这里还没完呢!里面又调用了下面的方法

<code class="hljs avrasm has-numbering">ActivityManagerNative<span class="hljs-preprocessor">.getDefault</span>()
                <span class="hljs-preprocessor">.startActivity</span></code><ul style="display: block;" class="pre-numbering"><li>1</li><li>2</li></ul>

这里的ActivityManagerNative.getDefault返回的就是ActivityManagerService的远程接口,即ActivityManagerProxy。

怎么知道的呢?往下看

<code class="hljs java has-numbering"><span class="hljs-keyword">public</span> <span class="hljs-keyword">abstract</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">ActivityManagerNative</span> <span class="hljs-keyword">extends</span> <span class="hljs-title">Binder</span> <span class="hljs-keyword">implements</span> <span class="hljs-title">IActivityManager</span>
{</span>

<span class="hljs-comment">//从类声明上,我们可以看到ActivityManagerNative是Binder的一个子类,而且实现了IActivityManager接口</span>
 <span class="hljs-keyword">static</span> <span class="hljs-keyword">public</span> IActivityManager <span class="hljs-title">getDefault</span>() {
        <span class="hljs-keyword">return</span> gDefault.get();
    }

 <span class="hljs-comment">//通过单例模式获取一个IActivityManager对象,这个对象通过asInterface(b)获得</span>
 <span class="hljs-keyword">private</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">final</span> Singleton<IActivityManager> gDefault = <span class="hljs-keyword">new</span> Singleton<IActivityManager>() {
        <span class="hljs-keyword">protected</span> IActivityManager <span class="hljs-title">create</span>() {
            IBinder b = ServiceManager.getService(<span class="hljs-string">"activity"</span>);
            <span class="hljs-keyword">if</span> (<span class="hljs-keyword">false</span>) {
                Log.v(<span class="hljs-string">"ActivityManager"</span>, <span class="hljs-string">"default service binder = "</span> + b);
            }
            IActivityManager am = asInterface(b);
            <span class="hljs-keyword">if</span> (<span class="hljs-keyword">false</span>) {
                Log.v(<span class="hljs-string">"ActivityManager"</span>, <span class="hljs-string">"default service = "</span> + am);
            }
            <span class="hljs-keyword">return</span> am;
        }
    };
}


<span class="hljs-comment">//最终返回的还是一个ActivityManagerProxy对象</span>
<span class="hljs-keyword">static</span> <span class="hljs-keyword">public</span> IActivityManager <span class="hljs-title">asInterface</span>(IBinder obj) {
        <span class="hljs-keyword">if</span> (obj == <span class="hljs-keyword">null</span>) {
            <span class="hljs-keyword">return</span> <span class="hljs-keyword">null</span>;
        }
        IActivityManager in =
            (IActivityManager)obj.queryLocalInterface(descriptor);
        <span class="hljs-keyword">if</span> (in != <span class="hljs-keyword">null</span>) {
            <span class="hljs-keyword">return</span> in;
        }

     <span class="hljs-comment">//这里面的Binder类型的obj参数会作为ActivityManagerProxy的成员变量保存为mRemote成员变量,负责进行IPC通信</span>
        <span class="hljs-keyword">return</span> <span class="hljs-keyword">new</span> ActivityManagerProxy(obj);
    }


}</code><ul style="display: block;" class="pre-numbering"><li>1</li><li>2</li><li>3</li><li>4</li><li>5</li><li>6</li><li>7</li><li>8</li><li>9</li><li>10</li><li>11</li><li>12</li><li>13</li><li>14</li><li>15</li><li>16</li><li>17</li><li>18</li><li>19</li><li>20</li><li>21</li><li>22</li><li>23</li><li>24</li><li>25</li><li>26</li><li>27</li><li>28</li><li>29</li><li>30</li><li>31</li><li>32</li><li>33</li><li>34</li><li>35</li><li>36</li><li>37</li><li>38</li><li>39</li><li>40</li><li>41</li><li>42</li></ul>

再看ActivityManagerProxy.startActivity(),在这里面做的事情就是IPC通信,利用Binder对象,调用transact(),把所有需要的参数封装成Parcel对象,向AMS发送数据进行通信。

<code class="hljs haskell has-numbering"> public int startActivity(<span class="hljs-type">IApplicationThread</span> caller, <span class="hljs-type">String</span> callingPackage, <span class="hljs-type">Intent</span> intent,
            <span class="hljs-type">String</span> resolvedType, <span class="hljs-type">IBinder</span> resultTo, <span class="hljs-type">String</span> resultWho, int requestCode,
            int startFlags, <span class="hljs-type">ProfilerInfo</span> profilerInfo, <span class="hljs-type">Bundle</span> options) throws <span class="hljs-type">RemoteException</span> {
        <span class="hljs-type">Parcel</span> <span class="hljs-typedef"><span class="hljs-keyword">data</span> = <span class="hljs-type">Parcel</span>.obtain<span class="hljs-container">()</span>;</span>
        <span class="hljs-type">Parcel</span> reply = <span class="hljs-type">Parcel</span>.obtain();
        <span class="hljs-typedef"><span class="hljs-keyword">data</span>.writeInterfaceToken<span class="hljs-container">(<span class="hljs-type">IActivityManager</span>.<span class="hljs-title">descriptor</span>)</span>;</span>
        <span class="hljs-typedef"><span class="hljs-keyword">data</span>.writeStrongBinder<span class="hljs-container">(<span class="hljs-title">caller</span> != <span class="hljs-title">null</span> ? <span class="hljs-title">caller</span>.<span class="hljs-title">asBinder</span>()</span> : null);</span>
        <span class="hljs-typedef"><span class="hljs-keyword">data</span>.writeString<span class="hljs-container">(<span class="hljs-title">callingPackage</span>)</span>;</span>
        intent.writeToParcel(<span class="hljs-typedef"><span class="hljs-keyword">data</span>, 0);</span>
        <span class="hljs-typedef"><span class="hljs-keyword">data</span>.writeString<span class="hljs-container">(<span class="hljs-title">resolvedType</span>)</span>;</span>
        <span class="hljs-typedef"><span class="hljs-keyword">data</span>.writeStrongBinder<span class="hljs-container">(<span class="hljs-title">resultTo</span>)</span>;</span>
        <span class="hljs-typedef"><span class="hljs-keyword">data</span>.writeString<span class="hljs-container">(<span class="hljs-title">resultWho</span>)</span>;</span>
        <span class="hljs-typedef"><span class="hljs-keyword">data</span>.writeInt<span class="hljs-container">(<span class="hljs-title">requestCode</span>)</span>;</span>
        <span class="hljs-typedef"><span class="hljs-keyword">data</span>.writeInt<span class="hljs-container">(<span class="hljs-title">startFlags</span>)</span>;</span>
        <span class="hljs-keyword">if</span> (profilerInfo != null) {
            <span class="hljs-typedef"><span class="hljs-keyword">data</span>.writeInt<span class="hljs-container">(1)</span>;</span>
            profilerInfo.writeToParcel(<span class="hljs-typedef"><span class="hljs-keyword">data</span>, <span class="hljs-type">Parcelable</span>.<span class="hljs-type">PARCELABLE_WRITE_RETURN_VALUE</span>);</span>
        } <span class="hljs-keyword">else</span> {
            <span class="hljs-typedef"><span class="hljs-keyword">data</span>.writeInt<span class="hljs-container">(0)</span>;</span>
        }
        <span class="hljs-keyword">if</span> (options != null) {
            <span class="hljs-typedef"><span class="hljs-keyword">data</span>.writeInt<span class="hljs-container">(1)</span>;</span>
            options.writeToParcel(<span class="hljs-typedef"><span class="hljs-keyword">data</span>, 0);</span>
        } <span class="hljs-keyword">else</span> {
            <span class="hljs-typedef"><span class="hljs-keyword">data</span>.writeInt<span class="hljs-container">(0)</span>;</span>
        }
        mRemote.transact(<span class="hljs-type">START_ACTIVITY_TRANSACTION</span>, <span class="hljs-typedef"><span class="hljs-keyword">data</span>, reply, 0);</span>
        reply.readException();
        int result = reply.readInt();
        reply.recycle();
        <span class="hljs-typedef"><span class="hljs-keyword">data</span>.recycle<span class="hljs-container">()</span>;</span>
        return result;
    }</code><ul style="display: block;" class="pre-numbering"><li>1</li><li>2</li><li>3</li><li>4</li><li>5</li><li>6</li><li>7</li><li>8</li><li>9</li><li>10</li><li>11</li><li>12</li><li>13</li><li>14</li><li>15</li><li>16</li><li>17</li><li>18</li><li>19</li><li>20</li><li>21</li><li>22</li><li>23</li><li>24</li><li>25</li><li>26</li><li>27</li><li>28</li><li>29</li><li>30</li><li>31</li><li>32</li><li>33</li></ul>

Binder本质上只是一种底层通信方式,和具体服务没有关系。为了提供具体服务,Server必须提供一套接口函数以便Client通过远程访问使用各种服务。这时通常采用Proxy设计模式:将接口函数定义在一个抽象类中,Server和Client都会以该抽象类为基类实现所有接口函数,所不同的是Server端是真正的功能实现,而Client端是对这些函数远程调用请求的包装。

为了更方便的说明客户端和服务器之间的Binder通信,下面以ActivityManagerServices和他在客户端的代理类ActivityManagerProxy为例。

ActivityManagerServices和ActivityManagerProxy都实现了同一个接口——IActivityManager。

<code class="hljs php has-numbering"><span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">ActivityManagerProxy</span> <span class="hljs-keyword">implements</span> <span class="hljs-title">IActivityManager</span>{</span>}

<span class="hljs-keyword">public</span> <span class="hljs-keyword">final</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">ActivityManagerService</span> <span class="hljs-keyword">extends</span> <span class="hljs-title">ActivityManagerNative</span>{</span>}

<span class="hljs-keyword">public</span> <span class="hljs-keyword">abstract</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">ActivityManagerNative</span> <span class="hljs-keyword">extends</span> <span class="hljs-title">Binder</span> <span class="hljs-keyword">implements</span> <span class="hljs-title">IActivityManager</span>{</span>}
</code><ul style="display: block;" class="pre-numbering"><li>1</li><li>2</li><li>3</li><li>4</li><li>5</li><li>6</li></ul>

虽然都实现了同一个接口,但是代理对象ActivityManagerProxy并不会对这些方法进行真正地实现,ActivityManagerProxy只是通过这种方式对方法的参数进行打包(因为都实现了相同接口,所以可以保证同一个方法有相同的参数,即对要传输给服务器的数据进行打包),真正实现的是ActivityManagerService。

但是这个地方并不是直接由客户端传递给服务器,而是通过Binder驱动进行中转。其实我对Binder驱动并不熟悉,我们就把他当做一个中转站就OK,客户端调用ActivityManagerProxy接口里面的方法,把数据传送给Binder驱动,然后Binder驱动就会把这些东西转发给服务器的ActivityManagerServices,由ActivityManagerServices去真正的实施具体的操作。

但是Binder只能传递数据,并不知道是要调用ActivityManagerServices的哪个方法,所以在数据中会添加方法的唯一标识码,比如前面的startActivity()方法:

<code class="hljs lasso has-numbering"><span class="hljs-keyword">public</span> int startActivity(IApplicationThread caller, <span class="hljs-built_in">String</span> callingPackage, Intent intent,
            <span class="hljs-built_in">String</span> resolvedType, IBinder resultTo, <span class="hljs-built_in">String</span> resultWho, int requestCode,
            int startFlags, ProfilerInfo profilerInfo, Bundle options) throws RemoteException {
        Parcel <span class="hljs-built_in">data</span> <span class="hljs-subst">=</span> Parcel<span class="hljs-built_in">.</span>obtain();
        Parcel reply <span class="hljs-subst">=</span> Parcel<span class="hljs-built_in">.</span>obtain();

        <span class="hljs-attribute">...</span>ignore some code<span class="hljs-attribute">...</span>

        mRemote<span class="hljs-built_in">.</span>transact(START_ACTIVITY_TRANSACTION, <span class="hljs-built_in">data</span>, reply, <span class="hljs-number">0</span>);
        reply<span class="hljs-built_in">.</span>readException();
        int result <span class="hljs-subst">=</span> reply<span class="hljs-built_in">.</span>readInt();
        reply<span class="hljs-built_in">.</span>recycle();
        <span class="hljs-built_in">data</span><span class="hljs-built_in">.</span>recycle();
        <span class="hljs-keyword">return</span> result;
    }</code><ul style="display: block;" class="pre-numbering"><li>1</li><li>2</li><li>3</li><li>4</li><li>5</li><li>6</li><li>7</li><li>8</li><li>9</li><li>10</li><li>11</li><li>12</li><li>13</li><li>14</li><li>15</li></ul>

上面的START_ACTIVITY_TRANSACTION就是方法标示,data是要传输给Binder驱动的数据,reply则接受操作的返回值。

客户端:ActivityManagerProxy =====>Binder驱动=====> ActivityManagerService:服务器

而且由于继承了同样的公共接口类,ActivityManagerProxy提供了与ActivityManagerService一样的函数原型,使用户感觉不出Server是运行在本地还是远端,从而可以更加方便的调用这些重要的系统服务。

但是!这里Binder通信是单方向的,即从ActivityManagerProxy指向ActivityManagerService的,如果AMS想要通知ActivityThread做一些事情,应该咋办呢?

还是通过Binder通信,不过是换了另外一对,换成了ApplicationThread和ApplicationThreadProxy。

客户端:ApplicationThread <=====Binder驱动<===== ApplicationThreadProxy:服务器

他们也都实现了相同的接口IApplicationThread

<code class="hljs php has-numbering">  <span class="hljs-keyword">private</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">ApplicationThread</span> <span class="hljs-keyword">extends</span> <span class="hljs-title">ApplicationThreadNative</span> {</span>}

  <span class="hljs-keyword">public</span> <span class="hljs-keyword">abstract</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">ApplicationThreadNative</span> <span class="hljs-keyword">extends</span> <span class="hljs-title">Binder</span> <span class="hljs-keyword">implements</span> <span class="hljs-title">IApplicationThread</span>{</span>}

  <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">ApplicationThreadProxy</span> <span class="hljs-keyword">implements</span> <span class="hljs-title">IApplicationThread</span> {</span>}</code><ul style="display: block;" class="pre-numbering"><li>1</li><li>2</li><li>3</li><li>4</li><li>5</li></ul>

剩下的就不必多说了吧,和前面一样。

AMS接收到客户端的请求之后,会如何开启一个Activity?

OK,至此,点击桌面图标调用startActivity(),终于把数据和要开启Activity的请求发送到了AMS了。说了这么多,其实这些都在一瞬间完成了,下面咱们研究下AMS到底做了什么。

注:前方有高能的方法调用链,如果你现在累了,请先喝杯咖啡或者是上趟厕所休息下

AMS收到startActivity的请求之后,会按照如下的方法链进行调用

调用startActivity()

<code class="hljs java has-numbering"><span class="hljs-annotation">@Override</span>
    <span class="hljs-keyword">public</span> <span class="hljs-keyword">final</span> <span class="hljs-keyword">int</span> <span class="hljs-title">startActivity</span>(IApplicationThread caller, String callingPackage,
            Intent intent, String resolvedType, IBinder resultTo, String resultWho, <span class="hljs-keyword">int</span> requestCode,
            <span class="hljs-keyword">int</span> startFlags, ProfilerInfo profilerInfo, Bundle options) {
        <span class="hljs-keyword">return</span> startActivityAsUser(caller, callingPackage, intent, resolvedType, resultTo,
            resultWho, requestCode, startFlags, profilerInfo, options,
            UserHandle.getCallingUserId());
    }</code><ul style="display: block;" class="pre-numbering"><li>1</li><li>2</li><li>3</li><li>4</li><li>5</li><li>6</li><li>7</li><li>8</li></ul>

调用startActivityAsUser()

<code class="hljs java has-numbering"> <span class="hljs-annotation">@Override</span>
    <span class="hljs-keyword">public</span> <span class="hljs-keyword">final</span> <span class="hljs-keyword">int</span> <span class="hljs-title">startActivityAsUser</span>(IApplicationThread caller, String callingPackage,
            Intent intent, String resolvedType, IBinder resultTo, String resultWho, <span class="hljs-keyword">int</span> requestCode,
            <span class="hljs-keyword">int</span> startFlags, ProfilerInfo profilerInfo, Bundle options, <span class="hljs-keyword">int</span> userId) {

            ...ignore some code...

        <span class="hljs-keyword">return</span> mStackSupervisor.startActivityMayWait(caller, -<span class="hljs-number">1</span>, callingPackage, intent,
                resolvedType, <span class="hljs-keyword">null</span>, <span class="hljs-keyword">null</span>, resultTo, resultWho, requestCode, startFlags,
                profilerInfo, <span class="hljs-keyword">null</span>, <span class="hljs-keyword">null</span>, options, userId, <span class="hljs-keyword">null</span>, <span class="hljs-keyword">null</span>);
    }
</code><ul style="display: block;" class="pre-numbering"><li>1</li><li>2</li><li>3</li><li>4</li><li>5</li><li>6</li><li>7</li><li>8</li><li>9</li><li>10</li><li>11</li><li>12</li></ul>

在这里又出现了一个新对象ActivityStackSupervisor,通过这个类可以实现对ActivityStack的部分操作。

<code class="hljs vbscript has-numbering">  final <span class="hljs-built_in">int</span> startActivityMayWait(IApplicationThread caller, <span class="hljs-built_in">int</span> callingUid,
            <span class="hljs-built_in">String</span> callingPackage, Intent intent, <span class="hljs-built_in">String</span> resolvedType,
            IVoiceInteractionSession voiceSession, IVoiceInteractor voiceInteractor,
            IBinder resultTo, <span class="hljs-built_in">String</span> resultWho, <span class="hljs-built_in">int</span> requestCode, <span class="hljs-built_in">int</span> startFlags,
            ProfilerInfo profilerInfo, WaitResult outResult, Configuration config,
            Bundle options, <span class="hljs-built_in">int</span> userId, IActivityContainer iContainer, TaskRecord inTask) {

            ...ignore some code...

              <span class="hljs-built_in">int</span> res = startActivityLocked(caller, intent, resolvedType, aInfo,
                    voiceSession, voiceInteractor, resultTo, resultWho,
                    requestCode, callingPid, callingUid, callingPackage,
                    realCallingPid, realCallingUid, startFlags, options,
                    componentSpecified, <span class="hljs-literal">null</span>, container, inTask);

            ...ignore some code...

            }</code><ul style="display: block;" class="pre-numbering"><li>1</li><li>2</li><li>3</li><li>4</li><li>5</li><li>6</li><li>7</li><li>8</li><li>9</li><li>10</li><li>11</li><li>12</li><li>13</li><li>14</li><li>15</li><li>16</li><li>17</li><li>18</li></ul>

继续调用startActivityLocked()

<code class="hljs vbscript has-numbering"> final <span class="hljs-built_in">int</span> startActivityLocked(IApplicationThread caller,
            Intent intent, <span class="hljs-built_in">String</span> resolvedType, ActivityInfo aInfo,
            IVoiceInteractionSession voiceSession, IVoiceInteractor voiceInteractor,
            IBinder resultTo, <span class="hljs-built_in">String</span> resultWho, <span class="hljs-built_in">int</span> requestCode,
            <span class="hljs-built_in">int</span> callingPid, <span class="hljs-built_in">int</span> callingUid, <span class="hljs-built_in">String</span> callingPackage,
            <span class="hljs-built_in">int</span> realCallingPid, <span class="hljs-built_in">int</span> realCallingUid, <span class="hljs-built_in">int</span> startFlags, Bundle options,
            boolean componentSpecified, ActivityRecord[] outActivity, ActivityContainer container,
            TaskRecord inTask) {

              <span class="hljs-built_in">err</span> = startActivityUncheckedLocked(r, sourceRecord, voiceSession, voiceInteractor,
              startFlags, <span class="hljs-literal">true</span>, options, inTask);
        <span class="hljs-keyword">if</span> (<span class="hljs-built_in">err</span> < <span class="hljs-number">0</span>) {
            notifyActivityDrawnForKeyguard();
        }
        return <span class="hljs-built_in">err</span>;
    }</code><ul style="display: block;" class="pre-numbering"><li>1</li><li>2</li><li>3</li><li>4</li><li>5</li><li>6</li><li>7</li><li>8</li><li>9</li><li>10</li><li>11</li><li>12</li><li>13</li><li>14</li><li>15</li><li>16</li></ul>

调用startActivityUncheckedLocked(),此时要启动的Activity已经通过检验,被认为是一个正当的启动请求。

终于,在这里调用到了ActivityStack的startActivityLocked(ActivityRecord r, boolean newTask,boolean doResume, boolean keepCurTransition, Bundle options)。

ActivityRecord代表的就是要开启的Activity对象,里面分装了很多信息,比如所在的ActivityTask等,如果这是首次打开应用,那么这个Activity会被放到ActivityTask的栈顶,

<code class="hljs bash has-numbering">final int startActivityUncheckedLocked(ActivityRecord r, ActivityRecord <span class="hljs-built_in">source</span>Record,
            IVoiceInteractionSession voiceSession, IVoiceInteractor voiceInteractor, int startFlags,
            boolean <span class="hljs-keyword">do</span>Resume, Bundle options, TaskRecord <span class="hljs-keyword">in</span>Task) {

            ...ignore some code...

            targetStack.startActivityLocked(r, newTask, <span class="hljs-keyword">do</span>Resume, keepCurTransition, options);

            ...ignore some code...

             <span class="hljs-keyword">return</span> ActivityManager.START_SUCCESS;
            }</code><ul style="display: block;" class="pre-numbering"><li>1</li><li>2</li><li>3</li><li>4</li><li>5</li><li>6</li><li>7</li><li>8</li><li>9</li><li>10</li><li>11</li><li>12</li></ul>

调用的是ActivityStack.startActivityLocked()

<code class="hljs rust has-numbering"> final void startActivityLocked(ActivityRecord r, boolean newTask,
            boolean doResume, boolean keepCurTransition, Bundle options) {

        <span class="hljs-comment">//ActivityRecord中存储的TaskRecord信息</span>
        TaskRecord rTask = r.<span class="hljs-keyword">task</span>;

         ...ignore some code...

        <span class="hljs-comment">//如果不是在新的ActivityTask(也就是TaskRecord)中的话,就找出要运行在的TaskRecord对象</span>
     TaskRecord <span class="hljs-keyword">task</span> = null;
        <span class="hljs-keyword">if</span> (!newTask) {
            boolean startIt = <span class="hljs-keyword">true</span>;
            <span class="hljs-keyword">for</span> (<span class="hljs-keyword">int</span> taskNdx = mTaskHistory.size() - <span class="hljs-number">1</span>; taskNdx >= <span class="hljs-number">0</span>; --taskNdx) {
                <span class="hljs-keyword">task</span> = mTaskHistory.get(taskNdx);
                <span class="hljs-keyword">if</span> (<span class="hljs-keyword">task</span>.getTopActivity() == null) {
                    <span class="hljs-comment">// task中的所有Activity都结束了</span>
                    continue;
                }
                <span class="hljs-keyword">if</span> (<span class="hljs-keyword">task</span> == r.<span class="hljs-keyword">task</span>) {
                    <span class="hljs-comment">// 找到了</span>
                    <span class="hljs-keyword">if</span> (!startIt) {
                        <span class="hljs-keyword">task</span>.addActivityToTop(r);
                        r.putInHistory();
                        mWindowManager.addAppToken(<span class="hljs-keyword">task</span>.mActivities.indexOf(r), r.appToken,
                                r.<span class="hljs-keyword">task</span>.taskId, mStackId, r.info.screenOrientation, r.fullscreen,
                                (r.info.flags & ActivityInfo.FLAG_SHOW_ON_LOCK_SCREEN) != <span class="hljs-number">0</span>,
                                r.userId, r.info.configChanges, <span class="hljs-keyword">task</span>.voiceSession != null,
                                r.mLaunchTaskBehind);
                        <span class="hljs-keyword">if</span> (VALIDATE_TOKENS) {
                            validateAppTokensLocked();
                        }
                        ActivityOptions.abort(options);
                        <span class="hljs-keyword">return</span>;
                    }
                    <span class="hljs-keyword">break</span>;
                } <span class="hljs-keyword">else</span> <span class="hljs-keyword">if</span> (<span class="hljs-keyword">task</span>.numFullscreen > <span class="hljs-number">0</span>) {
                    startIt = <span class="hljs-keyword">false</span>;
                }
            }
        }

      ...ignore some code...

        <span class="hljs-comment">// Place a new activity at top of stack, so it is next to interact</span>
        <span class="hljs-comment">// with the user.</span>
        <span class="hljs-keyword">task</span> = r.<span class="hljs-keyword">task</span>;
        <span class="hljs-keyword">task</span>.addActivityToTop(r);
        <span class="hljs-keyword">task</span>.setFrontOfTask();

        ...ignore some code...

         <span class="hljs-keyword">if</span> (doResume) {
            mStackSupervisor.resumeTopActivitiesLocked(this, r, options);
        }
    }</code><ul style="display: block;" class="pre-numbering"><li>1</li><li>2</li><li>3</li><li>4</li><li>5</li><li>6</li><li>7</li><li>8</li><li>9</li><li>10</li><li>11</li><li>12</li><li>13</li><li>14</li><li>15</li><li>16</li><li>17</li><li>18</li><li>19</li><li>20</li><li>21</li><li>22</li><li>23</li><li>24</li><li>25</li><li>26</li><li>27</li><li>28</li><li>29</li><li>30</li><li>31</li><li>32</li><li>33</li><li>34</li><li>35</li><li>36</li><li>37</li><li>38</li><li>39</li><li>40</li><li>41</li><li>42</li><li>43</li><li>44</li><li>45</li><li>46</li><li>47</li><li>48</li><li>49</li><li>50</li><li>51</li><li>52</li><li>53</li><li>54</li><li>55</li></ul>

靠!这来回折腾什么呢!从ActivityStackSupervisor到ActivityStack,又调回ActivityStackSupervisor,这到底是在折腾什么玩意啊!!!

淡定…淡定…我知道你也在心里骂娘,世界如此美妙,你却如此暴躁,这样不好,不好…

来来来,咱们继续哈,刚才说到哪里了?哦,对,咱们一起看下StackSupervisor.resumeTopActivitiesLocked(this, r, options)

<code class="hljs java has-numbering"> <span class="hljs-keyword">boolean</span> resumeTopActivitiesLocked(ActivityStack targetStack, ActivityRecord target,
            Bundle targetOptions) {
        <span class="hljs-keyword">if</span> (targetStack == <span class="hljs-keyword">null</span>) {
            targetStack = getFocusedStack();
        }
        <span class="hljs-comment">// Do targetStack first.</span>
        <span class="hljs-keyword">boolean</span> result = <span class="hljs-keyword">false</span>;
        <span class="hljs-keyword">if</span> (isFrontStack(targetStack)) {
            result = targetStack.resumeTopActivityLocked(target, targetOptions);
        }

          ...ignore some code...

        <span class="hljs-keyword">return</span> result;
    }</code><ul style="display: block;" class="pre-numbering"><li>1</li><li>2</li><li>3</li><li>4</li><li>5</li><li>6</li><li>7</li><li>8</li><li>9</li><li>10</li><li>11</li><li>12</li><li>13</li><li>14</li><li>15</li></ul>

我…已无力吐槽了,又调回ActivityStack去了…

ActivityStack.resumeTopActivityLocked()

<code class="hljs java has-numbering"><span class="hljs-keyword">final</span> <span class="hljs-keyword">boolean</span> resumeTopActivityLocked(ActivityRecord prev, Bundle options) {
        <span class="hljs-keyword">if</span> (inResumeTopActivity) {
            <span class="hljs-comment">// Don't even start recursing.</span>
            <span class="hljs-keyword">return</span> <span class="hljs-keyword">false</span>;
        }

        <span class="hljs-keyword">boolean</span> result = <span class="hljs-keyword">false</span>;
        <span class="hljs-keyword">try</span> {
            <span class="hljs-comment">// Protect against recursion.</span>
            inResumeTopActivity = <span class="hljs-keyword">true</span>;
            result = resumeTopActivityInnerLocked(prev, options);
        } <span class="hljs-keyword">finally</span> {
            inResumeTopActivity = <span class="hljs-keyword">false</span>;
        }
        <span class="hljs-keyword">return</span> result;
    }</code><ul style="display: block;" class="pre-numbering"><li>1</li><li>2</li><li>3</li><li>4</li><li>5</li><li>6</li><li>7</li><li>8</li><li>9</li><li>10</li><li>11</li><li>12</li><li>13</li><li>14</li><li>15</li><li>16</li></ul>

咱们坚持住,看一下ActivityStack.resumeTopActivityInnerLocked()到底进行了什么操作

<code class="hljs lasso has-numbering">  final boolean resumeTopActivityInnerLocked(ActivityRecord prev, Bundle options) {

          <span class="hljs-attribute">...</span>ignore some code<span class="hljs-attribute">...</span>
      <span class="hljs-comment">//找出还没结束的首个ActivityRecord</span>
     ActivityRecord next <span class="hljs-subst">=</span> topRunningActivityLocked(<span class="hljs-built_in">null</span>);

    <span class="hljs-comment">//如果一个没结束的Activity都没有,就开启Launcher程序</span>
    <span class="hljs-keyword">if</span> (next <span class="hljs-subst">==</span> <span class="hljs-built_in">null</span>) {
            ActivityOptions<span class="hljs-built_in">.</span><span class="hljs-keyword">abort</span>(options);
            <span class="hljs-keyword">if</span> (DEBUG_STATES) Slog<span class="hljs-built_in">.</span>d(<span class="hljs-built_in">TAG</span>, <span class="hljs-string">"resumeTopActivityLocked: No more activities go home"</span>);
            <span class="hljs-keyword">if</span> (DEBUG_STACK) mStackSupervisor<span class="hljs-built_in">.</span>validateTopActivitiesLocked();
            <span class="hljs-comment">// Only resume home if on home display</span>
            final int returnTaskType <span class="hljs-subst">=</span> prevTask <span class="hljs-subst">==</span> <span class="hljs-built_in">null</span> <span class="hljs-subst">||</span> <span class="hljs-subst">!</span>prevTask<span class="hljs-built_in">.</span>isOverHomeStack() <span class="hljs-subst">?</span>
                    HOME_ACTIVITY_TYPE : prevTask<span class="hljs-built_in">.</span>getTaskToReturnTo();
            <span class="hljs-keyword">return</span> isOnHomeDisplay() <span class="hljs-subst">&&</span>
                    mStackSupervisor<span class="hljs-built_in">.</span>resumeHomeStackTask(returnTaskType, prev);
        }

        <span class="hljs-comment">//先需要暂停当前的Activity。因为我们是在Lancher中启动mainActivity,所以当前mResumedActivity!=null,调用startPausingLocked()使得Launcher进入Pausing状态</span>
          <span class="hljs-keyword">if</span> (mResumedActivity <span class="hljs-subst">!=</span> <span class="hljs-built_in">null</span>) {
            pausing <span class="hljs-subst">|=</span> startPausingLocked(userLeaving, <span class="hljs-literal">false</span>, <span class="hljs-literal">true</span>, dontWaitForPause);
            <span class="hljs-keyword">if</span> (DEBUG_STATES) Slog<span class="hljs-built_in">.</span>d(<span class="hljs-built_in">TAG</span>, <span class="hljs-string">"resumeTopActivityLocked: Pausing "</span> <span class="hljs-subst">+</span> mResumedActivity);
        }

  }</code><ul style="display: block;" class="pre-numbering"><li>1</li><li>2</li><li>3</li><li>4</li><li>5</li><li>6</li><li>7</li><li>8</li><li>9</li><li>10</li><li>11</li><li>12</li><li>13</li><li>14</li><li>15</li><li>16</li><li>17</li><li>18</li><li>19</li><li>20</li><li>21</li><li>22</li><li>23</li><li>24</li><li>25</li></ul>

在这个方法里,prev.app为记录启动Lancher进程的ProcessRecord,prev.app.thread为Lancher进程的远程调用接口IApplicationThead,所以可以调用prev.app.thread.schedulePauseActivity,到Lancher进程暂停指定Activity。

<code class="hljs java has-numbering"> <span class="hljs-keyword">final</span> <span class="hljs-keyword">boolean</span> startPausingLocked(<span class="hljs-keyword">boolean</span> userLeaving, <span class="hljs-keyword">boolean</span> uiSleeping, <span class="hljs-keyword">boolean</span> resuming,
            <span class="hljs-keyword">boolean</span> dontWait) {
        <span class="hljs-keyword">if</span> (mPausingActivity != <span class="hljs-keyword">null</span>) {
            completePauseLocked(<span class="hljs-keyword">false</span>);
        }

       ...ignore some code...    

        <span class="hljs-keyword">if</span> (prev.app != <span class="hljs-keyword">null</span> && prev.app.thread != <span class="hljs-keyword">null</span>) 
          <span class="hljs-keyword">try</span> {
                mService.updateUsageStats(prev, <span class="hljs-keyword">false</span>);
                prev.app.thread.schedulePauseActivity(prev.appToken, prev.finishing,
                        userLeaving, prev.configChangeFlags, dontWait);
            } <span class="hljs-keyword">catch</span> (Exception e) {
                mPausingActivity = <span class="hljs-keyword">null</span>;
                mLastPausedActivity = <span class="hljs-keyword">null</span>;
                mLastNoHistoryActivity = <span class="hljs-keyword">null</span>;
            }
        } <span class="hljs-keyword">else</span> {
            mPausingActivity = <span class="hljs-keyword">null</span>;
            mLastPausedActivity = <span class="hljs-keyword">null</span>;
            mLastNoHistoryActivity = <span class="hljs-keyword">null</span>;
        }

      ...ignore some code...  

 }</code><ul style="display: block;" class="pre-numbering"><li>1</li><li>2</li><li>3</li><li>4</li><li>5</li><li>6</li><li>7</li><li>8</li><li>9</li><li>10</li><li>11</li><li>12</li><li>13</li><li>14</li><li>15</li><li>16</li><li>17</li><li>18</li><li>19</li><li>20</li><li>21</li><li>22</li><li>23</li><li>24</li><li>25</li><li>26</li><li>27</li></ul>

在Lancher进程中消息传递,调用ActivityThread.handlePauseActivity(),最终调用ActivityThread.performPauseActivity()暂停指定Activity。接着通过前面所说的Binder通信,通知AMS已经完成暂停的操作。

<code class="hljs avrasm has-numbering">ActivityManagerNative<span class="hljs-preprocessor">.getDefault</span>()<span class="hljs-preprocessor">.activityPaused</span>(token).</code><ul style="display: block;" class="pre-numbering"><li>1</li></ul>

上面这些调用过程非常复杂,源码中各种条件判断让人眼花缭乱,所以说如果你没记住也没关系,你只要记住这个流程,理解了Android在控制Activity生命周期时是如何操作,以及是通过哪几个关键的类进行操作的就可以了,以后遇到相关的问题之道从哪块下手即可,这些过程我虽然也是撸了一遍,但还是记不清。

最后来一张高清无码大图,方便大家记忆:

请戳这里(图片3.3M,请用电脑观看)

送给你们的彩蛋

不要使用 startActivityForResult(intent,RESULT_OK)

这是因为startActivity()是这样实现的

<code class="hljs cs has-numbering"><span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title">startActivity</span>(Intent intent, @Nullable Bundle options) {
        <span class="hljs-keyword">if</span> (options != <span class="hljs-keyword">null</span>) {
            startActivityForResult(intent, -<span class="hljs-number">1</span>, options);
        } <span class="hljs-keyword">else</span> {
            <span class="hljs-comment">// Note we want to go through this call for compatibility with</span>
            <span class="hljs-comment">// applications that may have overridden the method.</span>
            startActivityForResult(intent, -<span class="hljs-number">1</span>);
        }
    }</code><ul style="display: block;" class="pre-numbering"><li>1</li><li>2</li><li>3</li><li>4</li><li>5</li><li>6</li><li>7</li><li>8</li><li>9</li></ul>

<code class="hljs java has-numbering"> <span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">final</span> <span class="hljs-keyword">int</span> RESULT_OK  = -<span class="hljs-number">1</span>;</code><ul style="display: block;" class="pre-numbering"><li>1</li></ul>

所以

<code class="hljs erlang has-numbering"><span class="hljs-function"><span class="hljs-title">startActivityForResult</span><span class="hljs-params">(intent,<span class="hljs-variable">RESULT_OK</span>)</span> = <span class="hljs-title">startActivity</span><span class="hljs-params">()</span></span></code><ul style="display: block;" class="pre-numbering"><li>1</li></ul>

你不可能从onActivityResult()里面收到任何回调。而这个问题是相当难以被发现的,就是因为这个坑,我工作一年多来第一次加班到9点 (ˇˍˇ)

一个App的程序入口到底是什么?

是ActivityThread.main()。

整个App的主线程的消息循环是在哪里创建的?

是在ActivityThread初始化的时候,就已经创建消息循环了,所以在主线程里面创建Handler不需要指定Looper,而如果在其他线程使用Handler,则需要单独使用Looper.prepare()和Looper.loop()创建消息循环。

<code class="hljs cs has-numbering"> <span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">void</span> <span class="hljs-title">main</span>(String[] args) {

          ...ignore some code...    

      Looper.prepareMainLooper();

        ActivityThread thread = <span class="hljs-keyword">new</span> ActivityThread();
        thread.attach(<span class="hljs-keyword">false</span>);

        <span class="hljs-keyword">if</span> (sMainThreadHandler == <span class="hljs-keyword">null</span>) {
            sMainThreadHandler = thread.getHandler();
        }

        AsyncTask.init();

        <span class="hljs-keyword">if</span> (<span class="hljs-keyword">false</span>) {
            Looper.myLooper().setMessageLogging(<span class="hljs-keyword">new</span>
                    LogPrinter(Log.DEBUG, <span class="hljs-string">"ActivityThread"</span>));
        }

        Looper.loop();

          ...ignore some code...    

 }</code><ul style="display: block;" class="pre-numbering"><li>1</li><li>2</li><li>3</li><li>4</li><li>5</li><li>6</li><li>7</li><li>8</li><li>9</li><li>10</li><li>11</li><li>12</li><li>13</li><li>14</li><li>15</li><li>16</li><li>17</li><li>18</li><li>19</li><li>20</li><li>21</li><li>22</li><li>23</li><li>24</li><li>25</li></ul>

Application是在什么时候创建的?onCreate()什么时候调用的?

也是在ActivityThread.main()的时候,再具体点呢,就是在thread.attach(false)的时候。

看你的表情,不信是吧!凯子哥带你溜溜~

我们先看一下ActivityThread.attach()

<code class="hljs java has-numbering"><span class="hljs-keyword">private</span> <span class="hljs-keyword">void</span> <span class="hljs-title">attach</span>(<span class="hljs-keyword">boolean</span> system) {
        sCurrentActivityThread = <span class="hljs-keyword">this</span>;
        mSystemThread = system;
        <span class="hljs-comment">//普通App进这里</span>
        <span class="hljs-keyword">if</span> (!system) {

            ...ignore some code...    

            RuntimeInit.setApplicationObject(mAppThread.asBinder());
            <span class="hljs-keyword">final</span> IActivityManager mgr = ActivityManagerNative.getDefault();
            <span class="hljs-keyword">try</span> {
                mgr.attachApplication(mAppThread);
            } <span class="hljs-keyword">catch</span> (RemoteException ex) {
                <span class="hljs-comment">// Ignore</span>
            }
           } <span class="hljs-keyword">else</span> {
             <span class="hljs-comment">//这个分支在SystemServer加载的时候会进入,通过调用</span>
             <span class="hljs-comment">// private void createSystemContext() {</span>
             <span class="hljs-comment">//    ActivityThread activityThread = ActivityThread.systemMain();</span>
             <span class="hljs-comment">//} </span>

             <span class="hljs-comment">// public static ActivityThread systemMain() {</span>
        <span class="hljs-comment">//        if (!ActivityManager.isHighEndGfx()) {</span>
        <span class="hljs-comment">//            HardwareRenderer.disable(true);</span>
        <span class="hljs-comment">//        } else {</span>
        <span class="hljs-comment">//            HardwareRenderer.enableForegroundTrimming();</span>
        <span class="hljs-comment">//        }</span>
        <span class="hljs-comment">//        ActivityThread thread = new ActivityThread();</span>
        <span class="hljs-comment">//        thread.attach(true);</span>
        <span class="hljs-comment">//        return thread;</span>
        <span class="hljs-comment">//    }       </span>
           }
    }</code><ul style="display: block;" class="pre-numbering"><li>1</li><li>2</li><li>3</li><li>4</li><li>5</li><li>6</li><li>7</li><li>8</li><li>9</li><li>10</li><li>11</li><li>12</li><li>13</li><li>14</li><li>15</li><li>16</li><li>17</li><li>18</li><li>19</li><li>20</li><li>21</li><li>22</li><li>23</li><li>24</li><li>25</li><li>26</li><li>27</li><li>28</li><li>29</li><li>30</li><li>31</li><li>32</li><li>33</li></ul>

这里需要关注的就是mgr.attachApplication(mAppThread),这个就会通过Binder调用到AMS里面对应的方法

<code class="hljs java has-numbering"><span class="hljs-annotation">@Override</span>
    <span class="hljs-keyword">public</span> <span class="hljs-keyword">final</span> <span class="hljs-keyword">void</span> <span class="hljs-title">attachApplication</span>(IApplicationThread thread) {
        <span class="hljs-keyword">synchronized</span> (<span class="hljs-keyword">this</span>) {
            <span class="hljs-keyword">int</span> callingPid = Binder.getCallingPid();
            <span class="hljs-keyword">final</span> <span class="hljs-keyword">long</span> origId = Binder.clearCallingIdentity();
            attachApplicationLocked(thread, callingPid);
            Binder.restoreCallingIdentity(origId);
        }
    }</code><ul style="display: block;" class="pre-numbering"><li>1</li><li>2</li><li>3</li><li>4</li><li>5</li><li>6</li><li>7</li><li>8</li><li>9</li></ul>

然后就是

<code class="hljs avrasm has-numbering"> private final boolean attachApplicationLocked(IApplicationThread thread,
            int pid) {


             thread<span class="hljs-preprocessor">.bindApplication</span>(processName, appInfo, providers, app<span class="hljs-preprocessor">.instrumentationClass</span>,
                    profilerInfo, app<span class="hljs-preprocessor">.instrumentationArguments</span>, app<span class="hljs-preprocessor">.instrumentationWatcher</span>,
                    app<span class="hljs-preprocessor">.instrumentationUiAutomationConnection</span>, testMode, enableOpenGlTrace,
                    isRestrictedBackupMode || !normalMode, app<span class="hljs-preprocessor">.persistent</span>,
                    new Configuration(mConfiguration), app<span class="hljs-preprocessor">.compat</span>, getCommonServicesLocked(),
                    mCoreSettingsObserver<span class="hljs-preprocessor">.getCoreSettingsLocked</span>())<span class="hljs-comment">;</span>


            }</code><ul style="display: block;" class="pre-numbering"><li>1</li><li>2</li><li>3</li><li>4</li><li>5</li><li>6</li><li>7</li><li>8</li><li>9</li><li>10</li><li>11</li><li>12</li><li>13</li></ul>

thread是IApplicationThread,实际上就是ApplicationThread在服务端的代理类ApplicationThreadProxy,然后又通过IPC就会调用到ApplicationThread的对应方法

<code class="hljs haskell has-numbering"><span class="hljs-title">private</span> <span class="hljs-keyword">class</span> <span class="hljs-type">ApplicationThread</span> extends <span class="hljs-type">ApplicationThreadNative</span> {

  public final void bindApplication(<span class="hljs-type">String</span> processName, <span class="hljs-type">ApplicationInfo</span> appInfo,
                <span class="hljs-type">List</span><<span class="hljs-type">ProviderInfo</span>> providers, <span class="hljs-type">ComponentName</span> instrumentationName,
                <span class="hljs-type">ProfilerInfo</span> profilerInfo, <span class="hljs-type">Bundle</span> instrumentationArgs,
                <span class="hljs-type">IInstrumentationWatcher</span> instrumentationWatcher,
                <span class="hljs-type">IUiAutomationConnection</span> instrumentationUiConnection, int debugMode,
                boolean enableOpenGlTrace, boolean isRestrictedBackupMode, boolean persistent,
                <span class="hljs-type">Configuration</span> config, <span class="hljs-type">CompatibilityInfo</span> compatInfo, <span class="hljs-type">Map</span><<span class="hljs-type">String</span>, <span class="hljs-type">IBinder</span>> services,
                <span class="hljs-type">Bundle</span> coreSettings) {

                 ...ignore some code...    

             <span class="hljs-type">AppBindData</span> <span class="hljs-typedef"><span class="hljs-keyword">data</span> = new <span class="hljs-type">AppBindData</span><span class="hljs-container">()</span>;</span>
            <span class="hljs-typedef"><span class="hljs-keyword">data</span>.processName = processName;</span>
            <span class="hljs-typedef"><span class="hljs-keyword">data</span>.appInfo = appInfo;</span>
            <span class="hljs-typedef"><span class="hljs-keyword">data</span>.providers = providers;</span>
            <span class="hljs-typedef"><span class="hljs-keyword">data</span>.instrumentationName = instrumentationName;</span>
            <span class="hljs-typedef"><span class="hljs-keyword">data</span>.instrumentationArgs = instrumentationArgs;</span>
            <span class="hljs-typedef"><span class="hljs-keyword">data</span>.instrumentationWatcher = instrumentationWatcher;</span>
            <span class="hljs-typedef"><span class="hljs-keyword">data</span>.instrumentationUiAutomationConnection = instrumentationUiConnection;</span>
            <span class="hljs-typedef"><span class="hljs-keyword">data</span>.debugMode = debugMode;</span>
            <span class="hljs-typedef"><span class="hljs-keyword">data</span>.enableOpenGlTrace = enableOpenGlTrace;</span>
            <span class="hljs-typedef"><span class="hljs-keyword">data</span>.restrictedBackupMode = isRestrictedBackupMode;</span>
            <span class="hljs-typedef"><span class="hljs-keyword">data</span>.persistent = persistent;</span>
            <span class="hljs-typedef"><span class="hljs-keyword">data</span>.config = config;</span>
            <span class="hljs-typedef"><span class="hljs-keyword">data</span>.compatInfo = compatInfo;</span>
            <span class="hljs-typedef"><span class="hljs-keyword">data</span>.initProfilerInfo = profilerInfo;</span>
            sendMessage(<span class="hljs-type">H</span>.<span class="hljs-type">BIND_APPLICATION</span>, <span class="hljs-typedef"><span class="hljs-keyword">data</span>);</span>

           }

}</code><ul style="display: block;" class="pre-numbering"><li>1</li><li>2</li><li>3</li><li>4</li><li>5</li><li>6</li><li>7</li><li>8</li><li>9</li><li>10</li><li>11</li><li>12</li><li>13</li><li>14</li><li>15</li><li>16</li><li>17</li><li>18</li><li>19</li><li>20</li><li>21</li><li>22</li><li>23</li><li>24</li><li>25</li><li>26</li><li>27</li><li>28</li><li>29</li><li>30</li><li>31</li><li>32</li><li>33</li></ul>

我们需要关注的其实就是最后的sendMessage(),里面有函数的编号H.BIND_APPLICATION,然后这个Messge会被H这个Handler处理

<code class="hljs java has-numbering"> <span class="hljs-keyword">private</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">H</span> <span class="hljs-keyword">extends</span> <span class="hljs-title">Handler</span> {</span>

      ...ignore some code... 

     <span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">final</span> <span class="hljs-keyword">int</span> BIND_APPLICATION        = <span class="hljs-number">110</span>;

    ...ignore some code... 

     <span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title">handleMessage</span>(Message msg) {
          <span class="hljs-keyword">switch</span> (msg.what) {
        ...ignore some code... 
         <span class="hljs-keyword">case</span> BIND_APPLICATION:
                    Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, <span class="hljs-string">"bindApplication"</span>);
                    AppBindData data = (AppBindData)msg.obj;
                    handleBindApplication(data);
                    Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
                    <span class="hljs-keyword">break</span>;
        ...ignore some code... 
        }
 }</code><ul style="display: block;" class="pre-numbering"><li>1</li><li>2</li><li>3</li><li>4</li><li>5</li><li>6</li><li>7</li><li>8</li><li>9</li><li>10</li><li>11</li><li>12</li><li>13</li><li>14</li><li>15</li><li>16</li><li>17</li><li>18</li><li>19</li><li>20</li></ul>

最后就在下面这个方法中,完成了实例化,拨那个企鹅通过mInstrumentation.callApplicationOnCreate实现了onCreate()的调用。

<code class="hljs cs has-numbering"> <span class="hljs-keyword">private</span> <span class="hljs-keyword">void</span> <span class="hljs-title">handleBindApplication</span>(AppBindData data) {

 <span class="hljs-keyword">try</span> {

           ...ignore some code... 

            Application app = data.info.makeApplication(data.restrictedBackupMode, <span class="hljs-keyword">null</span>);
            mInitialApplication = app;

           ...ignore some code... 

            <span class="hljs-keyword">try</span> {
                mInstrumentation.onCreate(data.instrumentationArgs);
            }
            <span class="hljs-keyword">catch</span> (Exception e) {
            }
            <span class="hljs-keyword">try</span> {
                mInstrumentation.callApplicationOnCreate(app);
            } <span class="hljs-keyword">catch</span> (Exception e) {            }
        } <span class="hljs-keyword">finally</span> {
            StrictMode.setThreadPolicy(savedPolicy);
        }
 }</code><ul style="display: block;" class="pre-numbering"><li>1</li><li>2</li><li>3</li><li>4</li><li>5</li><li>6</li><li>7</li><li>8</li><li>9</li><li>10</li><li>11</li><li>12</li><li>13</li><li>14</li><li>15</li><li>16</li><li>17</li><li>18</li><li>19</li><li>20</li><li>21</li><li>22</li><li>23</li></ul>

data.info是一个LoadeApk对象。
LoadeApk.data.info.makeApplication()

<code class="hljs java has-numbering"> <span class="hljs-keyword">public</span> Application <span class="hljs-title">makeApplication</span>(<span class="hljs-keyword">boolean</span> forceDefaultAppClass,
            Instrumentation instrumentation) {
        <span class="hljs-keyword">if</span> (mApplication != <span class="hljs-keyword">null</span>) {
            <span class="hljs-keyword">return</span> mApplication;
        }

        Application app = <span class="hljs-keyword">null</span>;

        String appClass = mApplicationInfo.className;
        <span class="hljs-keyword">if</span> (forceDefaultAppClass || (appClass == <span class="hljs-keyword">null</span>)) {
            appClass = <span class="hljs-string">"android.app.Application"</span>;
        }

        <span class="hljs-keyword">try</span> {
            java.lang.ClassLoader cl = getClassLoader();
            <span class="hljs-keyword">if</span> (!mPackageName.equals(<span class="hljs-string">"android"</span>)) {
                initializeJavaContextClassLoader();
            }
            ContextImpl appContext = ContextImpl.createAppContext(mActivityThread, <span class="hljs-keyword">this</span>);
            app = mActivityThread.mInstrumentation.newApplication(
                    cl, appClass, appContext);
            appContext.setOuterContext(app);
        } <span class="hljs-keyword">catch</span> (Exception e) {        }
        mActivityThread.mAllApplications.add(app);
        mApplication = app;

    <span class="hljs-comment">//传进来的是null,所以这里不会执行,onCreate在上一层执行</span>
        <span class="hljs-keyword">if</span> (instrumentation != <span class="hljs-keyword">null</span>) {
            <span class="hljs-keyword">try</span> {
                instrumentation.callApplicationOnCreate(app);
            } <span class="hljs-keyword">catch</span> (Exception e) {

            }
        }
        ...ignore some code... 

       }

        <span class="hljs-keyword">return</span> app;
    }</code><ul style="display: block;" class="pre-numbering"><li>1</li><li>2</li><li>3</li><li>4</li><li>5</li><li>6</li><li>7</li><li>8</li><li>9</li><li>10</li><li>11</li><li>12</li><li>13</li><li>14</li><li>15</li><li>16</li><li>17</li><li>18</li><li>19</li><li>20</li><li>21</li><li>22</li><li>23</li><li>24</li><li>25</li><li>26</li><li>27</li><li>28</li><li>29</li><li>30</li><li>31</li><li>32</li><li>33</li><li>34</li><li>35</li><li>36</li><li>37</li><li>38</li><li>39</li><li>40</li></ul>

所以最后还是通过Instrumentation.makeApplication()实例化的,这个老板娘真的很厉害呀!

<code class="hljs java has-numbering"><span class="hljs-keyword">static</span> <span class="hljs-keyword">public</span> Application <span class="hljs-title">newApplication</span>(Class<?> clazz, Context context)
            <span class="hljs-keyword">throws</span> InstantiationException, IllegalAccessException, 
            ClassNotFoundException {
        Application app = (Application)clazz.newInstance();
        app.attach(context);
        <span class="hljs-keyword">return</span> app;
    }</code><ul style="display: block;" class="pre-numbering"><li>1</li><li>2</li><li>3</li><li>4</li><li>5</li><li>6</li><li>7</li></ul>

而且通过反射拿到Application对象之后,直接调用attach(),所以attach()调用是在onCreate()之前的。

参考文章

下面的这些文章都是这方面比较精品的,希望你抽出时间研究,这可能需要花费很长时间,但是如果你想进阶为中高级开发者,这一步是必须的。

再次感谢下面这些文章的作者的分享精神。

Binder

zygote

ActivityThread、Instrumentation、AMS

Launcher

结语

OK,到这里,这篇文章算是告一段落了,我们再回头看看一开始的几个问题,你还困惑吗?

  • 一个App是怎么启动起来的?
  • App的程序入口到底是哪里?
  • Launcher到底是什么神奇的东西?
  • 听说还有个AMS的东西,它是做什么的?
  • Binder是什么?他是如何进行IPC通信的?
  • Activity生命周期到底是什么时候调用的?被谁调用的?

再回过头来看看这些类,你还迷惑吗?

  • ActivityManagerServices,简称AMS,服务端对象,负责系统中所有Activity的生命周期
  • ActivityThread,App的真正入口。当开启App之后,会调用main()开始运行,开启消息循环队列,这就是传说中的UI线程或者叫主线程。与ActivityManagerServices配合,一起完成Activity的管理工作
  • ApplicationThread,用来实现ActivityManagerService与ActivityThread之间的交互。在ActivityManagerService需要管理相关Application中的Activity的生命周期时,通过ApplicationThread的代理对象与ActivityThread通讯。
  • ApplicationThreadProxy,是ApplicationThread在服务器端的代理,负责和客户端的ApplicationThread通讯。AMS就是通过该代理与ActivityThread进行通信的。
  • Instrumentation,每一个应用程序只有一个Instrumentation对象,每个Activity内都有一个对该对象的引用。Instrumentation可以理解为应用进程的管家,ActivityThread要创建或暂停某个Activity时,都需要通过Instrumentation来进行具体的操作。
  • ActivityStack,Activity在AMS的栈管理,用来记录已经启动的Activity的先后关系,状态信息等。通过ActivityStack决定是否需要启动新的进程。
  • ActivityRecord,ActivityStack的管理对象,每个Activity在AMS对应一个ActivityRecord,来记录Activity的状态以及其他的管理信息。其实就是服务器端的Activity对象的映像。
  • TaskRecord,AMS抽象出来的一个“任务”的概念,是记录ActivityRecord的栈,一个“Task”包含若干个ActivityRecord。AMS用TaskRecord确保Activity启动和退出的顺序。如果你清楚Activity的4种launchMode,那么对这个概念应该不陌生。

如果你还感到迷惑的话,就把这篇文章多读几遍吧,信息量可能比较多,需要慢慢消化~


  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值