Android中的消息机制——Looper、Handler、MessageQueue与Message

本篇提纲.png

一.Looper、Handler、MessageQueue与Message的关系与相关概念

1.什么是Android消息处理机制?

“消息”是windows运行机制中一个基本而又重要的概念。消息是一个报告事件发生的通知消息驱动是围绕消息的产生与处理展开的,并依靠消息循环机制来实现(百度百科)。与Windows系统一样,Android也是消息驱动型的系统。引用一下消息驱动机制的四要素:
  ①接收消息的“消息队列”
  ②阻塞式地从消息队列中接收消息并进行处理的“线程”
  ③可发送的“消息的格式”
  ④“消息发送函数”
与之对应,Android系统中对应实现了:
  ①接收消息的“消息队列” —— MessageQueue
  ②阻塞式地从消息队列中接收消息并进行处理的“线程” —— Thread+Looper
  ③可发送的“消息的格式” —— Message
  ④“消息发送函数”—— Handler的post()和sendMessage()
  Android有大量的消息驱动方式来进行交互,比如Android的四大组件——Activity, Service, Broadcast, ContentProvider的启动过程的交互,都离不开消息机制。

2.Handler

Android的消息机制,很多时候我们也称之为“Handler机制”,可见Handler这个东西是相当重要了~~那么Handler是用来干什么的呢?刚刚笔者在刷知乎的时候看到有大神怼(教导)一个Android菜鸟的时候其中就提到了一点“...以为Handler就是用来更新UI的...”——好吧,真是垂死病中惊坐起——开始学Android那会笔者也曾经这么认为(捂脸)。
  如果你也这么认为,那么从今天起你就要放弃这种狭隘的想法——Handler是Android消息机制的上层接口,因此我们在开发中与Handler打交道的机会最多。Handler并不是专门用来更新UI的,只是开发者常常用它来更新UI。Handler的主要用于同一个进程间的线程通信,Handler用于更新UI的时候是"子线程与主线程通信";当然,Handler也可以用于子线程之间通信。
  Handler的消息机制主要是就指“Handler的运行机制”,Handler的运行机制时需要底层的MessageQueue和Looper支持的。

3.MessageQueue

MessageQueue翻译过来是"消息队列"的意思,实际上它内部的数据结构不是队列,而是单向链表;MessageQueue中储存了大量的消息,由于一个线程同一时间只能处理一条消息,所以我们建了一个链表,将我们需要处理的消息按顺序储存起来,然后一项一项的交给需要的线程处理,这就是MessageQueue存在的价值。
  这里笔者想到一个问题——为什么MessageQueue要用链表而不用数组来作为数据结构呢?再经过网上查找博客+源码阅读,笔者认为是这样的——之前我们在一片文章中有讲过,单链表更适合做增删的操作,数组更适合做随机访问的操作。再MessageQueue中,我们不止要做随机访问(这里不是真的随机访问,消息的读取是根据计算出来的时间顺序来的,后文会讲)跟多的我们做的是插入删除操作,MessageQueue.enqueueMessage()就是插入消息(消息的插入需要根据时间发送的时间顺序来确定,不存在头插还是尾插),而插入消息;而MessageQueue.next()则是读取消息,且读取的同时也伴随这删除的过程。试想一下,一个消息队列要循环起来,必然要频繁的进行插入/读取操作,假如采用数组的话,这两个操作的平均时间复杂度都是O(N/2),而链表为O(1),显然链表更合适。

4.Looper

Looper和MessageQueue的消息就像水泵和井(里边装的是水)的关系一样,我们有了消息(水),但是为了把水从井中抽取出来(循环起来),我们得有一个水泵作为动力,这个动力就是Looper。
  如果我们在一个线程中调用Looper.prepare()...Looper.loop(),那么你的线程就成功升级为了一个Looper线程,这意味着你的线程有了一个消息泵(Looper)和一个消息队列(MessageQueue),此时你就可以调用Handler来进行线程间的通信了。
  我们应用的UI线程也就是主线程,在应用启动的时候,系统会自动初始化一个Looper,也就是说,我们的UI线程默认是Looper线程。这也就是为什么主线程中直接调用Handler没什么事,但是再子线程中创建Handler需要哦手动调用Looper.prepare()...Looper.loop()的和原因。

5.Message

Message也就是消息,井中的水。一个Message包括了消息类型(what),消息内容(arg1,arg2),发送它的Handler(target),Runnable回调接口等:

    public int what;        //数据类型
    public int arg1;        //简单的整数值
    public int arg2;        //简单的整数值可以直接发送,是一种替代setData(Bundle)的低成本方案,更加省资源
    public Object obj;
    ......
    /*package*/ int flags;
    /*package*/ long when;          //Handler发送一个消息之后,返回此消息的目标交付时间(以毫秒为单位)。
    /*package*/ Bundle data;        //Bundle可以携带更复杂的数据类型
    /*package*/ Handler target;     //哪个Handler发送的消息
    /*package*/ Runnable callback;

    //可以看到,Message带了一个指向一下个节点的链,也就是说,MessageQueue内部维护的实际上是一个链表
    /*package*/ Message next;

    private static final Object sPoolSync = new Object();
    private static Message sPool;       //消息池
    private static int sPoolSize = 0;

    private static final int MAX_POOL_SIZE = 50;    //消息池的最大容量

讲到这里,我们先上一张图加深一下大家对于这几个东西的直观认识:

Handler循环机制.png

二.子线程与主线程Handler通信原理(子线程是如何通过Handler更新UI的)

1.一些熟悉的场景
  private Handler handler = new Handler(){
       @Override
       public void handleMessage(Message msg) {
           super.handleMessage(msg);
           switch (msg.what) {      //判断标志位
               case 1:
                   //更新UI操作
                   break;
           }
        }
      };

       public class MyThread extends Thread {
         @Override
            public void run() {
              super.run();
              //耗时操作
            }
          Message msg =Message.obtain();//从全局池中返回一个message实例,避免多次创建(如new Message)
          msg.obj = data;
          msg.what=1;   //标志消息的标志
          handler.sendMessage(msg);
       }

       new MyThread().start();

上面代码是我们再Android开发中经常写的一段代码,其主要作用是再子线程中进行耗时操作,并通过Handler向主线程中发送消息,通知主线程做出相应的UI变化。注意这段代码中,Handler是再主线程中创建的,因此不需要手动调用Looper.prepare()添加Looper。
  如果我们试图再子线程中创建一个Handler,如:

      Handler handler ;
      public class MyThread extends Thread {
         @Override
            public void run() {
              super.run();
              //耗时操作
            }

          handler = new Handler();
          Message msg =Message.obtain();//从全局池中返回一个message实例,避免多次创建(如new Message)
          msg.obj = data;
          msg.what=1;   //标志消息的标志
          handler.sendMessage(msg);
       }

       new MyThread().start();

那么很显然,这个时候就会出bug了~~为了解决这个bug,我们需要手动再子线程中创建Looper:


      handler = new Handler();
      Looper.prepare();
      Message msg =Message.obtain();//从全局池中返回一个message实例,避免多次创建(如new Message)
      msg.obj = data;
      msg.what=1;   //标志消息的标志
      handler.sendMessage(msg);
      Looper.loop();

为什么是这样呢?接下来从源码的解读分析

2.Looper

(frameworks/base/core/java/android/os/Looper)

(1)Looper.perpare()
public final class Looper {
    private static final String TAG = "Looper";

    // sThreadLocal.get() will return null unless you've called prepare().
    static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();
    private static Looper sMainLooper;  // guarded by Looper.class

    final MessageQueue mQueue;
    final Thread mThread;

    public static void prepare() {
        prepare(true);
    }

    private static void prepare(boolean quitAllowed) {
        if (sThreadLocal.get() != null) {
            throw new RuntimeException("Only one Looper may be created per thread");
        }
        sThreadLocal.set(new Looper(quitAllowed));
    }

    ......
    private Looper(boolean quitAllowed) {
        mQueue = new MessageQueue(quitAllowed);
        mThread = Thread.currentThread();
    }

}

首先我们看到Looper.prepare()方法中调用了prepare(ture)方法,其中true表示,该Looper是可以被终止的。因为我们是在子线程中创建的Looper,当子线程的消息处理完之后,理应把改Looper终止掉(MessageQueue.quit)。但是在主线程的Looper中:

    public static void prepareMainLooper() {
        prepare(false);
        ......
    }

prepareMainLooper就是给主线程添加Looper,可以看到,主线程中的prepare(false)中的参数false表示的是,主线程中的Looper不能被终止掉,毕竟它是整个应用的生命,需要时刻准备着处理或者正在处理应用中的各种消息。
  好了我们接着看上面的prepare()中干了什么。这里出现了一个新的重量级的东西:sThreadLocal,它是ThreadLocal类的实例,关于ThreadLocal类是干什么的,我们在这里不做过多解释,我们只需要知道他是用于储存不同线程唯一对象的一个东西,即多个线程在ThreadLocal类中,通过ThreadLocal.set()保存了自己的变量之后,那么我们再各个子线程中调用ThreadLocal.get()方法,得到的仍然是当前线程之前存进去的那个值。各个线程存取各自的值,不会产生冲突。
  知道了ThreadLocal的作用之后,我们在来看sThreadLocal.set(new Looper(quitAllowed));这句表示我们再ThreadLocal类中保存了一个Looper对象(new Looper()),根据上面对ThreadLocal类的介绍,如果我们再当前线程中调用ThreadLocal.get()方法,则会得到本线程之前保存的唯一的变量。因此:

    if (sThreadLocal.get() != null) {
        throw new RuntimeException("Only one Looper may be created per thread");
    }

这个if表示的是,如果sThreadLocal.get() != null,说明当前线程中已经存在一个Looper,我们不能在一个线程中同时创建多个,所以此时会抛出异常。为了避免这种异常,我们可以在Looper.prepare()之前调用Looper.myLooper()类来返回当前线程中的Looper对象,判断为空之后,再调用prepare():

    public static @Nullable Looper myLooper() {
        return sThreadLocal.get();
    }

Ok,比比了这么多,我们接着看new Looper()中Looper中的构造函数中做了什么:

    private Looper(boolean quitAllowed) {
        mQueue = new MessageQueue(quitAllowed);
        mThread = Thread.currentThread();
    }

这里出现了消息队列MessageQueue(quitAllowed),其中参数就是表示是否允许Looper退出的标示符。可以看到,在Looper中我们创建了一个MessageQueue实例:

    MessageQueue(boolean quitAllowed) {
        mQuitAllowed = quitAllowed;
        mPtr = nativeInit();
    }
```
,此时我们的Looper就拥有了MessageQueue的对象引用。

###### (2)Looper.Loop()
  在Looper.perpare()调用完即我们为线程准备好Looper之后,最后一步我们还需要调用Loop()让整个Looper循环起来,这样消息才能发送出去:
```
    public static void loop() {
        final Looper me = myLooper();
        if (me == null) {
            throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");
        }
        final MessageQueue queue = me.mQueue;
        ......
        for (;;) {
            Message msg = queue.next(); // might block
            if (msg == null) {
                return;
            }
            ......
            msg.target.dispatchMessage(msg);
            ......
            msg.recycleUnchecked();     //释放占据的资源
        }
    }
```
首先调用了我们刚说过的myLooper()来获取当前线程再ThreadLcoal类中储存的Looper,如果Looper为空则会抛出异常,提示当前线程没有Looper。然后`inal MessageQueue queue = me.mQueue;`是获取当前线程的Looper中的MessageQueue对象。
  之后这个Looper便进入了一个无限循环的状态——for(;;),`Message msg = queue.next();`是一条一条遍历整个消息队列,拿出msg消息。
  而`msg.target.dispatchMessage(msg);`这句中的**msg.target**实际上就是当前消息的目标Handler,也就是**哪个线程中的Handler发送的消息,当然,这个发送它的Handler也要在自己所在的线程中接受这条消息。**
  `msg.recycleUnchecked();`这句是将这天消息放入Message类中的消息池中。
从上面我们基本上可以得出——**Looper > MessageQueue > Message**的关系,也就是说,**每个Looper中维护了一个消息队列,而一个消息队列中则以链表的形式排列着一条条消息。**


##### 3.MessageQueue.next()
(frameworks/base/core/java/android/os/MessageQueue)
上面的Looper类中我们实例化了一个MessageQueue对象,并调用了MessageQueue.next()类方法:

```
    Message next() {
        final long ptr = mPtr;      //当消息循环已经退出,则直接返回
        if (ptr == 0) {
            return null;
        }

        int pendingIdleHandlerCount = -1; // -1 only during first iteration 循环迭代的首次为-1,也就是初始值为-1
        int nextPollTimeoutMillis = 0;
        for (;;) {      //消息队列开始无限循环
            if (nextPollTimeoutMillis != 0) {
                Binder.flushPendingCommands();
            }

            //阻塞操作的方法,当等待nextPollTimeoutMillis时长,或者消息队列里边有了消息被唤醒时,
            //只有满足这两个条件该方法才会返回,for循环才能往下执行下面的代码,否则就一直在这等着
            nativePollOnce(ptr, nextPollTimeoutMillis);

            synchronized (this) {   //这一段同步块代码中就是再检索下一条message,如果找到了就返回
                final long now = SystemClock.uptimeMillis();    //手机开机到现在的时间(毫秒为单位),手机睡眠的时间不包括再内
                Message prevMsg = null;
                Message msg = mMessages;
                if (msg != null && msg.target == null) {
                    do {            //如果此时消息不为null,但是这个消息找不到发送它的Handler,说明为不合法消息,放弃并寻找下一条异步消息
                        prevMsg = msg;
                        msg = msg.next;
                    } while (msg != null && !msg.isAsynchronous());
                }
                if (msg != null) {  //如果消息不为null(这次是个正常消息)
                    if (now < msg.when) {
                        // 虽然消息是正常的,但是还没到发送的时间。msg.when表示消息发送的时间,因为我们可能调用了postDelay()
                        //延迟发送。我们之前说过,当等待nextPollTimeoutMillis时长后nativePollOnce()方法就会返回。
                        nextPollTimeoutMillis = (int) Math.min(msg.when - now, Integer.MAX_VALUE);
                    } else {    //else则表示,成功抓到了一条消息
                        mBlocked = false;
                        if (prevMsg != null) {      //单链表操作,下面讲
                            prevMsg.next = msg.next;
                        } else {
                            mMessages = msg.next;
                        }
                        msg.next = null;
                        msg.markInUse();
                        return msg;
                    }
                } else {
                    nextPollTimeoutMillis = -1;
                }
                ......
            }
            ......
        }
    }
```
首先:
```
    final long ptr = mPtr;
        if (ptr == 0) {
            return null;
        }
```
这个mPtr我们之前在MessageQueue的构造方法中提到过:
```
    MessageQueue(boolean quitAllowed) {
        mQuitAllowed = quitAllowed;
        mPtr = nativeInit();
    }
```
它通过` mPtr = nativeInit();`得到值,`nativeInit()`是一个native层的方法,根据注释来看,它主要是判断Looper目前的状态。`if (ptr == 0) {`表示的是当前线程的Looper已经退出或者被处理掉了,这种情况发生在“应用在退出之后试图重启Looper”的情况下,这种情况是不被允许的,因此此时`return null;`
  上面这段中还要讲的就是单链表的操作:
```
    if (prevMsg != null) {
            prevMsg.next = msg.next;
        } else {
            mMessages = msg.next;
        }
        msg.next = null;
        msg.markInUse();    //表示这个消息已经被传递(使用了)
        return msg;
    }
```
首先,再synchronized (this)同步块一开始的时候,有一个**全局变量mMessages,这实际上就是等待处理消息**,这个变量很重要,之后我们会多次遇到。这里还有一个叫prevMsg的Message,用于保存找不到msg.target的消息(废消息)。OK,回到链表操作中来,`if (prevMsg != null)`,很遗憾,有一个不合法的消息,此时上面已经经历了`prevMsg = msg; msg = msg.next;`这两步,加上`prevMsg.next = msg.next;`这一步,实际上就是prevMsg的后继引用跳过了msg,直接指向了msg的下一位(注意prevMsg是不合法的,但是它的下一位msg是合法的)。
  再看else,说明没有不合法的消息,`Message msg = mMessages;`加上`mMessages = msg.next;`,这两步实际上和上面一样,也是工作指针后移,越过了msg。**也就是msg出队,msg的下一条消息成为mMessages(待处理消息),否则就进入一个阻塞状态,一直等到有新的消息入队。**
  OK,上面if和else中越过的msg,就是我们要返回的正常消息,接着两句:`msg.next = null;`和`return msg;`就可以知道,前者是将msg的后继引用清空(将它从链表中删除),然后return。

  通过上面的分析我们知道,**MessageQueue.next()作用就是遍历链表,找出一个合法的msg,将它从链表中删除后返回**,这实际上也就是一个**消息出队**的过程

##### 4.Handler
###### (1)Handler.post/sendMessage将一个消息添加到消息队列中
  上面我们已经讲完了Handler通信的两个重要的基础类——**MessageQueue**和**Looper**,接下来我们分析一下Handler是如何将一个消息发送出去的。
  我们从Handler的构造函数开始看起:
```
    public Handler() {
        this(null, false);
    }
    ......
    public Handler(Callback callback, boolean async) {
        ......

        mLooper = Looper.myLooper();
        if (mLooper == null) {
            throw new RuntimeException(
                "Can't create handler inside thread that has not called Looper.prepare()");
        }
        mQueue = mLooper.mQueue;
        mCallback = callback;
        mAsynchronous = async;
    }
```
可以看到,Hnadler在发送消息的时候先获取当前线程的Looper,然后做一次looper的非空判断;接着获取了Looper中的MessageQueue对象。这样,我们的Handler已经和Looer以及MessageQueue取得了联系。
  接着回到文章最开始的时候举的那个例子中,handler.sendMessage()方法:
```
    public final boolean sendMessage(Message msg){
        return sendMessageDelayed(msg, 0);
    }

    public final boolean sendMessageDelayed(Message msg, long delayMillis){
        if (delayMillis < 0) {
            delayMillis = 0;
        }
        return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);
    }

    public boolean sendMessageAtTime(Message msg, long uptimeMillis) {
        MessageQueue queue = mQueue;
        if (queue == null) {
            RuntimeException e = new RuntimeException(
                    this + " sendMessageAtTime() called with no mQueue");
            return false;
        }
        return enqueueMessage(queue, msg, uptimeMillis);
    }

    private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
        msg.target = this;
        if (mAsynchronous) {
            msg.setAsynchronous(true);
        }
        return queue.enqueueMessage(msg, uptimeMillis);
    }
```
上面几个方法的逻辑都比较清晰,可以看到,最终调用了MessageQueue的enqueueMessage()方法,其中第一个参数为msg,第二个参数**uptimeMillis = SystemClock.uptimeMillis() + delayMillis**,也就是**开机到现在的时间(不包括睡眠时间)+我们设定的delay时间**,接下来我们看看enqueueMessage()方法:
```
    boolean enqueueMessage(Message msg, long when) {
        if (msg.target == null) {   //目标Handler为null
            throw new IllegalArgumentException("Message must have a target.");
        }
        if (msg.isInUse()) {    //msg已经用过了  
            throw new IllegalStateException(msg + " This message is already in use.");
        }

        synchronized (this) {
            if (mQuitting) {        //如果这个Looper正在退出,回收msg,加入到消息池
                IllegalStateException e = new IllegalStateException(
                        msg.target + " sending message to a Handler on a dead thread");
                msg.recycle();      //释放msg资源
                return false;
            }

            msg.markInUse();        //表示这个msg已经被使用了
            //这里我们可以知道,Message类的when属性实际上就是SystemClock.uptimeMillis() + delayMillis
            msg.when = when;
            Message p = mMessages;
            boolean needWake;
            if (p == null || when == 0 || when < p.when) {
            //如果p == null,说明MessageQueue中没有消息;或者msg的时间是这个消息队列中最靠前的
                msg.next = p;       //将这个msg提取出来,并复制给mMessages,mMessages会在上面的
                mMessages = msg;    //MessageQueue.next()方法中进行一系列判断后返回
                needWake = mBlocked;    //当阻塞时需要唤醒
            } else {        //elss,说明此时我们发送过来的消息需要按照一定规则插入到队列中
                //将消息按时间顺序插入到MessageQueue。一般地,不需要唤醒事件队列,除非
                //消息队头存在barrier,并且同时Message是队列中最早的异步消息。
                needWake = mBlocked && p.target == null && msg.isAsynchronous();
                Message prev;
      ①        for (;;) {
                    prev = p;
                    p = p.next;     //工作指针后移并且循环遍历链表
                    if (p == null || when < p.when) {   //找到一个不为空并且发送时间大于当前发送时间的消息,
                        break;      //跳出循环,准备把msg(要发送的消息)查到这条消息之前
                    }
                    if (needWake && p.isAsynchronous()) {
                        needWake = false;
                    }
                }
       ②       msg.next = p; // 单链表插入
                prev.next = msg;
            }

            // We can assume mPtr != 0 because mQuitting is false.
            if (needWake) {
                nativeWake(mPtr);
            }
        }
        return true;
    }
```
  MessageQueue是按照**Message触发时间的先后顺序**排列的,队头的消息是将要最早触发的消息。当有消息需要加入消息队列时,会**从队列头开始遍历**,直到找到消息应该插入的合适位置,以保证所有消息的时间顺序。
  上面代码中①处的for()循环就是在循环遍历MessageQueue以找到合适的msg插位置,②处的代码实际上就是一个单链表插入的过程,我们可以把整个插入的代码连起来,这样更容易看出:
```
    prev = p;
    p = p.next;
    msg.next = p;
    prev.next = msg;
```
没骗你吧~~这就是把我们的要发送的消息msg插入到了链表中的p节点之前。

  **我们需要明确的一点是,这里的Handler.post/sendMessage方法是和上面的MessageQueue.next()方法是对应的,他是消息的入队操作。**

###### (2)Looper.loop()中调用Handler.dispatchMessage()接受并处理消息
  好了,讲到这里,我们可以看到,Handler.post或者Handler.sendMessage方法,最终是将他们要发送的消息添加到了消息队列中(Handler.post实际上也是调用了Handler.sendMessageDaley())。
  那么接受消息在哪呢?好吧看标题你也知道,Looper.loop()方法中我们实现了消息的重写与接收。我们回过头去看Looper类,在该类中MessageQueue.next()这个消息出队的方法调用完之后,出现了`msg.target.dispatchMessage(msg);`这句代码,我们说过这句代码中的**msg.target**就是消息的目标Handler,于是我们回到Handler中看下这个方法:
```
    public void dispatchMessage(Message msg) {
        if (msg.callback != null) {
            handleCallback(msg);
        } else {
            if (mCallback != null) {
                if (mCallback.handleMessage(msg)) {
                    return;
                }
            }
            handleMessage(msg);
        }
    }

    /**
     * Subclasses must implement this to receive messages.
     */
    public void handleMessage(Message msg) {
    }
```
  可以看到最终我们调用了一个空方法,为什么呢,因为**消息的最终回调是由我们控制的**,我们在创建handler的时候会在Handler所在的线程中(如果是子线程更新主线程UI的话,就在主线程中)重写handleMessage方法,然后根据msg.what进行消息处理(对照开始给出的Hanlder常见场景)。

我们梳理一下整个流程:
```
① handler = new Handler();  //创建Handler对象
② Looper.prepare();     //准备好Looper,初始化MessageQueue
③ Message msg =Message.obtain();    //从消息池中取出一个可用的Message实例
④ msg.obj = data;           //消息的数据
   msg.what=1;              //标志消息的标志
⑤ handler.sendMessage(msg);     //发送消息,将一个消息添加到消息队列中去
⑥ Looper.loop();        //使用loop使消息队列循环起来,并进行消息的出队删除操作
```
Looper.loop()中的消息出队之后,将调用Handler的dispatchMessage,最终大我们在代码中重写的handleMessage用以自定处理:
```
    private Handler handler = new Handler(){
       @Override
       public void handleMessage(Message msg) {
           super.handleMessage(msg);
           switch (msg.what) {      //判断标志位
               case 1:
                   //更新UI操作
                   break;
           }
        }
      };
```
###### (3)Handler.post() & View.post() & Activity.runOnUiThread()
**①Handler.post()**
  上面我们说了Handler.sendMessage()方法,并且在文章开头的实例中展示了Handler.sendMessage()的使用方法,下面我们来说一下Handler.post(),该方法的具体使用还是略微的有一点不同的:
```
    public final boolean post(Runnable r){
       return  sendMessageDelayed(getPostMessage(r), 0);
    }

```
可以看到,Hander.post()方法还是调用了sendMessageDelayed方法,这跟之前的流程是一样的。这里我们要说的是`sendMessageDelayed(getPostMessage(r), 0);`中的getPostMessage(r)方法:
```
    private static Message getPostMessage(Runnable r) {
        Message m = Message.obtain();
        m.callback = r;
        return m;
    }
```
在这个方法中将消息的callback字段的值指定为传入的Runnable对象。这个callback字段之前我们有遇到过——在Handler的dispatchMessage()方法中原来有做一个检查,如果Message的callback等于null才会去调用handleMessage()方法,否则就调用handleCallback()方法:
```
    public void dispatchMessage(Message msg) {
        if (msg.callback != null) {
            handleCallback(msg);
        } else {
```
handleCallback(msg):
```
    private static void handleCallback(Message message) {
        message.callback.run();
    }
```
这里的run也就是一开始的时候传入的run接口。也就是说,如果通过post(Runnable r)传递消息的话,我们直接就可以在post()方法中进行UI操作:
```
    handler = new Handler();  
        new Thread(new Runnable() {  
            @Override  
            public void run() {  
                handler.post(new Runnable() {  
                    @Override  
                    public void run() {  
                        // 在这里进行UI操作  
                    }  
                });  
            }  
        }).start();  
```
写法上简洁了很多,但是本质上都是一样的。

**②View.post()**
(source/android-24/android/view/View):
```
    public boolean post(Runnable action) {
        final AttachInfo attachInfo = mAttachInfo;
        if (attachInfo != null) {
            return attachInfo.mHandler.post(action);
        }

        // Postpone the runnable until we know on which thread it needs to run.
        // Assume that the runnable will be successfully placed after attach.
        getRunQueue().post(action);
        return true;
    }
```
可以看到同样调用了Handler的post方法

**Activity.runOnUiThread()**
```
    public final void runOnUiThread(Runnable action) {
        if (Thread.currentThread() != mUiThread) {
            mHandler.post(action);
        } else {
            action.run();
        }
    }
```
如果当前的线程不等于UI线程(主线程),就去调用Handler的post()方法,否则就直接调用Runnable对象的run()方法。


##### 5.Message.obtain
上面我们需要解释的一个东西便是`Message msg =Message.obtain();`这句,Message的obtain()方法中维护了一个消息池,其最大容量**MAX_POOL_SIZE = 50**
```
    /**
     * Return a new Message instance from the global pool. Allows us to
     * avoid allocating new objects in many cases.
     */
    public static Message obtain() {
        synchronized (sPoolSync) {
            if (sPool != null) {    //消息池不为空时从消息池中直接拿消息
                Message m = sPool;
                sPool = m.next; //工作指针后移,准备取出sPool
                m.next = null;  //从sPool中取出一个Message对象,并消息链表断开
                m.flags = 0;    // 清除in-use flag
                sPoolSize--;    //消息池的可用大小进行减1操作
                return m;
            }
        }
        return new Message();   // 当消息池为空时,直接创建Message对象
    }

    /** Constructor (but the preferred way to get a Message is to call {@link #obtain() Message.obtain()}).
    */
    public Message() {
    }
```
  我们当然可以直接new Message()这样来创建消息,但是一般来讲我们还是应该调用Message.obtain()方法来返回一个消息实例,以避免Message对象的多次创建。

好了,到此为止,我们调用Handler发送消息更新UI的整个流程就说完了。

#### 三.Activity启动过程中UI线程的MainLooper的创建
  Actvity的启动流程我们之前在一片文章中有讲过,真正启动Activity的是ActiivtyThread类中的main()方法:
```
    public static void main(String[] args) {
        ......
        Looper.prepareMainLooper();

        ActivityThread thread = new ActivityThread();
        thread.attach(false);

        ......
        Looper.loop();
        ......
    }
```
可以看到,我们在启动一个Activity之前,我们在ActivityThread.main()方法中,调用了**Looper.prepareMainLooper()**方法:
```
    public static void prepareMainLooper() {
        prepare(false);
        synchronized (Looper.class) {
            if (sMainLooper != null) {
                throw new IllegalStateException("The main Looper has already been prepared.");
            }
            sMainLooper = myLooper();
        }
    }
```
该方法同上面的Looper.prepare()方法一样,只不过这里准备的是主线程中的Looper,因此**prepare(false);**其中的参数false表示的是,该线程的Looper不能退出。之后调用myLooper则是获取主线程中的Looper对象,这些和上面都没什么区别。

  我们回到ActivityThread.main()中,在准备完主线程的Looper之后,`ActivityThread thread = new ActivityThread();`创建一个ActivityThread实例。`thread.attach(false);`**参数false表示这不是系统进程,是给普通应用程序使用的进程。**
我们接着`thread.attach(false);`来看:
```
    private void attach(boolean system) {
        sCurrentActivityThread = this;
        mSystemThread = system;
        if (!system) {
            ViewRootImpl.addFirstDrawHandler(new Runnable() {
                @Override
                public void run() {
                    ensureJitEnabled();
                }
            });
            ......
            final IActivityManager mgr = ActivityManagerNative.getDefault();
            try {
                mgr.attachApplication(mAppThread);
            } catch (RemoteException ex) {
                throw ex.rethrowFromSystemServer();
            }
            ......
```
注意到`mgr.attachApplication(mAppThread);`这句,其中mAppThread是ApplicationThread的对象,**mgr**为**IActivityManager**接口类,而真正实现IActivityManager接口的是在ActivityManagerService(AMS)类中,关于AMS类中的代码我们这里就先不详细解释了,我们只需要知道,在它当中经过一系列的处理~~最终:
```
app.thread.scheduleLaunchActivity(new Intent(r.intent), r, System.identityHashCode(r),
        r.info, r.icicle, results, newIntents, !andResume, isNextTransitionForward());
}
```
又回调到了ActivityThread类中:
```
    //该方法在ApplicationThread(Binder线程)中调用
    @Override
        public final void scheduleLaunchActivity(Intent intent, IBinder token, int ident,
                ActivityInfo info, Configuration curConfig, Configuration overrideConfig,
                CompatibilityInfo compatInfo, String referrer, IVoiceInteractor voiceInteractor,
                int procState, Bundle state, PersistableBundle persistentState,
                List<ResultInfo> pendingResults, List<ReferrerIntent> pendingNewIntents,
                boolean notResumed, boolean isForward, ProfilerInfo profilerInfo) {

            updateProcessState(procState, false);

            ActivityClientRecord r = new ActivityClientRecord();

            r.token = token;
            r.ident = ident;
            r.intent = intent;
            r.referrer = referrer;
            r.voiceInteractor = voiceInteractor;
            r.activityInfo = info;
            r.compatInfo = compatInfo;
            r.state = state;
            r.persistentState = persistentState;

            r.pendingResults = pendingResults;
            r.pendingIntents = pendingNewIntents;

            r.startsNotResumed = notResumed;
            r.isForward = isForward;

            r.profilerInfo = profilerInfo;

            r.overrideConfig = overrideConfig;
            updatePendingConfiguration(curConfig);

            sendMessage(H.LAUNCH_ACTIVITY, r);
        }
```
可以看到,这里将一系列应用的信息封装在ActivityClientRecord中之后,最终调用了`sendMessage(H.LAUNCH_ACTIVITY, r);`发送消息:
```
    private void sendMessage(int what, Object obj) {
        sendMessage(what, obj, 0, 0, false);
    }
```
```
    private void sendMessage(int what, Object obj, int arg1, int arg2, boolean async) {
        Message msg = Message.obtain();
        msg.what = what;
        msg.obj = obj;
        msg.arg1 = arg1;
        msg.arg2 = arg2;
        if (async) {
            msg.setAsynchronous(true);
        }
        mH.sendMessage(msg);
    }
```
上面的“H”实际上就是一个Handler类:`private class H extends Handler {`,而这里的mH则是H的子类。接着往下看:
```
    public void handleMessage(Message msg) {
            switch (msg.what) {
                case LAUNCH_ACTIVITY: {
                    Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "activityStart");
                    final ActivityClientRecord r = (ActivityClientRecord) msg.obj;

                    r.packageInfo = getPackageInfoNoCheck(
                            r.activityInfo.applicationInfo, r.compatInfo);
                    handleLaunchActivity(r, null, "LAUNCH_ACTIVITY");
                    Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
                    ......
```
可以看到,这里handleMessage收到了我们发送的`LAUNCH_ACTIVITY`也就是启动Activity的请求,实际上这里还有`PAUSE_ACTIVITY`,`RESUME_ACTIVITY`等一系列请求的处理。
之后`handleLaunchActivity(r, null, "LAUNCH_ACTIVITY");`-->`performLaunchActivity`-->`mInstrumentation.callActivityOnCreate`一系列流程之后,我们的Activity就启动了,这个我们在之前的文章中有讲过,这里不再獒述。

  给大家展示这一段,主要是为了说明,在Activity的启动过程中,在系统层面也是调用Handler机制来进行一系列事件的处理,从而推动系统的循环进行。

站在巨人的肩膀上摘苹果:
《Android开发艺术探索》
[郭霖 Android异步消息处理机制完全解析,带你从源码的角度彻底理解](http://blog.csdn.net/guolin_blog/article/details/9991569)
[Gityuan Android消息机制1-Handler(Java层)](http://gityuan.com/2015/12/26/handler-message-framework/)
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,271评论 5 476
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,275评论 2 380
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,151评论 0 336
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,550评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,553评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,559评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,924评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,580评论 0 257
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,826评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,578评论 2 320
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,661评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,363评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,940评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,926评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,156评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,872评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,391评论 2 342

推荐阅读更多精彩内容