Handler、MessageQueue、Runnable与Looper的源码浅析

Handler、MessageQueue、Runnable与Looper的源码浅析

工作原理

Handler发送消息到MessageQueue中,Looper不断获取MessageQueue中的一个Message,然后分发给Handler去处理。这几个对象和线程的关系为:

  • 每个Thread 只对应一个Looper
  • 每个Looper 只对应一个MessageQueue
  • 每个MessageQueue包含了N个消息
  • 每个Message最多只能有对应一个Handler来进行处理
  • 每个 Thread 可以包含多个 Handler 对象

那么根据以上关系,不难发现,一个消息要想得到处理,必须经过这几个步骤

  1. 创建消息队以及管理它的Looper
  2. 将Handler绑定到消息队列中去
  3. Handle发送消息到队列中
  4. Looper将消息取出并发送给Handler
  5. Handler进行消息处理

接下来我们就看看这些步骤是怎样实现的

一、消息队列和Looper是怎么样初始化出来的

这要从两种情况考虑了,主线程和普通线程。我们先看看普通线程

普通线程的情况

先看一个使用Looper的典型样例代码:

class MyThread extends Thread{
    private Handler mHandler;

    @Override
    public void run() {
        Looper.prepare(); //初始化的工作在这里

        mHandler = new Handler(){
            @Override
            public void handleMessage(Message msg) {
                    // TODO: 处理消息
            }
        };

        Looper.loop(); // 开始循环收发消息
    }
}

Looper的prepare究竟做了什么呢?

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

这里面有一个sThreadLocal的变量,它是一个static final 类型的ThreadLocal对象。它可以为每个线程提供资源的副本。我们可以看到 sThreadLocal.set(new Looper(quitAllowed)),也就是为每个线程提供了一个Looper对象。Looper中自带有一个MessageQueue的成员变量mQueue,在Looper的构造方法中被new了出来,这样,通过调用prepare系统就为每个线程创建了一个Looper对象并配套了一个MessageQueue。

主线程

UI主线程其实是一个名为ActivityThread类的实例,来看看它的入口程序吧

public static void main(String[] args) {
     ...
     Looper.prepareMainLooper();
     ActivityThread thread = new ActivityThread();
     thread.attach(false);
      if (sMainThreadHandler == null) {
         sMainThreadHandler = thread.getHandler();
      }
       ...
       Looper.loop();
       ...
}

可以发现它和常规线程的不同之处,首先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();
    }
}

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

它利用了上述的prepare方法生产了一个不许退出的Looper(因为传入的boolean为false),从本质角度来说这个Looper的其他线程的Looper并没有本质的区别。只是它被记录在了sMainLooper的成员变量里了,其他线程可以通过getMainLooper()方法得到它。但是其他的线程的Looper只能在线程内访问。主线程的非主线程的Looper的区别就在于两者的访问权限是不同的。

二、Handler是如何和MessageQueue绑定的

这个要看看Handler的构造器了,当查看了它的几个构造方法时,发现Handler的内部是有这几个成员变量的

final MessageQueue mQueue;
final Looper mLooper;

各个构造器的关键就在于为这两个成员赋值:

mLooper = Looper.myLooper(); //这里面就一句话 sThreadLocal.get(),也就是通过sThreadLocal来获得属于所在线程的Looper对象
...
mQueue = mLooper.mQueue; // 这样就绑定了Looper所管理的消息队列

也就是在new 一个Handler时,默认情况下就会绑定当前线程Looper的MessageQueue。当然Handler还有两个有参构造器,允许你在非主线程中绑定主线程的Looper。

三、Handle如何发送消息到队列中

我们在使用时会发现Handler可以发送两种类型的消息,一个是Message一个是Runnable。对于Runnable而言,最终也是被打包成Message中发送的。将会把Runnable赋值到Message的callback成员。看代码:

 private static Message getPostMessage(Runnable r) {
        Message m = Message.obtain();
        m.callback = r;
        return m;
    }   
 ...
 public final boolean post(Runnable r){
       return  sendMessageDelayed(getPostMessage(r), 0);
 }

打包成Message之后要怎样发送呢?,继续看代码

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

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

在enqueueMessage中,msg.target被设置为this,也就是当前发送它的Handler,这个在未来分发和处理Message中我们会继续讨论。关键看看queue的enqueueMessage中是如何把Message插入的,我们看看它的关键部分

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 {
         // Inserted within the middle of the queue. Usually we don't have to wake
                // up the event queue unless there is a barrier at the head of the queue
                // and the message is the earliest asynchronous message in the queue.
                needWake = mBlocked && p.target == null && msg.isAsynchronous();
                Message prev;
                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;
            }

可见整个Queue非常想一种链表结构,首先看表头是不是为空,是的话就把msg放入,作为表头。否则就遍历整条链到最后一个消息,并把最新的消息插入。这样Handler就把Message发送到Queue中了

四、Looper如何将消息取出并发送给Handler

无论是否在主线程,都需要调用Looper的loop方法启动消息收发机制,看看loop都干嘛了

public static void loop(){
    final Looper me = myLooper();
    ...
    final MessageQueue queue = me.mQueue;
    for(;;){
        Message msg = queue.next();
        if(msg == null){
            return;
        }
        ...
        msg.target.dispatchMessage(msg);
        ...
        msg.recycle();
    }
}

首先通过myLooper()获取当前线程的Looper对象,再拿到对应的消息度列queue。然后遍历消息,注意取消息时可能因为没有消息可以拿到而阻塞。如果没有消息说明线程线程将要退出,故return了。还记得之前提过msg.target实际上个Handler吧,也就是发送它的那个Handler,开始对Message进行分发。最后回收这个处理过的Message。

五、Handler是如何处理消息的

关键看dispatchMessage方法,它的默认流程是这样的:

  1. 首先检查msg.callback是否为空,这个msg.callback是个Runnable对象,也就是开在开始是包装Runnable用的。如果不为空,那么就交由这个Runnable自己处理,即调用这个Runnable的run方法。
  2. 再检查这个Handler自己的成员mCallback是否为空。Handler有一个interface名为CallBack,里面声明了一个handleMessage方法。这个mCallback就是个Callback类型。开发者可以通过调用Handler(Callback callback)来传入自己实现的这个接口。如果这个mCallback不为空,那么消息就交由其处理
  3. 前面的情况都不是,那就调用Handler自己的handleMessage方法。开发者可以自己重写这个方法,按照业务需求处理消息。

小结

这就是Handler分发消息的原理——Handler负责收发,Looper负责不断的取消息,MessageQueue负责存储消息。还有很多细节笔者并没有写出,感兴趣的同学可以继续探讨。先就写这么多,如果所整理的有不当之处,欢迎读者批评指正。

你可能感兴趣的:(源码,android,handler,message,looper)