我知道关于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
首先通过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中几个重点了类:
MessageMessageQueue
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); }
逻辑很清晰:
- 获取当前线程对象。
- 获取线程的ThreadLocalMap对象。
- set方法添加Looper对象。
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死循环,我们看一下方法逻辑:
- 获取当前线程的looper对象。
- 进入死循环,不断获取当前线程的消息队列中的Message对象。
- 分发:msg.target.dispatchMessage(msg);
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,画的可能有点迷,见谅!!)。
喜欢的朋友希望关注一下,如果有不同的观点或者指出错误欢迎下方留言。