Android 消息机制之 Handler 发送消息的深入源码分析 [ 七 ]

Android 消息机制深入源码分析 [ 一 ]
Android 消息机制之 ThreadLocal 深入源码分析 [ 二 ]
Android 消息机制之 Looper 深入源码分析 [ 三 ]
Android 消息机制之 Message 与消息对象池的深入源码分析 [ 四 ]
Android 消息机制之 MessageQueue 深入源码分析 [ 五 ]
Android 消息机制之初识Handler [ 六 ]
Android 消息机制之 Handler 发送消息的深入源码分析 [ 七 ]
Android 消息机制之 MessageQueue.next() 消息取出的深入源码分析 [ 八 ]
Android 消息机制之消息的其他处理深入源码分析 [ 九 ]
Android 消息机制总结 [ 十 ]

上一章, 初步认识了 Handler, 本章节将继续接着上一章开始对消息的具体发送进行学习和分析.

1. Handler 消息发送的分类

  • Handler 平时发送消息主要是调用两大类方法, 分别是 send 方案 以及 post 方案

  • send 方案

    • boolean sendMessage(Message msg)
    • boolean sendEmptyMessage(int what)
    • boolean sendEmptyMessageDelayed(int what, long delayMillis)
    • boolean sendEmptyMessageAtTime(int what, long uptimeMillis)
    • boolean sendMessageDelayed(Message msg, long delayMillis)
    • boolean sendMessageAtTime(Message msg, long uptimeMillis)
    • boolean sendMessageAtFrontOfQueue(Message msg)
  • post 方案

    • boolean post(Runnable r)
    • boolean postAtFrontOfQueue(Runnable r)
    • boolean postAtTime(Runnable r, long uptimeMillis)
    • boolean postAtTime(Runnable r, Object token, long uptimeMillis)
    • boolean postDelayed(Runnable r, long delayMillis)
    • boolean postDelayed(Runnable r, Object token, long delayMillis)
       

 

2. send 方案.

还是先从 send 方案的第一个开始看, 也就是我们最常用的一个.

2.1 boolean sendMessage(Message msg)
Handler.java 602 行.

public final boolean sendMessage(Message msg){
    return sendMessageDelayed(msg, 0);
}
  • 官方注释:

在当前时间之前的所有挂起消息之后,将消息推送到消息队列的末尾. 在和当前线程关联的 Handler里面的 HandlerMessage 将收到这条消息.

  • 分析

代码中就是简单的调用了 sendMessageDelayed(msg, 0)

 
2.2 boolean sendMessageDelayed(Message msg, long delayMillis)
Handler.java 662 行.

public final boolean sendMessageDelayed(Message msg, long delayMillis){
    //判断 delayMillis 是否小于0
    if (delayMillis < 0) {
        delayMillis = 0;
    }
    return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);
}
  • 分析

如果传入的延迟时间小于 0 , 那么就等于 0. 然后调用 sendMessageAtTime 传入了 SystemClock.uptimeMillis() + delayMillis
SystemClock.uptimeMillis() = 从开机到现在的毫秒数(手机睡眠的时间不包括在内).

 
2.3 boolean sendMessageAtTime(Message msg, long uptimeMillis)
Handler.java 689 行.

public boolean sendMessageAtTime(Message msg, long uptimeMillis) {
    MessageQueue queue = mQueue;
    if (queue == null) {
        RuntimeException e = new RuntimeException(this + " sendMessageAtTime() called with no mQueue");
        Log.w("Looper", e.getMessage(), e);
        return false;
    }
    return enqueueMessage(queue, msg, uptimeMillis);
}
  • 官方注释

在 Android 系统的重开机到现在的毫秒数为基准的绝对时间下, 将消息放到所有的待处理消息后的队列中. 深度睡眠中的时间将会延迟执行的时间,(也就是说手机睡眠的时间不包括在内), 你将在和当前线程关联的 Handler里面的 HandlerMessage 将收到这条消息.

  • 分析

方法内部做了两件事情

  • 获取消息队列, 并对消息队列做非空判断.,如果为 null, 直接返回 false. (在 Handler 的构造方法中, 会对消息队列对象 mQueue 进行赋值)
  • 调用 enqueueMessage(queue, msg, uptimeMillis) 方法.

 
2.4 boolean enqueueMessagee(MessageQueue queue, Message msg, long uptimeMillis)
Handler.java 740 行.

private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
    msg.target = this;
    if (mAsynchronous) {
        msg.setAsynchronous(true);
    }
    return queue.enqueueMessage(msg, uptimeMillis);
}
  • 分析

内部做了三件事

  • 设置 msgtarget 变量指向当前 Handler
  • 如果当前 Handler 是异步的, 则设置 msg 也为异步. 让 HandlerMessage 达成一致. (在上一章 初识 Handler 中说过, 默认是非异步的, 需要在创建 Handler 的时候指定为异步)
  • 调用 MessageQueue.enqueueMessage() 方法进行消息入队 .

 
2.5 MessageQueue boolean enqueueMessage(Message msg, long when)
MessageQueue.java 536 行, 由于代码过长, 将会分为多段来分析.

boolean enqueueMessage(Message msg, long when) {
  //第一步
  if (msg.target == null) {
      throw new IllegalArgumentException("Message must have a target.");
  }
  //第二步
  if (msg.isInUse()) {
      throw new IllegalStateException(msg + " This message is already in use.");
  }
  • 分析
  1. 判断 msgtarget 变量是否为 null, 如果为 null,则会人会是障栅, 但是障栅的添加是通过 postSyncBarrier 方式添加的(在本系列的第五章节有分析). 所以正常 Messagetarget 一定是有值的. 反之抛出异常.
  2. 接着判断 msg 的标记为, 因为此时的 Message 是要入队, 意味着 msg 的标记位应该是未被使用. 如果显示已使用, 则抛出异常.
  //第三步
  synchronized (this) {
      //第四步
      if (mQuitting) {
          IllegalStateException e = new IllegalStateException(msg.target + " sending message to a Handler on a dead thread");
          Log.w(TAG, e.getMessage(), e);
          msg.recycle();
          return false;
      }
      //第五步
      msg.markInUse();
      msg.when = when;
      Message p = mMessages;
      boolean needWake;
      //第六步
      if (p == null || when == 0 || when < p.when) {
          //把 msg 的下一个元素设置为 p
          msg.next = p;
          //把 msg 设置为链表的头部元素
          mMessages = msg;
          //如果有阻塞,则需要唤醒
          needWake = mBlocked;
      } 
  • 分析
  1. 加入同步锁
  2. 判断消息队列是否正在关闭, 如果是, 直接返回 false, 表示消息入队失败. 并且回收消息.
  3. 设置 msg 的发送时间, 以及设置 msg 正在使用的标记位. 接着取出消息链表中头部元素赋值给 p
  4. p == null 说明当前要入队的消息是第一个消息. when == 0 表示立即执行. when < p.when 表示当前要入队 msg 的执行时间要早于消息链表头部元素的执行时间. 所以这三个条件, 无论哪个条件成立, 都会把当前要入队的 msg 设置为消息链表的头部.
      //第七步
      else {
          //第八步
          needWake = mBlocked && p.target == null && msg.isAsynchronous();
          Message prev;
          //第九步
          //不断遍历消息队列, 根据 when 的比较找到合适的插入 message 的位置
          for (;;) {
              prev = p;
              p = p.next;
              //第十步
              if (p == null || when < p.when) {
                  break;
              }
              //第十一步
              if (needWake && p.isAsynchronous()) {
                  needWake = false;
              }
          }
          //第十二步
          msg.next = p; // invariant: p == prev.next
          prev.next = msg;
      }
            //第十三步
      if (needWake) {
          nativeWake(mPtr);
      }
  }
  //第十四步
  return true;
}
  • 分析
  1. 如果以上三个条件都不满足, 则说明要吧 msg 插入到消息链表中非头部的位置.
  2. 如果头部元素是障栅或者是异步消息,而且还是插入中间位置,我们不会唤醒
    mBlocked 这里先认为是 false, 它的赋值是在 MessageQueue.next() 方法中 , 如果当前消息链表没有消息, 或者未到执行时间, 并且也没有可执行的空闲消息的时候, 会被赋值为 true, 并且消息队列开始阻塞, 当有可以立即执行的消息的时候会被赋值为 false.
    p.target == null 表示头部元素是一个障栅.
    msg.isAsynchronous() 当前要入队的消息是一个异步消息.
  3. 进入死循环, 将 p 赋值给 prev, 那么 prev 也指向了消息链表的头部, 接着将 p 指向了 p.next. 也就是链表中的第二个元素. 这两个赋值操作, 实现了消息指针的移动.
  4. 消息指针移动过后, p == null, 说明没有下一个元素了, when < p.when 则说明要入队的这个消息的执行时间是小于当前 p 指向消息的执行时间的. 这两个条件有一个成立说明现在这个位置适合插入要入队的消息, 然后就跳出死循环,
  5. 若第 10 步没有跳出循环, 并且执行到了 11 步, 那么说明没有满足条件, 队列中还有消息, 不需要唤醒.
  6. 跳出循环后做了两件事.
  • 将要入队的这个消息的 next 指向循环中获取到应该排在这个入队消息之后的 Message.
  • 将要入队的这个消息前面消息的 next 指向自己. 这样就完成了入队操作.
  1. 是否要唤醒.
  2. 入队成功返回 true

 
以上就是我们常用的 sendMessage(Message msg) 消息发送流程, 接下来看 send 方案中剩下的.

2.5 boolean sendMessageAtFrontOfQueue(Message msg)
Handler.java 712 行.

public final boolean sendMessageAtFrontOfQueue(Message msg) {
    MessageQueue queue = mQueue;
    if (queue == null) {
        RuntimeException e = new RuntimeException(this + " sendMessageAtTime() called with no mQueue");
        Log.w("Looper", e.getMessage(), e);
        return false;
    }
    return enqueueMessage(queue, msg, 0);
}
  • 官方翻译

在消息队列的最前面插入一个消息, 在消息循环的下一次迭代中进行处理. 你将在当前线程关联的 HandlerhandleMessage() 中收到这个消息. 由于它可以轻松的解决消息的排序和其他的意外副作用.

  • 解析

方法内部的实现和 boolean sendMessageAtTime(Message msg, long uptimeMillis) 大体上一致. 唯一的区别就是该方法在调用入队方法的时候, 最后一个参数是 0, 表示需要立即执行.

 
剩下的 boolean sendEmptyMessage(int what)boolean sendEmptyMessageDelayed(int what, long uptimeMillis) 就不再分析了, 非常简单, 可以自己看一下.
 
send 方案的最后, 都是会调用 enqueueMessage() 入队方法来完成消息的入队.

3. post 方案

3.1 boolean post(Runnable r)
Handler.java 393行.

public final boolean post(Runnable r){
   return  sendMessageDelayed(getPostMessage(r), 0);
}
  • 官方注释

将一个 Runnable 添加到消息队列中, 这个 Runnable 将会在和当前 Handler 关联的线程中执行.

-分析

这个方法内部简单, 就是调用了 sendMessageDelayed() 这个方法, 所以可见 boolean post(Runnable r) 这个方法最终还是走到上面说的 send 方案的流程中. 最后还是调用 enqueueMessage() 入队方案.

看一下调用的 getPostMessage(Runnable r)

 
3.1 Message getPostMessage(Runnable r)
Handler.java 859 行

private static Message getPostMessage(Runnable r) {
    Message m = Message.obtain();
    m.callback = r;
    return m;
}
  • 分析
  1. 从消息对象池中获取一个空的 Message
    2.将空 Messagecallback 指向 Runnable (这个 CallbackMessage 中的 Runnable)
    3.最后返回这个 Message

由此我们可以得知 boolean post(Runnable r) 方法的内部也是通过 Message.obtain 来获取一个 Message 对象. 然后仅仅只是把 Message 对象的 Runnable callback 赋值而已. 最后还是调用了 send 方案的某个流程最终调用到入队方法.

后面剩余的 post 方法依旧如此, 只是调用的是不同的 send 方案中的方法.

 

4.消息发送总结

Handler 发送消息 (除障栅外), 无论是 send 还是 post 最终都会调用 MessageQueue.enqueueMessage(Message msg, long when) 方法进行入队. 下图展示了他们之间的关系.

Handler 消息发送调用关系图

消息发送出去了, 剩下的就是取出消息以及其他处理了, 将在下一章分析.

你可能感兴趣的:(Android 消息机制之 Handler 发送消息的深入源码分析 [ 七 ])