Android中的MessageQueue,Handler,Looper和Thread

来源:互联网 发布:剑灵天族美女捏脸数据 编辑:程序博客网 时间:2024/04/28 11:35

http://hi.baidu.com/wangyu8702/blog/item/dfdda8448b505999b2b7dc1b.html


前几天和一位同事讨论Android中Handler和Thread,其中一个问题是:创建Handler时会不会创建Thread?

我对JAVA编程不熟,但直觉告诉我不会:我认为Handler只是用来辅助实现异步操作的东西。当时我拿了GTK+中的idle来做对比,sendMessage就相当于加一个idle函数,系统处理完前面的Message后就会处理这个Message。毕竟没有看过里面的实现代码,所以当时并不确信。今天看了下:

MessageQueue

消息队列MessageQueue是一个以执行时间为序的优先级队列:

o 普通消息的执行为当前时间,先发送的前面,后发送在后面,这是典型的FIFO。

o 最高优先级的消息执行时间为0,所以直接插在队列的最前面,通常会立即执行。

o 而在将来执行的Message相当于timer,执行时间为当前时间+delay的时间。

MessageQueue的函数boolean enqueueMessage(Message msg, long when)用来向队列中插入消息。

Message和GTK+ idle不同之处在于,Message只是一段数据,里面说明了要做什么,但不并知道如何做。而idle带有自己的数据和处理函数,它知道如何做。Message放入队列中后,在处理这些消息时到底要做些什么呢?这就引入了Handler:

Handler

Handler 对消息队列的enqueueMessage做了包装,这其实并不重要,因为完全可以直接调用enqueueMessage来实现。重要的Handler把在包装enqueueMessage的同时,把Message的target设成了自己,即为Message指定执行的行为:

if (queue != null) {            msg.target = this;            sent = queue.enqueueMessage(msg, uptimeMillis);        }

这样一来,当前Message被处理的时候就会调用Handler的dispatchMessage,而这个函数就会调用你要实现的虚函数handleMessage了。经过消息队列转了一圈,还是调用了你自己的实现函数,但是同步操作变成了异步操作。


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

Looper

Message放在消息队列里了,谁来一个一个的取出来处理呢?这时轮到Looper上场了,它的函数loop会一直循环处理队列中的消息,直到遇上一个没有target的Message:

public static final void loop() {        Looper me = myLooper();        MessageQueue queue = me.mQueue;        while (true) {            Message msg = queue.next(); // might block            //if (!me.mRun) {            //    break;            //}            if (msg != null) {                if (msg.target == null) {                    // No target is a magic identifier for the quit message.                    return;                }                if (me.mLogging!= null) me.mLogging.println(                        ">>>>> Dispatching to " + msg.target + " "                        + msg.callback + ": " + msg.what                        );                msg.target.dispatchMessage(msg);                if (me.mLogging!= null) me.mLogging.println(                        "<<<<< Finished to    " + msg.target + " "                        + msg.callback);                msg.recycle();            }        }    }

由此可见:一个Looper总是和一个MessageQueue关联起来的。

Thread

loop只是一个函数,它也需要别人来执行它。由于它一执行就会阻塞在那里,所以一定需要一个线程来调用:


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

一个Looper也总是和一个Thread关联起来的,不过不一定要创建新线程,可以是主线程的。Handler和Thread不是一一对应的,理论上,在一个LooperThread中,可以有任何多个Handler,每个消息都可以指定不同的Handler,因为每个消息都可以有不同的行为。

在创建Handler时并不会创建Thread,它只是取当前线程的Looper的MessageQueue:


public Handler() {        if (FIND_POTENTIAL_LEAKS) {            final Class<? extends Handler> klass = getClass();            if ((klass.isAnonymousClass() || klass.isMemberClass() || klass.isLocalClass()) &&                    (klass.getModifiers() & Modifier.STATIC) == 0) {                Log.w(TAG, "The following Handler class should be static or leaks might occur: " +                    klass.getCanonicalName());            }        }         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 = null;    }

通过myLooper取出当前线程的Looper:


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

这个Looper是在Looper.prepare里创建的:


public static final void prepare() {        if (sThreadLocal.get() != null) {            throw new RuntimeException("Only one Looper may be created per thread");        }        sThreadLocal.set(new Looper());    }