看了这个图你再不会忘记消息循环

1,156 阅读4分钟
原文链接: www.jianshu.com

之前看过若干次消息框架,隔了一段时间又记不起来一些细节了。这次补画了一些图,再理顺一下,有图的辅助,以后再看一下图就马上回忆起来了!

说起安卓的消息循环,你一定知道四个类:
Handler,Looper,ThreadLocal,MessageQueue。这次从源码的角度附加图做一个笔记。

基本的逻辑如下图,描述了主要类的结构以及他们是如何绑定到同一个线程的。请看下面的详细的描述。


我是标题

Handler

Handler能够发送和处理和MessageQueue关联的 MessageRunnable。每个Handler和一个单独的线程关联,这个线程就是你创建这个Handler的时候所在的线程,需要处理的MessageQueue也是这个线程的MessageQueue。不信,你看:

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;

Handler有很多个构造函数,最终都会调用到这个构造函数。你可以看到Handler中的的Looper成员,就是通过Looper的静态方法myLooper得到的,myLooper是干啥的?你可以往下看Looper的内容,在这里你只要知道得到了一个和这个线程关联的Looper。如果 mLooper成员是null,那么就抛出异常。你可能会问,我在activity中随便创建handler啊,没有调用Looper.myLooper()方法。那是因为当你的应用运行的时候,Android已经通过Looper的静态方法prepareMainLooper创建了,这个方法只能执行一次,否则就会抛出异常了。这个Looper适合线程绑定的,你再看看mQueue,是从mLooper中拿到的。

Looper

Looper是做什么的?Looper的职能是为一个线程创建MessageQueue,绑定到这个线程,为此线程执行消息循环。
线程默认是没有looper的,除非你在线程调用prepare方法,然后才能执行loop方法才能进行消息处理。不信你看:

public static void loop() {    
      final Looper me = myLooper();    
      if (me == null) {        
          throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this 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();
     }
}

prepare做了什么呢?

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

你可以看到prepare()方法只能调用一次。在最后会创建一个Looper放在ThreadLocal里保存。关于ThreadLocal请看后文,这里你只要知道它是了一个和线程相关的存储结构。Looper是如何创建的呢?

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

可以看到,构造方法是私有的,新创建了一个MessageQueue,mThread就是当前线程。
那么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();       
              msg.target.dispatchMessage(msg);     
              msg.recycleUnchecked();    
        }
}

可以看到,通过一个无限循环,不停的在消息队列中拿消息,将消息分发到指定的地方。

Message的target其实就是Handler

大多数和消息循环的交互都是通过Handler去完成的,就像你在Handler那部分看到的那样。记得在你不再执行消息循环的时候调用Looperquit方法。

ThreadLocal

每个线程有一些和自己相关的变量,ThreadLocal的作用就是保存这些变量的。所有的变量是通过内部的静态类Value存储的。虽然,线程都是通过访问相同的ThreadLocal,但是每个线程保存的变量是分开的,不信,你看:

public void set(T value) {    
      Thread currentThread = Thread.currentThread();    
      Values values = values(currentThread);    
      if (values == null) {        
            values = initializeValues(currentThread);    }    
      values.put(this, value);
}

上面的set方法中,values方法返回当前线程的localValues成员变量:

/** 
  * Gets Values instance for this thread and variable type. 
  */
Values values(Thread current) {    
      return current.localValues;
}

那么线程内部的localValues是什么?

public class Thread implements Runnable {
    /** 
      * Normal thread local values. 
      */
    ThreadLocal.Values localValues;
    /*省略若干代码*/
}

可以看到,Thread中的localValues是定义在ThreadLocal中线程本地的变量。如果在 values ()方法取得null值,就执行initializeValues方法。
initializeValues是如何实现的呢?

Values initializeValues(Thread current) {    
      return current.localValues = new Values();
}

然后将value的值放在当前线程的的localValues里。这样,虽然看起来访问的是用一个ThreadLocal,但是得到的值却是根据线程而不同的。

MessageQueue

MessageQueue是一个消息队列,包含成员变量Message mMessages;,可以理解成链表的头部。
内部包含5个native方法:

private native static long nativeInit();
private native static void nativeDestroy(long ptr);
private native static void nativePollOnce(long ptr, int timeoutMillis);
private native static void nativeWake(long ptr);
private native static boolean nativeIsIdling(long ptr);

这个类有很多native代码的调用,所以我并没有仔细看这个类,欢迎各位同学指点。