吐槽:想起大约一周前的一个夜晚,我正在好好地学习。突然之间。。。胸口一阵疼痛,然后就是一直疼,并且随着深呼吸会更加剧痛。我。。。。做错了什么???难道嫉妒我这么爱学习就要猝死了???怎么能这样!!!那天晚上,我忍着痛入眠,第二天醒来,疼痛依然伴随吾身。。。去医院。。。一顿拍片检查操作之后,医生:“嗯,你肺里进空气了。”我(满脸问号):“???”,我肺里没空气我怎么活的?医生(一脸平静):“肺泡破了吧,空气进入胸腔了,压缩了肺部”。。。。好吧,是。得,想我一不运动,二不高又不瘦,我会得这种病也是**奇了怪了。然后就是。。每天吸氧、拍片复查。。重复重复。。。直到现在,我还是不能理解,,,为什么我会得这种病???
零零碎碎的东西总是记不长久,仅仅学习别人的文章也只是他人咀嚼后留下的残渣。无意中发现了这个,想了想如果只是简单地去思考,那么不仅会收效甚微,甚至难一点的题目自己可能都懒得去想,坚持不下来。所以不如把每一次的思考、理解以及别人的见解记录下来。不仅加深自己的理解,更要激励自己坚持下去。
Handler 机制
Handler 想必接触 Android 的都已经很熟悉了,我们通常用用于子线程与主线程的通信,并在主线程中处理相关消息,比如更改 UI 等。Handler 的消息机制为我们处理线程中的通信确实方便了许多。
基本使用
最简单的使用就是在主线程中创建 Handler 对象并重写 handlerMessage() 方法处理消息,在子线程中通过 handler 对象的 sendMessage 方法发送消息。
Handler handler = new Handler(){ @Override public void handleMessage(Message msg) { super.handleMessage(msg); // TODO write your message processing code ... } }; new Thread(){ @Override public void run() { super.run(); //TODO handling events ... handler.sendMessage(Message.obtain()); } };复制代码
为了简单示例,Handler 才这样写的。这种创建方法会造成内存泄漏,具体愿意以及解决方案在 都已经说明,这里不再赘述。
而关于 Handler 的原理,我们可能会多多少少的了解到与 Looper、Message、MessageQueue 都是离不开的,那么具体是怎麽配合呢?下面我根据消息机制的过程,结合源码一步一步的解析。
Handler 原理
创建 Handler 对象
在我们使用时,首先就是创建 Handler,那我们看一下 Handler 的构造函数都干了些什么吧。
public Handler(Callback callback, boolean async) { if (FIND_POTENTIAL_LEAKS) { final Class 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()); } } //获取本地 TLS 存储区的 Looper 对象引用 mLooper = Looper.myLooper(); if (mLooper == null) { throw new RuntimeException( "Can't create handler inside thread " + Thread.currentThread() + " that has not called Looper.prepare()"); } //使用 Looper 中的 MessageQueue mQueue = mLooper.mQueue; mCallback = callback; mAsynchronous = async;} public Handler(Looper looper, Callback callback, boolean async) { mLooper = looper; mQueue = looper.mQueue; mCallback = callback; mAsynchronous = async;}复制代码
好吧,很多构造函数,不过有用的就是这两个,其他都是调用这两个而已。从两个对比可以看出,如果你没有传入 Looper 对象,那么就会通过 Looper.myLooper() 获取。传入的话,就是用自定义的,并且 MessageQueue 一直是使用 Looper 中的 MessageQueue,所以这里出现了第两个重要结论:
- Handler 对象的创建一定需要一个 Looper 对象
- Looper 中一定有 MessageQueue
然后还有两个参数 callBack 和 async,callBack 是 Handler 内部的一个接口,内部只有一个 handleMessage() 方法,作用我们下面讲到再说。然后就是 async,这个就好理解了,就是决定是异步处理消息,还是同步处理消息,默认为同步 false;
public interface Callback { public boolean handleMessage(Message msg);}复制代码
Looper 对象的创建
上一个步骤中我们知道,Handler 是直接从 Looper.myLooper() 中获得对象的,我们具体探讨一下。
public static @Nullable Looper myLooper() { return sThreadLocal.get();}复制代码
很简单,是从一个集合中拿到的,这个 sThreadLocal,是 ThreadLocal 类的对象,代表着本地存储区(Thread Local Storage 简称 TLS),线程中的唯一 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));}复制代码
找到啦,由两个方法的权限访问修饰符可知,我们只能调用第一个,也就是说,第二个方法的参数永远是 true。再来看第二个方法,首先就是一个异常捕获,有的人可能已经很熟悉了,那就是一个线程中只能有一个 Looper。后面就是没有的话就 new 一个 Looper 对象,构造函数中都干了什么呢?
private Looper(boolean quitAllowed) { mQueue = new MessageQueue(quitAllowed); mThread = Thread.currentThread();}复制代码
也很简单,初始化了 mQueue 也就是 MessageQueue,还有就是当前所在的线程 mThread。从权限访问修饰符可以看出,这是一个私有的构造方法,所以说,我们创建 Looper 方法只有 prepare() 方法啦。
MessageQueue 对象的创建
上述 Looper 对象的创建中,new 了一个 MessageQueue 方法,我们看看都干了什么。
MessageQueue(boolean quitAllowed) { mQuitAllowed = quitAllowed; mPtr = nativeInit();}复制代码
也很简单,初始化了两个变量 mQuitAllowed 与 mPtr,第一个代表着消息队列是否可以退出,上面也说了,我们么的办法,只能是 true。第二个 mPtr,涉及到 Native 层的代码,在 native 层也做了一个初始化,具体深入了解可到此处
发送消息
创建完 Handler,下一步就是发送 Message 了,去看看源码
public final boolean sendMessage(Message msg){};public final boolean sendEmptyMessage(int what){};public final boolean sendEmptyMessageDelayed(int what, long delayMillis){};public final boolean sendEmptyMessageAtTime(int what, long uptimeMillis){};public final boolean sendMessageDelayed(Message msg, long 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);}复制代码
又是好多发送消息的,不过他们最后都调用了 sendMessageAtTime 方法。第一个参数是要发送的消息,而第二个是一个绝对时间,也就是发送消息的时间。在做了一些判断之后,调用了 enqueueMessage 方法。
private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) { //消息获得发送该消息的 Handler 对象引用 msg.target = this; //是否同步属性 if (mAsynchronous) { msg.setAsynchronous(true); } return queue.enqueueMessage(msg, uptimeMillis);}复制代码
在又做了一些 Message 的属性初始化后,调用了 queue 的 enqueueMessage 方法,而这个 queue 就是在 Looper 对象中获得的 MessageQueue 对象。下面是 MessageQueue 中的 enqueueMessage 方法
boolean enqueueMessage(Message msg, long when) { // 每一个 Message 必须有一个 target if (msg.target == null) { throw new IllegalArgumentException("Message must have a target."); } if (msg.isInUse()) { throw new IllegalStateException(msg + " This message is already in use."); } synchronized (this) { if (mQuitting) { //正在退出时,回收 msg,加入到消息池 msg.recycle(); return false; } msg.markInUse(); msg.when = when; Message p = mMessages;//mMessages 为当前消息队列的头结点 boolean needWake; if (p == null || when == 0 || when < p.when) { //p 为 null(代表 MessageQueue 没有消息) 或者 msg 的触发时间是队列中最早的, 则进入该该分支 msg.next = p; mMessages = msg; needWake = mBlocked; } else { //将消息按时间顺序插入到 MessageQueue。一般地,不需要唤醒事件队列,除非 //消息队头存在 barrier,并且同时 Message 是队列中最早的异步消息。 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; prev.next = msg; } if (needWake) { nativeWake(mPtr); } } return true;}复制代码
总结就是,MessageQueue 按照消息的触发时间插入队列,队头是最早要触发的消息。一个新的消息加入会根据触发时长从队头开始遍历。
消息轮询
好了,消息已经发送给 MessageQueue 了,那么谁来管理这个 MessageQueue,将其中的消息正确的、准确的分发呢?那就是 Looper,准确的说是 Looper 中的 loop() 方法,这也是我们在子线程中创建 Handler 先要之前要 Looper.perpare(),之后要 Looper.loop() 的原因。
public static void loop() { final Looper me = myLooper();//获取 TSL 存储区的 Looper 对象 if (me == null) { throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread."); } final MessageQueue queue = me.mQueue;//获取相对应的消息队列 ................ for (;;) { //消息主循环,除非线程退出,不然会一直循环,没有消息时会阻塞 //获取下一个消息,没有消息时会阻塞,消息队列退出后会返回 null,则该循环也退出 Message msg = queue.next(); // might block if (msg == null) { return; } // 默认为 null,可通过 setMessageLogging() 方法来指定输出,用于 debug 功能 final Printer logging = me.mLogging; if (logging != null) { logging.println(">>>>> Dispatching to " + msg.target + " " + msg.callback + ": " + msg.what); } ................ try { //获取消息后根据 msg 的 target 即所属 Hnadler 分发消息 //target 即 Handler 在上面发送消息代码解释中有说明 msg.target.dispatchMessage(msg); dispatchEnd = needEndTime ? SystemClock.uptimeMillis() : 0; } finally { if (traceTag != 0) { Trace.traceEnd(traceTag); } } ................. if (logging != null) { logging.println("<<<<< Finished to " + msg.target + " " + msg.callback); } ................ //分发完此消息,就回收此 Message 对象,留以复用 msg.recycleUnchecked(); }}复制代码
一些代码解释已经很清楚了,在在这里面主要是一个死循环轮询消息。由 MessageQueue 的 next() 方法取出消息,再由 Message 所属的 Handler 对象 dispatchMessage() 方法分发消息。首先我们看 next() 方法。
获取消息
Message next() { // Return here if the message loop has already quit and been disposed. // This can happen if the application tries to restart a looper after quit // which is not supported. final long ptr = mPtr; if (ptr == 0) { //当消息循环已经退出,则直接返回 return null; } int pendingIdleHandlerCount = -1; // -1 only during first iteration int nextPollTimeoutMillis = 0;//阻塞时长 for (;;) { if (nextPollTimeoutMillis != 0) { Binder.flushPendingCommands(); } //阻塞函数,参数 nextPollTimeoutMillis 表示等待时长,或者消息队列被唤醒,都会返回 nativePollOnce(ptr, nextPollTimeoutMillis); synchronized (this) { Message prevMsg = null; Message msg = mMessages; if (msg != null && msg.target == null) { //当消息中的 Handler 为空时,在 MessageQueue 中寻找下一个异步消息 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 { // 获得消息 mBlocked = false; if (prevMsg != null) { prevMsg.next = msg.next; } else { mMessages = msg.next; } msg.next = null; if (DEBUG) Log.v(TAG, "Returning message: " + msg); //改变 message 状态 msg.markInUse(); return msg; } } else { // No more messages. //没有消息,设置阻塞时长 nextPollTimeoutMillis = -1; } // 如果正在退出,返回空 if (mQuitting) { dispose(); return null; } ............. }}复制代码
nativePollOnce 是一个阻塞函数,参数 nextPollTimeoutMillis 则代表阻塞时长,当值为-1 时,则会一直阻塞下去。 所以说,next 函数在 MessageQ 有消息时,会获取消息并返回,在没有消息时,则会一直阻塞。
分发消息
获取完消息,就要到分发消息了,也就是消息轮询总结中的 dispatchMessage 函数,在 Handler 类中
public void dispatchMessage(Message msg) { if (msg.callback != null) { handleCallback(msg); } else { if (mCallback != null) { if (mCallback.handleMessage(msg)) { return; } } handleMessage(msg); }}复制代码
这里的调用流程就是
- 首先判断 Message 的 callback 是否存在,如果存在,就执行 handleCallback 函数,这个函数就一个简单的 message.callback.run() 方法调用。Message 中的 callback 实际上是一个 Runnable,所以就会执行自定义的 run 函数。这个的作用是在 Handler 的 post 消息上,其实 post 消息与 send 消息并没有太大的不用,只是通过 getPostMessage 方法将 Message 封装了一下,其实就是将自定义的 Runnable 传给 Message 的 callback,这样在分发消息的时候就是直接执行自定义的 Runnable 中的 run 函数。
public final boolean post(Runnable r){ return sendMessageDelayed(getPostMessage(r), 0);}private static Message getPostMessage(Runnable r) { Message m = Message.obtain(); m.callback = r; return m;}复制代码
- 然后就是 Handler 类中有一个 Callback 接口,接口中只有一个 handleMessage() 函数。如果成员变量 mCallBack 存在,就会首先执行此接口中的函数。实际使用中我们可以实现此接口,并重写方法。然后通过 Handler 的构造方法传入。
public interface Callback { public boolean handleMessage(Message msg);}复制代码
- 最后才是我们熟悉的重写 Handler 类中的 handleMessage 方法,这个方法如果在上面那种处理过后返回的是 true,那么就根本到不了这个函数。所以说上面两种处理方式都可以拦截消息。其中第一种是一定会拦截消息,第二种则由返回值确定。
public void handleMessage(Message msg) {}复制代码
总结
转了一圈,从 Handler 到 Looper 又到 MessageQ 最后又回到 Handler,整个消息机制也就差不多这样啦,当然 Message 在其中就是一个实体啦,可以协上数据一起传递消息。我也是根据这个顺序,一步一步的慢慢了解每个步骤的。
最后,附上一张图吧,会更清晰一点