生动形象理解Handler源码

我知道关于Handler的文章在网上已经快被写烂了/笑着哭,但是还是想写出来,我觉得只有自己亲自写过这些东西,才会更深入的理解这些。
虽然以前也写过Handler,但是当时刚刚接触Handler的源码,所以写的像屎一样。。。。(在简书上写的,不忍直视)。现在本人自我感觉关于Handler已经有个相对有一点自己见解了,所以今天会更加细致的写这篇博客。
好了不多说了,开始我们今天的正题:

这个东西如果直接从源码讲起来会很乱,所以今天我们先从我们接触到的一些类和方法开始讲。


Handler使用(简述)

关于Handler的使用这里就不多说了,相信在看这篇文章的时候大家已经把Handler用烂了。简述一下调用的几个方法:
handler.postXXX(Runnable,。。。。);

还有handler.sendXXX(。。。。);这是我们使用Handler的两类方法,大家应该都会用的,不用多说。

Handler的构造方法中出现的相关方法

我们使用Handler,首先要有一个Handler的对象,通常我们最简单的方法,就是直接new ,然后重写handlerMessage方法:

Handler handler = new Handler(){
    @Override
    public void handleMessage(Message msg) {
        super.handleMessage(msg);
    }
};

当然也可以在参数里面加上Callback回调来用(个人喜欢这种,因为第一种warning是一坨黄色的,看着很烦-。+):

Handler handler = new Handler(new Handler.Callback() {
    @Override
    public boolean handleMessage(Message msg) {
        return false;
    }
});

先进入Handler的第一个构造方法:

public Handler() {
    this(null, false);
}

这里面的两个参数,我们在Android Studio中可以发现,第一个就是我们的Callback,第二个是说是否异步(我们不管他这个东西)。

点进this看一下:

public Handler(Callback callback, boolean async) {
    ......

    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;
    mAsynchronous = async;
}

我省略掉了上面的一些代码,只要看一下这一段就好:
这里唯一一个比较陌生的就是Looper,其他的都能看懂。其实应该也不算陌生:因为在学Handler时候,各个书上差不多都有一个简述Handler的工作原理,里面都有提到过Looper,这个Looper称为轮询器,负责不停地轮询,把我们的消息取出来,然后执行。这只是大概工作原理,其实如果真的吃透Handler机制会发现,真正的轮询另有其人(下面会提到)。

既然他先出来的,那么我们先看一下mLooper方法:

/**
 * Return the Looper object associated with the current thread.  Returns
 * null if the calling thread is not associated with a Looper.
 */
public static @Nullable Looper myLooper() {
    return sThreadLocal.get();
}

这个方法返回了和当前线程关联的looper对象。
先说一下这个sThreadLocal:
// sThreadLocal.get() will return null unless you've called prepare().
static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();


这是在Looper中的一个静态引用,这么看来我们所有的Looper都要共享这个mThreadLocal了,我们可以把这个mThreadLocal理解为一个大箱子,里面放着很多的Looper对象。上面用到了ThreadLocal的get方法,我们看一下:

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

他返回值是当前线程的某个值,现在我们把泛型指定为Looper ,所以他肯定是拿到了当前线程的Looper,这个很容易理解。接着我们简单说一下下面出现的这几个类:
  • ThreadLocalMap
  • Entry
ThreadLocalMap是ThreadLocal的一个静态内部类,而Entry又是ThreadLocalMap的一个静态内部类,(个人感觉他们是组合关系,大家有不同意的可以评论交流一下)

首先通过getMap方法拿到map对象,我们看一下getMap方法:

ThreadLocalMap getMap(Thread t) {
    return t.threadLocals;
}

是返回了Thread中的一个属性,这样看来Thread中有ThreadLocalMap的引用。Thread和ThreadLocalMap可以关联。

在上述代码中我们注意到最后是从Entry e中拿到了value作为返回值返回,这么推理是Entry中存储了我们的Looper。我们看getEntry方法:

private Entry getEntry(ThreadLocal<?> key) {
    int i = key.threadLocalHashCode & (table.length - 1);
    Entry e = table[i];
    if (e != null && e.get() == key)
        return e;
    else
        return getEntryAfterMiss(key, i, e);
}

table是ThreadLocalMap的一个属性:

private Entry[] table;

我们通过table拿到Entry对象,然后返回这个e。(在一般不出意外的情况下)

现在我们跳回到上面的get方法中,目前我们已经拿到了e对象,如果不为空会返回e存储的值(value):

if (e != null) {
    @SuppressWarnings("unchecked")
    T result = (T)e.value;
    return result;
}

这是我们myLooper的方法。简单总结一下:

我们调用了sThreadLocal的get方法,在get方法中首先获取到当前线程(Thread.currentThread()),然后拿到对应线程的ThreadLocalMap对象(getMap),最后通过ThreadLocalMap拿到Entry对象(getEntry)返回e的value值。


我们在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;
    mAsynchronous = async;

跳回我们Handler构造方法:在获取到looper对象之后,我们就是给Handler的属性赋值,这里又出现了一个新的类,mQueue对应的类——MessageQueue,关于这个类我们下面详细讲,现在我们只需要Handler和Looper保留了它的引用。在Handler构造方法中进行了相关属性赋值。到现在我们Handler的构造方法可以过了。

handler.sendXXX(。。。。);

上面说到Handler发送消息有两种方式,我这里就那sendMessage方法举例了(其实最好会发现,所有方法都一样-。+):

public final boolean sendMessage(Message msg)
{
    return sendMessageDelayed(msg, 0);
}

这是我们点进了sendMessage方法,我们看到他是嵌套了一个方法,我们再点进去看一下:

public final boolean sendMessageDelayed(Message msg, long delayMillis)
{
    if (delayMillis < 0) {
        delayMillis = 0;
    }
    return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);
}

又是嵌套(其实还有好多嵌套呢-。+),接着点:

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

也是嵌套,但是这个嵌套有点东西:

MessageQueue queue = mQueue;
return enqueueMessage(queue, msg, uptimeMillis);

注意这两行代码,在接下来的方法中,我们又放入和一个MessageQueue引用做参数,现在我们再点进这个方法看:

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

好了,现在又有一个Message对象,是不是一个没搞懂,又冒出来好几个的感觉,大家淡定,先分析一下他的逻辑,只有这几个类会在下面详细介绍的:

我们将msg的target属性设为this,这个方法是在Handler中的,没猜错的话target应该是Handler类型的,点进去看一下:

/*package*/ Handler target;

果然如此哈哈!
最后返回的是MessageQueue的方法了,好我们就此打住。
总结handler.sendXXX():

经过层层方法嵌套,最后进入了MessageQueue的enqueueMessage方法。


接下来我们重点看一下刚才提到的还有在Handler中几个重点了类:

Message
MessageQueue
Looper

我们分别看一下:

Message

目前主要看这几个属性就好:

/*package*/ Handler target;
/*package*/ Runnable callback;

/*package*/ long when;
// sometimes we store linked lists of these things
/*package*/ Message next;

第一个是我们刚才看到的target,我们在enqueueMessage方法中,将当前的Handler赋值给了msg的target。

callback:这个属性在我们通过post方法发送消息的时候会用到,具体大家自己分析完post方法就知道了。
when:这个是一个用作记录时刻的属性,官方解释为运行回调的绝对时间。
next:相信大家都知道链表是个什么东西,这个next就是一个指向链表下一元素的指针。

其他的我们等会儿遇到了再说,现在先知道这三个属性就好。

MessageQueue

如果没猜错的话,这个类应该才是我们最应该头疼的一个类了,我们简单看一下它的注释:

/**
 * Low-level class holding the list of messages to be dispatched by a
 * {@link Looper}.  Messages are not added directly to a MessageQueue,
 * but rather through {@link Handler} objects associated with the Looper.
 *
 * <p>You can retrieve the MessageQueue for the current thread with
 * {@link Looper#myQueue() Looper.myQueue()}.
 */

我们只看其中一句很重要的:Messages are not added directly to a MessageQueue:我们的Message不是直接储存到MessageQueue,这是神魔意思?我查找了一下MessageQueue的所有属性,顾名思义,如果MessageQueue是用来存储Message的,那么首先肯定有用一个集合相关的属性,来存储Message,可是我看完了所有的属性,。。。。。。真的没有!

但是我们发现了熟悉的身影:mMessage属性,他是Message类。还记得我们上面提到过的Message.next属性吗?我们是不是可以大胆地猜测一下:MessageQueue中放置了一个Message链表的头结点,然后通过Message元素自身添加和删除,实现Message的链式存储。(目前这个只是咱们的猜测)
刚才在Handler.sendXXX()中,最后跳转到的就是MessageQueue的enQueueMessage方法,我们来看一下这个方法(前方高能!):

MessageQueue.enqueueMessage

    boolean enqueueMessage(Message msg, long when) {
        ......

        synchronized (this) {
            ......

         
            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.
                //原来mMessages为链表头,现在让msg变成了链表头,原来的mMessagges引用的Message对象在msg的后面。
                msg.next = p;
                mMessages = msg;
                needWake = mBlocked;
            } else {
                ......

                /*
                * msg不是新的头,所以要按照时间顺序排列链表,查找对应的位置*/
                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;
    }

为了广大的读者,我已经删除了一部分无用的代码,但是还看着很多。我们逆着想一下:我们handler.sendXXX方法是把我的Message对象放入了队列中,所以最后跳转到的这个方法应该是一个存储Message的过程,首先我们有着这样的一个总体思路,然后接着看:

首先我们下面所有的代码都写入了同步锁锁住的代码块中,首先给传入msg的when属性赋值,新建了一个Message引用等于mMessages。
我们的进行了一个简单的if判断,无论链表为空,还是绝对时刻为0,或者是新插入的msg时刻比头结点的时刻小(简单理解是在它之前),这三种情况只要满足其中一种,就把新插入的msg设为新的头结点。这三行代码就是指定新的头结点,自己看去。。。
如果这三种情况都不是,那么就要查找msg应该对应的位置,标准就是绝对时刻(when):通过遍历队列,直到找到对应位置,如果到最后还没有对应的位置,说明他的绝对时刻最大,所以放到最后。
最后返回true。
这是我们enqueueMessage方法的过程。对应代码上又备注。
MessageQueue还有一个很重要的方法:next()(至于用处一会儿回用到):
MessageQueue.next():
    Message next() {
        ......
        for (;;) {
            ......

            synchronized (this) {
                // Try to retrieve the next message.  Return if found.
                final long now = SystemClock.uptimeMillis();
                Message prevMsg = null;
                Message msg = mMessages;
                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 {
                        // Got a 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;
                }

                // Process the quit message now that all pending messages have been handled.
                if (mQuitting) {
                    dispose();
                    return null;
                }

                ......
            }

            ......

            ......
        }
    }

又删除了百分之80的垃圾代码-。+。刚才我们说enqueueMessage方法是把Message放入,那么next方法就是将Message取出的。我们来看一下:
    do {
        prevMsg = msg;
        msg = msg.next;
    } while (msg != null && !msg.isAsynchronous());

我们先看这一段代码:首先新建了prevMsg,一个新的Message引用,然msg指向头结点。

接着把preMsg和msg移动到同步Message位置(因为这段程序整个都在同步锁中,异步的Message不受限制,这段话可以当做放屁-。+)。

    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 {
            // Got a 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;
        }
    }

这段是将msg取出,然后将链表断开位置重新连接。(详细逻辑如上,大家自己看)


Looper

这个类是我们需要说的最后一个类了,感觉很累。。

我们知道如果在子线程中使用Handler,我们还需要做额外的两件事:Looper.prepare()和Looper.loop();这两个方法有什么作用呢?我们来看一下:

Looper.prepare()

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

相信到这里大家看的应该比较轻松了,因为几乎都是我们刚刚了解的方法:
首先进行判断,在sThreadLocal中是否存在当前线程的looper对象,在初次创建时候,是没有的。
然后创建一个新的Looper对象,放入sThreadLocal中。
我们看一下set方法:
Looper.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);
    }


逻辑很清晰:
  1. 获取当前线程对象。
  2. 获取线程的ThreadLocalMap对象。
  3. set方法添加Looper对象。
我们来看一下这个set方法:

ThreadLocalMap.set(。。。):

        private void set(ThreadLocal<?> key, Object value) {

            // We don't use a fast path as with get() because it is at
            // least as common to use set() to create new entries as
            // it is to replace existing ones, in which case, a fast
            // path would fail more often than not.

            Entry[] tab = table;
            int len = tab.length;
            int i = key.threadLocalHashCode & (len-1);

            for (Entry e = tab[i];
                 e != null;
                 e = tab[i = nextIndex(i, len)]) {
                ThreadLocal<?> k = e.get();

                if (k == key) {
                    e.value = value;
                    return;
                }

                if (k == null) {
                    replaceStaleEntry(key, value, i);
                    return;
                }
            }

            tab[i] = new Entry(key, value);
            int sz = ++size;
            if (!cleanSomeSlots(i, sz) && sz >= threshold)
                rehash();
        }

这个不怎么重要,我们只需要知道最后我们把value添加给了Entry[]  table就好。
Looper.loop():
    public static void loop() {
        final Looper me = myLooper();
        ......
        final MessageQueue queue = me.mQueue;

        ......

        for (;;) {
            Message msg = queue.next(); // might block
            if (msg == null) {
                // No message indicates that the message queue is quitting.
                return;
            }

            ......
            ......
            final long start = (slowDispatchThresholdMs == 0) ? 0 : SystemClock.uptimeMillis();
            final long end;
            try {
                msg.target.dispatchMessage(msg);
                end = (slowDispatchThresholdMs == 0) ? 0 : SystemClock.uptimeMillis();
            } finally {
                if (traceTag != 0) {
                    Trace.traceEnd(traceTag);
                }
            }
            ......

        }
    }


loop方法是用来轮询的的,在这里我们也看到了一个for死循环,我们看一下方法逻辑:
  1. 获取当前线程的looper对象。
  2. 进入死循环,不断获取当前线程的消息队列中的Message对象。
  3. 分发:msg.target.dispatchMessage(msg);
如果有消息,我们会通知msg对应的handler执行dispatchMessage方法。我们一起看一下这个方法:

handler.dispatchMessage(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(这个是post发送消息最后的回调)
  • mCallback接口执行handleMessage方法(这个是我们在构造Handler时候添加和Callback情况下的回调)。
  • handleMessage(这个是我们重写和Handler.handleMessage情况下的回调)。
相信这几行代码大家已经很亲切了。

讲到这里,相信大家对我们Handler的运行机制,心里已经透彻了一些,但是还没完,我们要知道我们在主线程中(MainActivity中的实例变量为例),他就不用调用Looper.prepare和Looper.loop方法,我们看一下ActivityThread的main方法就一目了然了:

    public static void main(String[] args) {
        ......

        Looper.prepareMainLooper();

        ActivityThread thread = new ActivityThread();
        thread.attach(false);

        if (sMainThreadHandler == null) {
            sMainThreadHandler = thread.getHandler();
        }

        if (false) {
            Looper.myLooper().setMessageLogging(new
                    LogPrinter(Log.DEBUG, "ActivityThread"));
        }

        ......
        Looper.loop();

        throw new RuntimeException("Main thread loop unexpectedly exited");
    }

我只留下了这两个方法,和最后的异常:我们在运行之初,就已经给主线程进行了prepare(prepareMainLooper本质还是调用prepare,大家下去自己去看)和loop方法,所以说一旦loop方法调用之后,不会终止,这是一个真的无效轮询过程。否则就会抛出如上异常。

大家还记得我之前说的吗:相比looper,真正轮询的工作另有其人。可能现在这么说已经不太对了。就目前来看,我们在运行时有两个无线轮询:loop方法中和next方法中。如果说他们都是轮询的工作的话,那么looper是进行着不同线程之间的轮询(通过获取当前线程来获取当前轮询器,调用对应的消息队列进行消息轮询),而MessageQueue的轮询,是当前线程的消息轮询(如上所述-。=)。

Handler机制总结:

非主线程情况,需要调用Looper.prepaore和Looper.loop方法:

Looper.prepare方法给当前线程绑定一个Looper对象(本质是Thread中有ThreadLocalMap引用,而Looper中也有Thread引用,从而实现了双向关联)。Looper.loop方法进入无限轮询,不断地调用当前线程的Looper对象的mQueue对象的next方法(有点拗口)。当获取到消息时,就分发给对应的handler去执行(通过三种回调,具体看上面)

几张很有用的图-。+:

这么多文字,相信大家已经看得想吐了,最后给大家带来几张图吧。
这张图对Handler的轮询机制是很形象,但是希望大家不要吐槽我的画图能力-。+:

传送带,电池。。。。都是大家能看懂的东西,领会精神哈!

这个是刚才我们Looper相关类的类图。

最后一张送上涵盖我们上述的主要的类的一张类图。(刚学UML,画的可能有点迷,见谅!!)。

喜欢的朋友希望关注一下,如果有不同的观点或者指出错误欢迎下方留言。


  • 2
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值