Handler
的原理分析这个标题,不少文章都写过,最近认真将源码逐行一字一句研究,特此也简单总结一遍。java
首先是Handler
整个Android消息机制的简单归纳:android
分三部分对消息机制的整个流程进行阐述:git
Handler
的建立,包括Looper
、MessageQueue
的建立;Handler
发送消息,Message
是如何进入消息队列MessageQueue
的(入列);Looper
轮询消息,Message
出列,Handler
处理消息。// 最简单的建立方式 public Handler() { this(null, false); } // ....还有不少种方式,但这些方式最终都执行这个构造方法 public Handler(Callback callback, boolean async) { // 1.检查内存泄漏 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()); } } // 2.经过Looper.myLooper()获取当前线程的Looper对象 mLooper = Looper.myLooper(); // 3.若是Looper为空,抛出异常 if (mLooper == null) { throw new RuntimeException( "Can't create handler inside thread " + Thread.currentThread() + " that has not called Looper.prepare()"); } mQueue = mLooper.mQueue; mCallback = callback; mAsynchronous = async; }
首先,如何避免Handler
的内存泄漏是一个很是常见的面试题,其实Handler
的源码中已经将答案很是清晰告知给了开发者,即让Handler
的导出类保证为static
的,若是须要,将Context
做为弱引用的依赖注入进来。github
同时,在Handler
建立的同时,会尝试获取当前线程惟一的Looper
对象:web
public final class Looper { static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>(); public static @Nullable Looper myLooper() { return sThreadLocal.get(); } }
关于ThreadLocal
,我在上一篇文章中已经进行了分析,如今咱们知道了ThreadLocal
保证了当前线程内有且仅有惟一的一个Looper
。面试
那就是须要调用Looper.prepare()
方法:数据结构
public final class Looper { 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(); } }
这也就说明了,为何当前线程没有Looper
的实例时,会抛出一个异常并提示开发者须要调用Looper.prepare()
方法了。app
也正如上述代码片断所描述的,若是当前线程已经有了Looper
的实例,也会抛出一个异常,提示用户每一个线程只能有一个Looper
(throw new RuntimeException("Only one Looper may be created per thread");
)。async
此外,在Looper
实例化的同时,也建立了对应的MessageQueue
,这也就说明,一个线程有且仅有一个Looper
,也仅有一个MessageQueue
。ide
sendMessage()
流程以下:
// 1.发送即时消息 public final boolean sendMessage(Message msg) { return sendMessageDelayed(msg, 0); } // 2.其实是发射一个延时为0的Message public final boolean sendMessageDelayed(Message msg, long delayMillis) { if (delayMillis < 0) { delayMillis = 0; } return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis); } // 3.将消息和延时的时间进行入列(消息队列) 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); } // 4.内部实际上仍是执行了MessageQueue的enqueueMessage()方法 private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) { msg.target = this; if (mAsynchronous) { msg.setAsynchronous(true); } return queue.enqueueMessage(msg, uptimeMillis); }
注意第四步实际上将Handler
对象最为target,附着在了Message
之上;接下来看MessageQueue
类内部是如何对Message
进行入列的。
boolean enqueueMessage(Message msg, long when) { //... 省略部分代码 synchronized (this) { msg.markInUse(); msg.when = when; // 得到链表头的Message Message p = mMessages; boolean needWake; if (p == null || when == 0 || when < p.when) { // 如有如下情景之一,将Message置于链表头 // 1.头部Message为空,链表为空 // 2.消息为即时Message // 3.头部Message的时间戳大于最新Message的时间戳 msg.next = p; mMessages = msg; needWake = mBlocked; } else { // 反之,将Message插入到链表对应的位置 Message prev; // for循环就是找到合适的位置,并将最新的Message插入链表 for (;;) { prev = p; p = p.next; if (p == null || when < p.when) { break; } } msg.next = p; // invariant: p == prev.next prev.next = msg; } if (needWake) { nativeWake(mPtr); } } return true; }
MessageQueue
的数据结构自己是一个单向链表。
当Handler
建立好后,若在此以前调用了Looper.prepare()
初始化Looper
,还须要调用Looper.loop()
开始该线程内的消息轮询。
public static void loop() { // ...省略部分代码 // 1. 获取Looper对象 final Looper me = myLooper(); if (me == null) { throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread."); } // 2.获取messageQueue final MessageQueue queue = me.mQueue; // 3. 轮询消息,这里是一个死循环 for (;;) { // 4.从消息队列中取出消息,若消息队列为空,则阻塞线程 Message msg = queue.next(); if (msg == null) { return; } // 5.派发消息到对应的Handler msg.target.dispatchMessage(msg); // ... } }
比较简单,须要注意的一点是MessageQueue.next()
是一个可能会阻塞线程的方法,当有消息时会轮询处理消息,但若是消息队列中没有消息,则会阻塞线程。
private native void nativePollOnce(long ptr, int timeoutMillis); Message next() { // ...省略部分代码 int nextPollTimeoutMillis = 0; for (;;) { // ... // native方法 nativePollOnce(ptr, nextPollTimeoutMillis); synchronized (this) { final long now = SystemClock.uptimeMillis(); Message prevMsg = null; Message msg = mMessages; // 从消息队列中取出消息 if (msg != null) { // 当时间小于message的时间戳时,获取时间差 if (now < msg.when) { // 该值将会致使在下次循环中阻塞对应时间 nextPollTimeoutMillis = (int) Math.min(msg.when - now, Integer.MAX_VALUE); } else { // 取出消息并返回 mBlocked = false; if (prevMsg != null) { prevMsg.next = msg.next; } else { mMessages = msg.next; } msg.next = null; msg.markInUse(); return msg; } } // ... } }
注意代码片断最上方的native方法——循环体内首先调用nativePollOnce(ptr, nextPollTimeoutMillis)
,这是一个native方法,实际做用就是经过Native层的MessageQueue
阻塞nextPollTimeoutMillis
毫秒的时间:
搞清楚这一点,其它就都好理解了。
正如上文所说的,msg.target.dispatchMessage(msg)
实际上就是调用Handler.dispatchMessage(msg)
,内部最终也是执行了Handler.handleMessage()
回调:
public void dispatchMessage(Message msg) { if (msg.callback != null) { handleCallback(msg); } else { if (mCallback != null) { if (mCallback.handleMessage(msg)) { return; } } // 若是消息没有定义callBack,或者不是经过 // Handler(Callback)的方式实例化Handler, // 最终会走到这里 handleMessage(msg); } }
Hello,我是却把清梅嗅,若是您以为文章对您有价值,欢迎 ❤️,也欢迎关注个人博客或者Github。
若是您以为文章还差了那么点东西,也请经过关注督促我写出更好的文章——万一哪天我进步了呢?