Andoid handler, looper, messager

前面一篇文章中介绍了AsyncTask,但是正如以前提到的那样,AsyncTask具有一定的局限性及其弊端,比如使用不当会造成内存泄漏。今天我们介绍Android的另外一种异步机制:Handler。事实上,Handler和AsyncTask是Android中最常用的异步处理的工具。

使用

Handler的常规用法想必大家很熟悉了,一般来说就是在UI线程中创建一个Handler,然后在其他线程中拿到该handler的引用,然后再向其发送消息以便处理某些需求,比如更新UI等。鉴于比较简单就不再赘述了。

而这篇文章想说的更多的是一些handler背后的东西。

under the hood

在handler的处理函数中,一般来讲(只要handler是在UI线程中国年创建的)是可以直接处理UI元素的。但是如果我们在后台线程中创建的Handler直接操作UI的话,就会导致应用crash,并且发现下面的错误log:

java.lang.RuntimeException: Can’t create handler inside thread that has not called Looper.prepare()

为什么会出现这个错误呢?简单的回答是我们只能在UI线程中操作view,而更加准确的回答是我们只能在持有Looper的线程中操作View。那么什么是Looper呢?我们一起来深入的看一下Handler背后的东西。

Handler机制会涉及到以下几个概念:

  • Message

Message封装了线程中传递的消息,也就是传递给handler的数据。

对于Message对象,一般并不推荐直接使用它的构造方法得到,而是建议通过使用Message.obtain()这个静态的方法或者Handler.obtainMessage()获取。Message.obtain()会从消息池中获取一个Message对象,如果消息池中是空的,才会使用构造方法实例化一个新Message,这样有利于消息资源的利用。并不需要担心消息池中的消息过多,它是有上限的,上限为10个。

另外需要注意的是如果传递的数据不多的话,,请优先使用Message自带的属性传值,这比用Bundle更省内存,Message自带的有如下几个属性:

int arg1:参数一,用于传递不复杂的整型数据。
int arg2:参数二,用于传递不复杂的整型数据。
Object obj:传递一个任意的对象。
int what:定义的消息码,一般用于设定消息的标志。

  • MessageQueue

顾名思义,MessageQueue就是一个Message队列,也就是将从外部传递进来的message依次放到一个队列里,等待处理。

  • Looper

Looper是整个Handler框架中的‘指挥官’,它负责维护上面所说的MessageQueue的创建以及调度,也就是从MesageQueue里面依次取出里面的Message,然后交给Handler处理。

下面一起来看看android源码中的Looper调度处理MessageQueue的函数框架:

public static final void loop() {
    MessageQueue queue = me.mQueue;  //得到当前looper的MQ

    // 无限循环用于依次处理message
    while (true) {
        Message msg = queue.next(); // 取出message
        if (msg != null) {
            if (msg.target == null) {
                // message没有指定的handler作为worker,则退出循环
                return;
            }
            // 将真正的处理工作交给message的target,即后面要讲的handler
            msg.target.dispatchMessage(msg);

            // 回收message资源
            msg.recycle();
        }
    }
}

需要注意的是,一个线程只能对应一个Looper。可以通过loop thread的getLooper()方法来获取到looper,或者通过Looper.myLooper()得到当前线程的looper。

  • Handler

Handler有两个主要作用。第一,向Looper thread发送Message,进而放到MessageQueue中去。第二,当looper从MessageQueue中取出一个messge后,handler作为message的执行者,去处理message中的需求。

Handler中,与Message发送消息相关的方法有:

post(Runnable); 
postAtTime(Runnable, long); 
postDelayed(Runnable, long); 
sendEmptyMessage(int);
sendMessage(Message); 
sendMessageAtTime(Message, long);
sendMessageDelayed(Message, long)

void removeMessage():从消息队列中移除一个未响应的消息。

HandlerThread

默认来讲,Handler会执行在创建它的线程之中,但是你可以在创建Handler的时候指定让它执行在其他的线程中。

你可以自己实现一个带Looper的Thread,仅仅需要两个关键点:Looper.prepare() 以及 Looper.loop(). 通过Looper.prepare初始化好消息队列后调用Looper.loop进入消息循环了,然后我们就可以向消息队列发送消息,消息循环就会取出消息进行处理。示例代码如下:

public class MyLooperThread extends Thread {
    @Override
    public void run() {
        // 将当前线程初始化为Looper线程
        Looper.prepare();

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

        // 开始循环处理消息队列
        Looper.loop();
       }
}

不过我推荐你用HandlerThread,它已经集成好了一个Looper,并且将setup的工作已经替我们做好了。

好了,基于前面讲了这么多的理论基础,一起来看看如何构造并且使用HandlerThread:

// 首先定义一个Hanler的callback,用来接收处理message
Handler.Callback callback = new Handler.Callback() {
    @Override 
    public boolean handleMessage(Message msg) {
        // 处理message
        ... ...
    } 
};

// 构建HandlerThread,并启动
HandlerThread ht = new HandlerThread("MyHandlerThread");
ht.start();


// 创建handler,并指定其和HandlerThread绑定在一起,也及时运行在后台线程中
mHtHandler = new Handler(ht.getLooper(), callback);
mUiHandler = new Handler(callback);

在HandlerThread的外面向其发送消息:

mHtHandler.sendEmptyMessageDelayed(0, 3000);

不需要时停止HandlerThread:

mHtHandler.quit();

Handler应用场景引伸

hanlder是一个被开发者严重低估的工具,大多的时候只是被后台的线程要求处理一些UI处理的需求。但是实际上,handler能做的远远不仅如此,由于它能post Runnable对象,它还能与Looper配合使用,所以可以用来实现经典的Pipeline Thread(流水线线程)模式。具体可以请参考此文
Android Guts: Intro to Loopers and Handlers

Reference