Android消息处理机制分析

Android的消息处理机制有如下主要类实现:Message, MessageQueue, Looper, Handler。读完这篇文章能对这几个核心类的职责和功能有所了解,对这几个类之间的相互依赖关系有进一步的认识。Hope Help!

1.核心类的职责和部分源码分析

a. Message:消息对象包含一个描述和任意数据对象,该对象可以被发送给Handler。同时,又额外定义了两个int字段和一个对象字段,使用这些已定义的字段就可以满足我们大部分场景的需求。

  • Message内部使用了享元模式,维护了一个对象池,所以有追求的我们肯定要使用Message.obtain()来获取消息
  • Message对象有一个callback字段,是Runnable类型, 如果有设置,将优先执行该callback。来看看Handler中的dispatchMessage()。优先由Msg中的callback来处理,Handler中的mCallback次之,最后才是我们最熟悉的Handler中的handleMessage()
/**
* Handle system messages here.
*/
public void dispatchMessage(Message msg) {
if (msg.callback != null) {
    handleCallback(msg);
} else {
 if (mCallback != null) {
     if (mCallback.handleMessage(msg)) {
         return;
     }
 }
 handleMessage(msg);
}       

b. MessageQueue:底层类,表示一个消息队列。名为消息队列,该类中并没有一个列表或者队列的定义,而是通过Message对象来实现的。因为Message成员变量中包含了一个next字段,类型为Message,实现了一个链表。该类主要有两个职责:
一是负责消息入队列,见enqueueMessage(Message msg, long when)

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.");
     }

     synchronized (this) {
         ...

         msg.markInUse();
         msg.when = when;
         Message p = mMessages;
         boolean needWake;
         if (p == null || when == 0 || when < p.when) {
             // New head, wake up the event queue if blocked.
             msg.next = p;
             mMessages = msg;
             needWake = mBlocked;
         } else {

             /*
              *这里一开始很疑惑,明明每次enqueueMessage都有个msg.target为null的判断,为什么底下还有个p.target == null的判断呢?
              *原来MessageQueue里还有一个syncBarrier机制,详见postSyncBarrier(), 在那里构建的msg不指定target
              */

             needWake = mBlocked && p.target == null && msg.isAsynchronous();
             Message prev;
             for (;;) {
                 prev = p;
                 p = p.next;
                 if (p == null || when < p.when) {
                     break;
                 }

                 //neekWake为true意味着上面的三个条件都成立,有syncBarrier的作用同时消息为异步消息,这时又将needWake置为false//表明不需要唤醒底层的管道通信,这映证了syncBarrier除了带有消息优先级外(优先处理异步消息),
                 //还会拖延、阻碍同步消息的处理。
                 if (needWake && p.isAsynchronous()) {
                     needWake = false;
                 }
             }
             msg.next = p; // invariant: p == prev.next
             prev.next = msg;
         }

         // We can assume mPtr != 0 because mQuitting is false.
         if (needWake) {
             nativeWake(mPtr);
         }
     }
     return true;
 }

二是负责获取下一条已准备好待处理的消息,详见next()。这里就不看源码了,主要就是在for构造的无线循环内部调用native方法nativePollOnce(ptr, nextPollTimeoutMillis)去取消息,有就读取缓存并获取消息,没有就会阻塞。

c. Looper:线程默认是不存在消息循环的,但是通过在线程内部调用Looper.prepare(),接着调用Looper.loop()制造了一个消息循环。使得线程可以一直处于运行状态直到循环退出。

我们来看一个典型的Looper Thread的例子:

class LooperThread extends Thread {
     public Handler mHandler;

     public void run() {
         Looper.prepare();

         mHandler = new Handler() {
              public void handleMessage(Message msg) {
                 // process incoming messages here
             }
         };

         Looper.loop();
     }
}
  • 第一步:在线程的run()先调用了Looper.prepare(),代码实现很简单,通过线程本地变量ThreadLocal保证每个线程只能创建一个Looper。
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));
}
  • 第二步:在run()结束前调用Looper.loop()。从源码可见,loop()实则是运行了一个无限的for循环,每次循环会从消息队列中获取一个消息,然后进行消息的分发处理。
    虽然是无限循环,但我们不用担心其性能。因为queue.next()在获取不到消息时会进入空闲等待,并不会占用CPU资源。

    在很早之前学习Android的消息机制时,对Looper把线程变成一个无限循环的线程感到很神奇。不过从源码中得知,不过是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) {
              // No message indicates that the message queue is quitting.
              return;
          }

          ...

          try {
              msg.target.dispatchMessage(msg);
          } finally {

          }

    ...
          msg.recycleUnchecked(); //回收msg对象
      }
 }

d. Handler:允许我们发送和处理Message以及Runnable对象。当我们创建一个新的Handler,它就和线程以及消息队列产生了关联。Handler会将Message和Runnable对象发送到绑定的消息队列中并按照它们出队列的顺序来执行。

从下面Handler的构造方法我们可以看出,Handler和线程,以及消息队列是如何关联起来的了。在这个构造方法中,调用了Looper.myLooper()获取Looper对象,Looper对象在实例化时就与当前线程关联起来了。又从Looper中获取到了消息队列赋值给了Handler的mQueue。

private Looper(boolean quitAllowed) {
      mQueue = new MessageQueue(quitAllowed);
      mThread = Thread.currentThread();
  }
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;
  }

2.核心类依赖关系

画图之前正好又温习了一遍UML的6种关系。然后在网上又找了一遍消息机制核心类的UML类图,没找到合适的,或者说看到了好多个版本,但跟自己的认知有一定偏差,于是就凭着自己的理解画了一个。

我把Looper、MessageQueue以及Thread之间的关系定义为组合关系,因为我的理解,这三个可以独立存在,但我觉得如果分开那就不是消息循环了。可能有些人会觉得这三者之间的关系用聚合来表示更合适,但个人更倾向组合,因为缺一不可,不可分割。不过这带有个人的主观想法在里面。
这里写图片描述

3.总结

通过本篇文章,主要有以下收获:

  1. 梳理了几个核心类之间的关系,脉络更加清晰,对其中的代码实现有了更加深刻的认识。

  2. 了解了postSyncBarrier机制,即同步屏障,了解了其作用与实现原理。这个以前还真不晓得。点击查看详情

  3. 理解了消息循环机制Looper,对于自定义一个消息循环应该是手到擒来了。

  4. 了解了消息机制的空闲等待,即queue.next() //might block, 内部的实现是Linux底层的epoll机制。顺便扩展了select, poll, epoll的相关知识点。点击学习

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值