读Handler 源码之浅析

来源:互联网 发布:网页预览pdf的js插件 编辑:程序博客网 时间:2024/05/29 03:32

        Handler的东西网上的资料一大堆,自己也看了不少,相关的知识点也已经很熟悉了。但是总感觉少点什么,没错,就是源码。好好的阅读一下源码对自己的成长还是很有好处的。

       本文按照如下的几个方面来阅读源码:

        1)Looper.prepare();

              new Handler(...);

              Looper.loop();

        这背后都做了哪些工作?

        2)消息的发送

        3)消息的分发

        首先看第一个问题:在非UI线程如果想要使用Handler,就必须添加Looper.prepare();和Looper.loop();这两段代码。这两段代码做了啥?请看源码:

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

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

        关于ThreadLocal的资料网上一大堆,自己可以学习一下,说白了ThreadLocal可以为当前的线程保存一个单独的副本。如上述源码,我们可以看到prepare为我们做了如下的工作:

        1、判断当前线程是不是已经有Looper了,如果有则抛出异常;

        2、将Looper保存到当前的线程ThreadLocal.ThreadLocalMap中;

        除此之外,生成Looper对象的时候也生成了消息队列,这个消息队列就是消息的存放、遍历的数据结构。

        接着看new Handler(...)的源码,只摘取比较重要的部分:

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;
        首先,从当前线程取出Looper,拿到了Looper就拿到了MessageQueue,至于mCallBack则是Handler暴露给我们的一个接口,可以作为初始化Handler的一个参数。到目前为止,Handler拿到了Looper、MessageQueue这两个最重要的东西了。

      最后,Looper.Loop()的源码在(2)(3)之后在说明,Looper.Loop()的作用就是做好了从MessageQueue的准备,并在MessageQueue有消息的时候,取出消息并交给Handler处理 。

      万事俱备,现在可以发送消息了,消息的发送最终都是调用的如下的代码:

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);}
        这个mQueue是什么呢?就是Looper为我们准备的消息队列,有了这个队列,我们就可以将发送的消息排队存放到这个消息队列,等待Hanlder处理了。

        好了,现在消息也发送了,并且也保存到了消息队列了,怎么取呢?这就是Looper.Loop()的工作了。看Loop()方法的源码(紧摘取关键的部分):

for (;;) {    Message msg = queue.next(); // might block    if (msg == null) {        // No message indicates that the message queue is quitting.        return;    }    // This must be in a local variable, in case a UI event sets the logger    final Printer logging = me.mLogging;    if (logging != null) {        logging.println(">>>>> Dispatching to " + msg.target + " " +                msg.callback + ": " + msg.what);    }    final long traceTag = me.mTraceTag;    if (traceTag != 0) {        Trace.traceBegin(traceTag, msg.target.getTraceName(msg));    }    try {        msg.target.dispatchMessage(msg);    } finally {        if (traceTag != 0) {            Trace.traceEnd(traceTag);        }    }

        我们可以看到,Loop()方法主要是一个for(;;),这意味Looper会不断的遍历MessageQueue,只要有消息就会取出来(Message msg = queue.next()),然后交给Hanlder进行处理( msg.target.dispatchMessage(msg);)。问题来了,我们怎么知道要交给哪个Handler来处理呢?这就要看消息是怎么被放到MessageQueue了,如下所示:

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

        最关键的是第一行:msg.target = this。这个this是什么呢?没错,就是handler。

        好了,知道将消息交给谁处理了,但是到底是怎么处理的呢?其实关键点就是: msg.target.dispatchMessage(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是什么呢?还记得handler的post方法吧?这个post的参数是一个Runnable,这个callback就是我们post的Runnable。其次,mCallback是什么呢?其实就是我们在Handler构造函数中传递的CallBack接口。最后,handleMessage就是我们普通的Message了。

        ----------------------------------------------------------------------------------------------

        关于Looper还需注意:在子线程中,如果手动的为其创建Looper,那么所有的事情处理完毕之后,应该调用quit方法来终止消息循环,否则子线程就会一直处于等待状态,如果退出Looper,那么子线程就会立即终止,因此建议在不需要的时候终止Looper

0 0
原创粉丝点击