Handler 与异步消息的源码解析

转载请注明出处:blog.csdn.net/wl9739/arti…

在上一篇 Handler 与异步消息处理 中,简单介绍了 Handler 的使用场景和常用方法,并且强调了 Handler 必须和 Looper 相关联才能使用 这一观点。由于 Looper 的构造方法是私有的,创建一个 Looper 的方法是调用 Looper.prepare(),如果当前线程中已经有了一个 Looper,那么调用这个方法就会抛出异常,说 “Only one Looper may be created per thread”。那么如何判断一个线程(注意是线程)里面有多个 Looper 呢?那么今天的主题就从这里开始。

ThreadLocal 和 Looper

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

这个方法里面最重要的无疑就是 sThreadLocal 对象的 get 和 set 方法了,而刚才我们说的那个异常信息,就是通过 sThreadLocal.get() 的返回值进行判断,sThreadLocal 是 ThreadLocal 的对象,要明白”如何在一个线程中只保留一份 Looper”,就需要看看 ThreadLocal 的内部结构是什么,为了明白 ThreadLocal 的内部结构,我们先从它的 set 方法入手,set 方法的源码如下:

public void set(T value) {
    Thread t = Thread.currentThread();
    ThreadLocalMap map = getMap(t);
    if (map != null)
        map.set(this, value);
    else
        createMap(t, value);
}

这个方法里面出现了一个 ThreadLocalMap 静态内部类,该类其实就是一个自定义的 HashMap,将当前 ThreadLocal 对象作为 key,传入的泛型参数值作为 value,这样就达到了“在线程作用域下的变量存储”的效果。而 createMap 方法也只是 new 了一个 ThreadLocalMap 对象,关于 ThreadLocalMap 里面的结构这里就不多谈了,大家可以根据 HashMap 的实现原理来类比,后面有机会也会单独写篇文章分析 HashMap 的实现。

注意,Thread 类里面是包含有 ThreadLocal.ThreadLocalMap 的对象 threadLocals,getMap(Thread t) 方法正是通过 t.threadLocals 来获取 ThreadLocalMap 对象的。

LocalThread 的 get 方法也很简单,代码如下:

public T get() {
    Thread t = Thread.currentThread();
    ThreadLocalMap map = getMap(t);
    if (map != null) {
        ThreadLocalMap.Entry e = map.getEntry(this);
        if (e != null)
            return (T)e.value;
    }
    return setInitialValue();
}

这里面同样获取了当前线程对象,根据线程对象来获取一个 ThreadLocalMap,然后获取我们存储的值,如果 map 不存在或者 ThreadLocalMap.Entry 为 null,则调用 setInitialValue() 方法,该方法其实就是将 T 初始化为 null,然后创建一个 ThreadLocalMap 对象,并将 ThreadLocal 对象和 T 的值(null)传入。

那么回到 Looper.prepare() 方法,sThreadLocal.get() 的意思就是根据当前线程对象来获取一个 ThreadLocal 对象。如果为 null 则抛出异常,如果不为 null,则调用 set 方法,先创建一个 Looper 对象,并将该 Looper 对象传入 set 方法中,这样就在当前线程中保存了一份该 Looper 对象。

Looper 对象创建完成之后,我们会调用 Looper.loop() 这个方法来启动 Looper 的循环,loop 方法的源码如下:

public static void loop() {
    // myLooper() 方法其实就是调用 sThreadLocal.get() 方法,获取当前线程中的 Looper 对象。
    final Looper me = myLooper();
    // 做一次检查,判断 Looper 对象是否为 nul。
    if (me == null) {
        throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");
    }
    // 从 Looper 对象中获取 MessageQueue
    final MessageQueue queue = me.mQueue;
    ......

    // 一个好可怕的死循环!!!
    for (;;) {
        // 从 MessageQueue 中获取 Message 对象,由于 MessageQueue 的 next() 也是一个死循环,所以会造成阻塞。
        Message msg = queue.next(); // might block
        if (msg == null) {
            // No message indicates that the message queue is quitting.
            return;
        }
        ......
        try {
            // msg.target 其实就是 Handler 对象,这里就把 Message 返回到 Handler 进行处理。 
            msg.target.dispatchMessage(msg);
        } finally {
            if (traceTag != 0) {
                Trace.traceEnd(traceTag);
            }
        }
        ......
        // 回收 Message 对象,因此通过 Message msg = Message.obtain() 或 Handler.obtainMessage() 来获取一个 Message 对象比使用 new Mesage() 更高效。
        msg.recycleUnchecked();
    }
}

上面将源码进行了简单的注释,解释了 loop 方法里面的执行流程。大概就是先获取到 MessageQueue 中的 Message 对象,如果 Message 对象不为 null,则调用 dispatchMessage 方法将其交与 Handler 处理,并回收 Message 对象。

而 dispatchMessage 方法源码如下:

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 对象,获取 Message 的方法除了 Message.obtain(Handler h) 外还有一个 Message.obtain(Handler h, Runnable callback) 方法。也就是说,如果我们使用 Message.obtain(Handler h) 方法创建了 Message 对象,并调用 sendMessage() 方法,那么最终会返回到 Handler 的 handleMessage() 方法中处理消息,而如果我们使用的是 Message.obtain(Handler h, Runnable callback) 方法创建的 Message 对象,并且 callback 不为 null,那么最终会调用 Handler 的 handleCallback 方法来处理消息,也就是执行 callback 的 run 方法。

Looper 里面的相关流程就基本介绍完了,可能有童鞋会对 loop 方法有疑问:loop 中的死循环难道不会阻塞主线程么?对于这个问题,知乎上面有一个非常好的回答:Android中为什么主线程不会因为Looper.loop()里的死循环卡死?

MessageQueue 和 Handler

说完了 Looper,下面该说说 MessageQueue 了,毕竟在 loop 里面,Message 对象是通过 MessageQueue 获取的。MessageQueue 是一个由单链表实现的先入先出队列,既然是一个队列,那么插入和取出是最主要的操作了。在 MessageQueue 当中,插入方法是 enqueueMessage,而取出方法使 next。

由于之前 Looper.loop 中出现了 next 方法,那我们就先看一下这个方法的源码:

Message next() {
    ......
    for (;;) {
        ......
        synchronized (this) {
            final long now = SystemClock.uptimeMillis();
            Message prevMsg = null;
            Message msg = mMessages;
            // msg.target 其实就是 Handler。
            if (msg != null && msg.target == null) {
                // Stalled by a barrier. Find the next asynchronous message in the queue.
                do {
                    prevMsg = msg;
                    msg = msg.next;
                } while (msg != null && !msg.isAsynchronous());
            }
            if (msg != null) {
                if (now < msg.when) {
                    // Next message is not ready. Set a timeout to wake up when it is ready.
                    nextPollTimeoutMillis = (int) Math.min(msg.when - now, Integer.MAX_VALUE);
                } else {
                    // 获取 Message 对象
                    mBlocked = false;
                    if (prevMsg != null) {
                        prevMsg.next = msg.next;
                    } else {
                        mMessages = msg.next;
                    }
                    msg.next = null;
                    if (DEBUG) Log.v(TAG, "Returning message: " + msg);
                    msg.markInUse();
                    return msg;
                }
            } else {
                // No more messages.
                nextPollTimeoutMillis = -1;
            }
            ......
    }
}

MessageQueue 的 next 方法其实就是不断地从 MessageQueue 维护的链表中获取 Message 对象。那么我们再来看 :

boolean enqueueMessage(Message msg, long when) {
    ......
    synchronized (this) {
        ......
        msg.markInUse();
        msg.when = when;
        Message p = mMessages;
        boolean needWake;
        if (p == null || when == 0 || when < p.when) {
            // New head, wake up the event queue if blocked.
            msg.next = p;
            mMessages = msg;
            needWake = mBlocked;
        } else {
            needWake = mBlocked && p.target == null && msg.isAsynchronous();
            Message prev;
            for (;;) {
                prev = p;
                p = p.next;
                if (p == null || when < p.when) {
                    break;
                }
                if (needWake && p.isAsynchronous()) {
                    needWake = false;
                }
            }
            msg.next = p; // invariant: p == prev.next
            prev.next = msg;
        }
        ......
    }
    return true;
}

也是有一个死循环,不断地将 Message 追加到 MessageQueue 的链表当中。

next 方法是在 Looper.loop 方法中调用,而 enqueueMessage 方法自然就是在 Handler 中调用了。我们在使用 Handler 来发送一个消息时,无论是使用 sendMessage 还是 post(Runnable),最终都会调用到 sendMessageAtTime 这个方法,而在这个方法里面就完成了将 Message 添加到 MessageQueue 的操作:

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

而 Handler 中的 enqueueMessage 方法则是调用了 MessageQueue 中的 enqueueMessage 方法,来将 Message 添加到 MessageQueue 中:

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

至此,我们从尾到头地将 Handler 与异步消息处理机制的源码梳理了一遍。那么我们再从头到尾地简单总结一下整个流程:Handler 将 Message 对象,通过调用 enqueueMessage 方法添加到 MessageQueue 这个单链表构成的队列中,然后 Looper.loop 不断地调用 MessageQueue 的 next 方法取出 Message 对象,并将其返还给 Handler 处理,或者调用 Message.callback 的 run 方法执行任务。

    原文作者:HashMap源码分析
    原文地址: https://juejin.im/entry/58abfa200ce463006b1a77d5
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞