消息机制概述

从开发角度而言,HandlerAndroid消息机制的上层接口,所以,消息机制主要就是Handler的运行机制,Handler的作用就是在一个线程中发送消息,然后在另一个线程中处理,通过在消息中存放数据达到线程间通信。如果你想在哪个线程中接收消息,那你就要在那个线程中生成一个Handler对象(当然实际上是在哪个线程中调用了Looper.loop()方法,那么就会在哪个线程中接收消息)。Handler的运行需要LooperMessageQueue的支撑。所以接下来就分别分析这三个类。

MessageQueue

这个类翻译过来就是消息队列,但是这其实是一个链表,因为对于这个类而言,它的作用就是存取handler发送的消息,它包含的主要操作就是插入和读取,读取的时候也会涉及删除操作,意思就是一个消息只能被处理一次,所以使用链表的结构会更好,因为链表在插入和删除上有优势。这个类的对象是在Looper的构造函数中生成的,也就是说一个Looper对象就对应自己的一个MessageQueue对象,代码如下:

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

而对于一个线程而言,就只有一个Looper对象,所以一个线程就只有一个MessageQueue对象。为什么一个线程就只有一个Looper对象,我们继续往下看。

Looper

这个类的作用就是管理handler发送的消息,也就意味着它需要和MessageQueue一起使用,这可能就是在源码里为什么会将MessageQueue的构造放在Looper的构造函数里。在Looper的构造函数里,会构造一个MessageQueue对象,同时会得到当前的线程,代码如下:

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

这也就意味着Looper的操作都是基于当前线程的,那我们是如何实现线程切换的呢?其实说起来很简单,我们只需要得到每个线程中的Looper对象就可以了。那么,如何得到呢?接下来就介绍一个重要的类:ThreadLocal,掌声欢迎。

ThreadLocal

ThreadLocal是一个线程内部的数据存储类,通过它可以在指定的线程中存储数据,数据存储以后,只有在指定的线程中可以获取到存储的数据,对于其他线程则无法获取到数据。这样说太抽象了,直接上代码:

  public class MainActivity extends Activity {
  private ThreadLocal<String> stringThreadLocal;
@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);
    TestThreadLocal();
}
public void TestThreadLocal(){
    stringThreadLocal=new ThreadLocal<>();
    stringThreadLocal.set("main");
    Log.e("log", "main=" + stringThreadLocal.get());
    new Thread("thread1"){
        @Override
        public void run() {
            super.run();
            stringThreadLocal.set("thread1");
            Log.e("log","thread1="+stringThreadLocal.get());
        }
    }.start();
    new Thread("thread2"){
        @Override
        public void run() {
            super.run();
            stringThreadLocal.set("thread2");
            Log.e("log","thread2="+stringThreadLocal.get());
        }
    }.start();
}
}

运行结果如下:

TestThreadLocal-20161219
从上面的Log可以看出,在不同的线程中访问同一个ThreadLocal对象,ThreadLocal获取到的值是不一样的。这就是ThreadLocal的奇妙之处。ThreadLocal之所以有这么奇妙的效果,是因为不同线程访问同一个ThreadLocal的get方法,ThreadLocal内部会从各自的线程中取出一个数组,然后再从数组中根据当前的ThreadLocal的索引去查找对应的value值。而不同的线程中的数组是不同的,这就是为什么通过ThreadLocal可以在不同的线程中维护一套数据的副本并且彼此互不干扰。

这样的话就很简单了,通过这个ThreadLocal类我们就可以轻松的得到各个线程中的Looper对象了,从源码中我们也可以看出来:

static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();

这里就贴这一段了,贴多了看着不好看,有兴趣的同学可以下去自己看一下Looper的源码,其实很简单。既然我们可以得到各个线程中的Looper对象,那么也就意味着其实我们就已经实现了线程切换。然后我们再看一下Loope对象中的重要方法:

prepare():这个方法的作用是构造一个Looper对象,调用的顺序是先调用prepare(boolean quitAllowed)方法,然后调用sThreadLocal.set(new Looper(quitAllowed));方法,其中quitAllowed的值的作用就是区分主线程和其他线程,区分的作用是设置主线程不允许退出的

loop():这个方法的作用是循环遍历整个消息队列,将handler发送的消息送给msg.target.dispatchMessage(msg);
处理,这是一个死循环,退出的唯一方式就是MessageQueuenext()方法返回null,msg.target其实就是你定义的handler对象,这样就实现了将消息又返回给handler自身来处理。而且,实现了线程的切换。

quie()和quitSafely():这两个方法的作用就是退出loop()循环,两个的区别就是,第一个是立即退出,第二个时等消息队列中的已有消息处理完毕再退出。所以,当我们手动创建Looper的时候注意在合适的时候把loop退出。

Handler

这个类的作用就是发送消息和处理消息,当你在创建一个Handler对象的时候,在Handler的构造函数中会得到当前的线程的Looper对象,按照之前的分析,得到了Looper对象其实就得到了MessageQueue对象,这里注意是当前线程,当我们调用sendMessage(message)方法的时候,会将一个消息发送到调用Looper.loop()方法所在的线程的消息队列中,跟在哪个线程里调用sendMessage(message)方法没有关系,然后创建Handler的线程的Looper对象的Loop()方法就会将消息返回给handler对象的dispatchMessage(msg);来处理。也就是说将sendMessage(message)在别的线程中调用,最后message会在调用Looper.loop()方法的线程中来执行。使用Handler发送消息有两种方式,一个是post(runnable),还有一个是sendMessage(message),在调用第一个的时候,会调用getPostMessage(Runnable r)方法,将Runnable对象存储到Message对象中,所以第一个还是调用第二个,下面看一下HandlerdispatchMessage(Message msg),代码如下:

   public void dispatchMessage(Message msg) {
    if (msg.callback != null) {
        handleCallback(msg);
    } else {
        if (mCallback != null) {
            if (mCallback.handleMessage(msg)) {
                return;
            }
        }
        handleMessage(msg);
    }
}

其中的msg.callback就是Runnable对象,而mCallback就是当你创建Handler是通过传递一个Handler.Callback对象的时候的Handler.Callback对象。

public Handler(Callback callback) {
    this(callback, false);
}

最后就是Handler自身的handleMessage(msg)。这就是HandlerdispatchMessage(Message msg)的执行顺序,也就是先执行Runnable对象,然后是Handler.Callback对象,最后是Handler自身的handleMessage(msg);

从使用流程分析

当我们创建一个Handler的时候,在Handler的构造函数里会调用Looper.myLooper();来得到当前的线程的Looper对象,得到了Looper对象也就得到了当前线程的MessageQueue对象,注意这里是当前线程,然后用创建好了的Handler对象来发送消息,一般来说发送消息都会在别的线程,因为这样才有意义,然后当调用HandlersendMessage(message)后,就会调用MessageQueueenqueueMessage(msg, uptimeMillis);方法,这样就将消息发送到了调用Looper.loop()方法所在的线程的消息队列中,然后将消息发送给msg.target.dispatchMessage(msg);来处理,其中的msg.target就是你创建的Handler对象,然后消息就得到了处理。在实际的开发中,我们的发送消息都会在别的线程,并且在这个线程发送消息之前会完成一些耗时任务,比如下载,然后下载完成后发送消息,在handleMessage(Message msg)或者run()去执行操作,因为这两个方法的调用都是在创建Handler的线程中调用的,这样就完成了将下载任务指定到专门的下载线程中,并且能够在原来的线程中操作下载任务的结果。这也就是实现了将一个任务切换到指定的线程中工作