16c0641a5f63bed0.pngjava
Android 的消息机制主要指的是 Handler 的运行机制,从开发者的角度来讲 Handler 是 Android 消息机制的上层接口,而底层的逻辑则是由 MessageQueue、 Looper 来完成的。android
Handler 的设计目的是为了解决不能在 Android 主线程中作耗时操做而又只有主线程才能访问 UI 的矛盾。经过 Handler 消息机制可让开发者在子线程中完成耗时操做的同时在主线程中更新UI。算法
Handler 机制是 Android 用于 UI 刷新的一套消息机制。开发者可使用这套机制达到线程间通讯、线程切换目的。shell
这里要思考一个问题:为何 Android 非要规定只有主线程才能更新 UI 呢?安全
由于 Android 的全部 View 控件都不是线程安全的,若是在多线程中并发访问极可能形成意想不到的结果。对于加锁这种方案也不可取,首先加锁以后会让 UI 访问逻辑变的很复杂,开发者须要时刻考虑多线程并发将会带来的问题,其次锁机制过重了它会严重影响 UI 访问效率。介于这两个缺点,最简单且高效的方法就是采用单线程的方式访问 UI。Handler 机制应运而生。数据结构
那么 Handler 内部是如何完成线程切换的呢?答案就是神奇的 :ThreadLocal多线程
ThreadLocal 并非 Thread ,他的特色颇有意思: 每个线程存储的值是相互隔离的并发
public class TreadLocalDemo { // 就算设置为 static 结果也是同样的 ThreadLocal<Boolean> mThreadLocal = new ThreadLocal<Boolean>(); public void runDemo() { mThreadLocal.set(true); System.out.println(Thread.currentThread().getName() + " " + mThreadLocal.get()); new Thread("Thread#1") { @Override public void run() { super.run(); mThreadLocal.set(false); System.out.println(Thread.currentThread().getName() + " " + mThreadLocal.get()); } }.start(); new Thread("Thread#2") { @Override public void run() { super.run(); System.out.println(Thread.currentThread().getName() + " " + mThreadLocal.get()); } }.start(); System.out.println(Thread.currentThread().getName() + " " + mThreadLocal.get()); } }
运行的结果很清晰的展现他的特色,虽然在主线程和线程1中都作了赋值操做,但并不能改变原来线程的赋值状况。app
image-20190905231656636.pngless
对于 ThreadLocal 的原理简单来讲:每一线程都有一个专门用于保存 ThreadLocal 的成员变量 localValues
。 尽管在不一样线程中访问同一个 ThreadLocal 的 set
和 get
方法,但所作的操做都仅限制于各自线程的内部。这就是 ThreadLocal 能够在多个线程中互不干扰的存储和读取数据的缘由。正是这种特性让 Handler 作到了线程的切换。
Looper 正是借助 ThreadLocal 的特色在不一样的线程建立不一样的实例。至此 Handler 与 Looper 、线程达到了一一对应的绑定关系。因此不管此 Handler 的实例在什么线程调用,最终的回调都会分发到建立线程。
MessageQueue 主要有两个操做:插入和读取。读取操做也会伴随着删除。插入和读取的方法分别对应的是:enquequeMessage
和 next
,MessageQueue 并非像名字同样使用队列做为数据结构,而是使用单链表来维护消息。单链表在插入和删除上比较有优点。
首先来讲说 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) { // 可见只有在调用 quit() 方法以后才会返回空 return null; } ...... // 一个死循环 ! for (;;) { if (nextPollTimeoutMillis != 0) { Binder.flushPendingCommands(); } // 一个 native 方法,此方法在没有消息或者消息没有到执行时间的时候会让线程进入等待状态。 // 有点相似于 Object.wait 可是 nativePollOnce 能够自定等待时间 nativePollOnce(ptr, nextPollTimeoutMillis); synchronized (this) { ...... if (!keep) { synchronized (this) { // 获取消息后从列表中移除 mIdleHandlers.remove(idler); } } } }
最关键的是三点内容
nativePollOnce
是一个 native 方法,若是单列表中没有消息或者等待的时间没有到,那么当前线程将会被设置为 wait 等待状态 ,直到能够获取到下一个 Message
。更详细的内容能够参见 StackOverflow 上关于 nativePollOnce的回答而这个死循环的目的就是不让 next
方法退出,等待 nativePollOnce 的响应。等到获取到消息以后再将这个消息从消息列表中移除。
enqueueMessage 方法的主要工做就是向单链表中插入数据,当线程处于等待状态则调用 nativeWake
唤醒线程,让 next 方法处理消息。
boolean enqueueMessage(Message msg, long when) { ...... // We can assume mPtr != 0 because mQuitting is false. if (needWake) { nativeWake(mPtr); } } return true; }
详情请参见Handler是怎么作到消息延时发送的 下面再抄一部分结论:
在 next 方法中若是头部的这个 Message 是有延迟并且延迟时间没到的(now < msg.when
),会计算一下时间(保存为变量 nextPollTimeoutMillis
), 而后在循环开始的时候判断若是这个 Message 有延迟,就调用nativePollOnce (ptr, nextPollTimeoutMillis)
进行阻塞。nativePollOnce()
的做用相似与 Object.wait()
, 只不过是使用了 Native 的方法对这个线程精确时间的唤醒。
postDelay()
一个10秒钟的 Runnable A、消息进队,MessageQueue 调用 nativePollOnce()
阻塞,Looper 阻塞;post()
一个 Runnable B、消息进队,判断如今A时间还没到、正在阻塞,把B插入消息队列的头部(A的前面),而后调用 nativeWake()
方法唤醒线程;MessageQueue.next()
方法被唤醒后,从新开始读取消息链表,第一个消息B无延时,直接返回给 Loopernext()
方法,MessageQueue 继续读取消息链表,第二个消息A还没到时间,计算一下剩余时间(假如还剩 9秒)继续调用 nativePollOnce()
阻塞;直到阻塞时间到或者下一次有Message 进队;image-20190908155849230.png
Looper 在 Android 消息机制中扮演着消息循环的角色。具体来讲他的任务就是不停的从 MessageQueue 中获取消息,若是有新消息就当即处理,没有消息的时候,与 Looper 绑定的线程就会被 MessageQueue 的 next 的 nativePollOne 方法置于等待状态。
/** Initialize the current thread as a looper. * This gives you a chance to create handlers that then reference * this looper, before actually starting the loop. Be sure to call * {@link #loop()} after calling this method, and end it by calling * {@link #quit()}. */ 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"); } // 建立 Looper 实例,将实例保存在 sThreadLocal 中与当前线程绑定。 sThreadLocal.set(new Looper(quitAllowed)); }
在构造方法里面他会建立一个 MessageQueque,并保存当前线程。
private Looper(boolean quitAllowed) { mQueue = new MessageQueue(quitAllowed); mThread = Thread.currentThread(); }
getMainLooper 能够在任何地方获取到主线程的 Looper,那么主线程是如何建立 Looper 的呢?
咱们的目光来到了 AndroidThread 类, 在 AndroidThread 中咱们看到了熟悉的方法 :main(String[] args
。千万不要被 AndroidThread 的名字所迷惑,AndroidThread 并非一个线程,它只是一个开启主线程的类。
public static void main(String[] args) { .... // 建立 Looper 和 MessageQueue 对象,用于处理主线程的消息 Looper.prepareMainLooper(); // 建立 ActivityThread 对象 ActivityThread thread = new ActivityThread(); // 创建 Binder 通道 (建立新线程) thread.attach(false); // 消息循环运行 Looper.loop(); throw new RuntimeException("Main thread loop unexpectedly exited"); }
注意调用的是 prepare(false)
不容许退出,这是为何呢?
public static void prepareMainLooper() { // 不容许退出 prepare(false); synchronized (Looper.class) { if (sMainLooper != null) { throw new IllegalStateException("The main Looper has already been prepared."); } sMainLooper = myLooper(); } }
这是由于主线程的 Looper 伴随着一个 App 的整个生命周期,全部的 UI访问、View 刷新都是在 Looper 里面完成的,若是容许开发者手动退出,那么整个 App 都会变得不可控。
更多细节能够参见下面的一节「 Looper中的死循环为何没有卡死线程」
/** * Run the message queue in this thread. Be sure to call * {@link #quit()} to end the loop. */ public static void loop() { final Looper me = myLooper(); ...... final MessageQueue queue = me.mQueue; ...... // 死循环 for (;;) { // 可能会被阻塞 Message msg = queue.next(); if (msg == null) { // msg 为 null 会当即退出循环,这也是退出循环的惟一方法。 return; } ...... try { // 开始分发消息 msg.target.dispatchMessage(msg); dispatchEnd = needEndTime ? SystemClock.uptimeMillis() : 0; } finally { if (traceTag != 0) { Trace.traceEnd(traceTag); } } ...... } }
loop 方法是一个死循环,他的工做就是不断的检查 MessageQueue 是否有能够处理的消息,若是有这将消息分发给 Handler 处理。既然是死循环那么为何没有卡死线程呢?更多细节能够参见下面的一节「 Looper中的死循环为何没有卡死线程」
Looper 内部提供了两种退出的方法,分别是 quit、quitSafely。从本质上来说 quit 调用后会当即退出 Looper,而 quitSafely 只是设定一个退出标记,等待消息队列中的已有消息处理完毕后,再退出。
Looper 退出后,经过 Handler 发送的消息会失败,这个时候 Handler send 方法会返回 false。在子线程中,若是手动为其建立了 Looper,那么在全部的逻辑完成后理应手动调用 quit 方法终止 Looper 内部的循环,不然这个子线程会一直处于等待状态,而退出 Looper 以后,子线程也就会随之终止,所以在子线程中使用 Looper,==必须在恰当的时机终止它==。
/** * Quits the looper. */ public void quit() { mQueue.quit(false); } /** * Quits the looper safely. */ public void quitSafely() { mQueue.quit(true); }
若是是主线程开发者就退出不了,要是退出了,就麻烦大了。
public static void prepareMainLooper() { // fasle 不容许退出 prepare(false); .... }
在 Looper.quit
的源码中能够清晰看到,本质上调用的是 MessageQueue 的 quite 方法。而在调用 MessageQueue.quite
以后 再次调用 MessageQueue.next()
会返回 null
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) { // 可见只有在调用 quit() 方法以后才会返回空 return null; } ......
Looper.loop()
在调用 queue.next()
得的结果为 null 的时候会当即跳出死循环, 这也是退出死循环的惟一方式。
public static void loop() { …… for (;;) { Message msg = queue.next(); // might block if (msg == null) { // No message indicates that the message queue is quitting. return; } ……
参考知乎:Android中为何主线程不会由于Looper.loop()里的死循环卡死?
咱们都知道:一个简单的死循环会消耗掉大量资源致使线程被卡死。可是 Looper.loop() 方法开启就是一个死循环,它为何没有卡死线程呢?总结一下主要有3个疑惑:
首先要说说的是为何在 Looper 中使用死循环。在 CPU 看来操做系统线程(这里的定义能够参见《Java基础》多线程和线程同步 —— 进程与线程一节 ) 只不过是一段能够执行的代码,CPU 会使用 CFS 调度算法,保证每个 task 都尽量公平的享用 CPU 时间片。既然操做系统线程是一段能够执行的代码,当可执行的代码结束以后,线程生命周期也就终止,线程将会退出。可是对于 Android 这类的 GUI 程序,咱们绝对不但愿代码执行一段时间以后主线程就本身中止掉,那么如何保证线程一直执行下去呢?简单的作法就是在线程中执行死循环,让线程一直工做下去不会中止退出。
总的来讲,在线程中使用死循环想要解决的问题就是防止线程本身退出。因此对于 Looper 而言,他的死循环就是但愿不断的从 MessageQueue 中获取消息,而不但愿线程线性执行以后就退出。
首先 Android 全部的 UI 刷新和生命周期的回调都是由 Handler消息机制完成的,就是说 UI 刷新和生命周期的回调都是依赖 Looper 里面的死循环完成的,这样设计的目的上文已经阐述清楚。这篇文章里面贴了 AndroidTread 对于 Handler 的实现类 H 的源码(进入文章后搜索:内部类H的部分源码) 源码太长,我就不贴了。
其次Looper 不是一直拼命干活的傻小子,而是一个有活就干没活睡觉的老司机,因此主线程的死循环并非一直占据着 CPU 的资源不释放,不会形成过分消耗资源的问题。这里涉及到了Linux pipe/epoll机制,简单说就是在主线程的 MessageQueue 没有消息时,便在 loop 的 queue.next() 中的 nativePollOnce() 方法里让线程进入休眠状态,此时主线程会释放CPU资源,直到下个消息到达或者有事务发生才会再次被唤醒。因此 Looper 里的死循环,没有一直空轮询着瞎忙,也不是进入阻塞状态占据着 CPU 不释放,而是进入了会释放资源的等待状态,等待着被唤醒
通过上面的讨论能够得知:
那么唤醒 Looper 的消息是从哪里来的呢?
目光回到 AndroidThread 类中的这几行代码
public static void main(String[] args) { .... // 建立ActivityThread对象 ActivityThread thread = new ActivityThread(); //创建Binder通道 (建立新线程) thread.attach(false); Looper.loop(); //消息循环运行 }
在建立 ActivityThread 后会经过thread.attach(false)
方法在 ActivityThread 中建立 Binder 的服务端用于接收系统服务AMS发送来的事件,而后经过 ActivityThread 的内部类 ApplicationThread 中 sendMessage 方法
...... public final void scheduleStopActivity(IBinder token, boolean showWindow, int configChanges) { sendMessage( showWindow ? H.STOP_ACTIVITY_SHOW : H.STOP_ACTIVITY_HIDE, token, 0, configChanges); } public final void scheduleWindowVisibility(IBinder token, boolean showWindow) { sendMessage( showWindow ? H.SHOW_WINDOW : H.HIDE_WINDOW, token); } public final void scheduleSleeping(IBinder token, boolean sleeping) { sendMessage(H.SLEEPING, token, sleeping ? 1 : 0); } public final void scheduleResumeActivity(IBinder token, int processState, boolean isForward, Bundle resumeArgs) { updateProcessState(processState, false); sendMessage(H.RESUME_ACTIVITY, token, isForward ? 1 : 0); ......
将消息发送给 AndroidThread 的 Handler 实现内部类 H。从而完成了 Binder Thread 到 UI 线程即主线程的切换,唤醒 Looper 进行 dispatchMessage 的动做。
唤醒的具体操做参见上文「MessageQueue -> enqueueMessage -> nativeWake」
经过 ActivityThread 的源码能够清楚看到
public static void main(String[] args) { .... //建立Looper和MessageQueue对象,用于处理主线程的消息 Looper.prepareMainLooper(); .... Looper.loop(); //消息循环运行 .... }
Android 在启动一个 App 的时候都会建立一个 Looper,而用户启动子线程的时候是没有这个操做的,因此须要开发者本身建立并调用 Looper.loop() 让 Looper 运行起来。
new Thread("Thread#1") { @Override public void run() { // 手动生成为当前线程生成 Looper Looper.prepare(); Handler handler = new Handler(); Looper.loop(); } }.start();
此处咱们作个实验,既然 Looper 是个死循环那么在 loop() 以后的代码是否是永远没有机会执行呢?
/** * Android 消息机制 —— Handler * <p> * Created by im_dsd on 2019-09-07 */ public class HandlerDemo { public static final String TAG = "HandlerDemo"; private Handler mHandler; private Looper mLooper; /** * 如何在子线程中开启 Handler */ public void startThreadHandler() { new Thread("Thread#1") { @Override public void run() { // 手动生成为当前线程生成 Looper Looper.prepare(); mLooper = Looper.myLooper(); mHandler = new Handler() { @Override public void handleMessage(Message msg) { super.handleMessage(msg); Log.d(TAG,Thread.currentThread().getName() + " " + msg.what); } }; Log.d(TAG,Thread.currentThread().getName() + "loop 开始 会执行吗? "); // 手动开启循环 Looper.loop(); Log.d(TAG,Thread.currentThread().getName() + "loop 结束 会执行吗? "); } }.start(); // 等待线程启动 try { Thread.sleep(100); } catch (InterruptedException e) { e.printStackTrace(); } Log.d(TAG,"start send message"); mHandler.sendEmptyMessage(1); mHandler.post(() -> Log.d(TAG,Thread.currentThread().getName())); } }
自启动到将 App 完全杀死,输出结果也是如此:loop 后面的代码没有执行!
2964-3007/com.example.dsd.demo D/HandlerDemo: Thread#1loop 开始 会执行吗? 2964-2964/com.example.dsd.demo D/HandlerDemo: start send message 2964-3007/com.example.dsd.demo D/HandlerDemo: Thread#1 1 2964-3007/com.example.dsd.demo D/HandlerDemo: Thread#1
这意味两个严重的问题:looper()
后面的代码一直都不会执行并且线程 Thread#1 将会一直运行下去!在 JVM 规范里面规定==处于运行中的线程会不被 GC==。在没有消息的时候 Looper 会处于等待状态。等待在 Thread 的生命周期里仍然属于运行状态,它永远不会被 GC。
因此不少网上不少文章里都有一个致命的缺陷,根本就没有说起到要在使用完毕后即便退出 Looper。紧接上文的代码
// 尝试 1秒 后中止 try { Thread.sleep(1000); mLooper.quit(); } catch (InterruptedException e) { e.printStackTrace(); }
此时的结果
2964-3007/com.example.dsd.demo D/HandlerDemo: Thread#1loop 开始 会执行吗? 2964-2964/com.example.dsd.demo D/HandlerDemo: start send message 2964-3007/com.example.dsd.demo D/HandlerDemo: Thread#1 1 2964-3007/com.example.dsd.demo D/HandlerDemo: Thread#1 2964-3007/com.example.dsd.demo D/HandlerDemo: Thread#1loop 结束 会执行吗?
根据综上所述,Handler 机制彻底能够在 Android 中用做线程间的消息同步,这里要强调一下,Handler 机制是 Android 独有的,笔者在写上面的 Demo 的时候居然傻傻的将 Handler 的启动放在了 Java 中,直接抛出了 RuntimException Stub 的错误。
总结一下在子线程中使用 Handler 机制要注意两点问题:
Looper.prepare();
手动生成为当前线程生成 Looper,并调用Looper.looper()
启动内部的死循环。Looper.myLooper().quit()
退出当前线程。Handler 的工做主要就是发送和接收消息。消息的发送能够经过 post 的一系列方法和 send 的一系类方法。在建立 Handler 的时候他会默认使用当前线程的 Looper ,若是当前线程没有建立过 Looper 会抛出以下异常。
image-20190908125220700.png
固然也能够手动指定不一样线程的 Looper。
Handler mHandler = new Handler(Looper.getMainLooper());
public final boolean sendMessage(Message msg) { return sendMessageDelayed(msg, 0); } public final boolean sendMessageDelayed(Message msg, long delayMillis) { if (delayMillis < 0) { delayMillis = 0; } return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis); } public final boolean sendEmptyMessageAtTime(int what, long uptimeMillis) { Message msg = Message.obtain(); msg.what = what; return sendMessageAtTime(msg, uptimeMillis); } 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); }
通过一系列的跟踪,最终的结果是调用了enqueueMessage(MessageQueue, Message, long)
方法,目的就是为了向 MessageQueue 中插入一条消息。
private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) { msg.target = this; if (mAsynchronous) { msg.setAsynchronous(true); } return queue.enqueueMessage(msg, uptimeMillis); }
然后 nativeWake
将会唤醒等待的线程,MessageQueue#next
将会在Looper.loop()
中将这条消息返回,Looper.loop()
在收到这条消息以后最终会交由 Handler#dispatchMessage
处理
/** * Looper 的 loop 方法 */ public static void loop() { ...... // 死循环 for (;;) { ...... // 开始分发消息 msg.target 指的就是发送消息的 Handler msg.target.dispatchMessage(msg); ...... } }
/** * Handle 的 dispatchMessage 方法 */ public void dispatchMessage(Message msg) { // 首先检查 msg 的 callback 是否为 null if (msg.callback != null) { // 不为 null 使用 msg 的 callback 处理消息 handleCallback(msg); } else { // mCallback 是否为 null if (mCallback != null) { if (mCallback.handleMessage(msg)) { return; } } // 都没有指定则交由开发者重写的 handleMessage 处理 handleMessage(msg); } }
从上面的逻辑咱们能够看出 callback 的优先级:msg#callback > new Handler(Callback) 中 指定的 Callback> 重写 Handler 的 callBack
mCallback
指的是一个接口 , 可使用 Handler handler = new Handler(Callback)
的方式指定回调,这种方式能够由外部传递进来会回调方法,更加灵活。
* Callback interface you can use when instantiating a Handler to avoid * having to implement your own subclass of Handler. */ public interface Callback { /** * @param msg A {@link android.os.Message Message} object * @return True if no further handling is desired */ public boolean handleMessage(Message msg); }
至此对于 Android 的消息机制已经讲解完毕,你是否已经有了清晰的认识呢?对于开篇的问题:Handler 是如何完成线程切换的,你找到答案了吗?
16c0641ad370a90a.jpeg
mThread 是主线程,这里会检查当前线程是不是主线程。
这个问题缘由出如今 Activity 的生命周期中 , 在 onCreate 方法中, UI 处于建立过程,对用户来讲界面还不可见,直到 onStart 方法后界面可见了,再到 onResume 方法后页面能够交互,从某种程度来说, 在 onCreate 方法中不能算是更新 UI,只能说是配置 UI,或者是设置 UI 属性。 这个时候不会调用到 ViewRootImpl.checkThread () , 由于 ViewRootImpl 没有建立。 而在 onResume 方法后, ViewRootImpl 才被建立。 这个时候去交户界面才算是更新 UI。
setContentView 知识创建了 View 树,并无进行渲染工做 (其实真正的渲染工做实在 onResume 以后)。也正是创建了 View 树,所以咱们能够经过 findViewById() 来获取到 View 对象,可是因为并无进行渲染视图的工做,也就是没有执行 ViewRootImpl.performTransversal。一样 View 中也不会执行 onMeasure (), 若是在 onResume() 方法里直接获取 View.getHeight() / View.getWidth () 获得的结果老是 0 解决方案是在 UI 真正可见的方法 onWindowFocusChanged()
里面获取。
若是在 Handler 构造方法里面直接 new Looper(), 多是没法保证 Looper 惟一,只有用 Looper.prepare() 才能保证惟一性,具体能够看 prepare 方法。
由于一个线程只绑定一个 Looper ,因此在 Looper 构造方法里面初始化就能够保证 mQueue 也是惟一的 Thread 对应一个 Looper 对应一个 mQueue。
这个方法里面是一个死循环,可是里面的方法 nativePollOnce 运用了 Linux 的 epoll 机制,在没有消息的时候回会将线程挂起,注意此时的挂起至关于 Object.wait() : 它会释放 CPU 资源,等待唤醒。有消息进入的时候回到用 MessageQueue#enqueueMessage() 加入数据,此时 MessageQueue#enqueueMessage() 内部的 nativeWeak 会从新唤醒线程,
能够发现:
MessageQueue#next() 中会判断消息的时间,若是尚未到消息执行的时间,会将消息定时挂起(咱们记录它 为 A。若是这个时候有新的消息到来(记录为 B), MessageQueue#enqueueMessage() 会按照消息的执行时间排序插入,而后唤醒 MessageQueue#next() 处理消息。若是 B 不是定时消息当即处理,若是是定时消息更新挂起时间继续阻塞,等到阻塞时间到的时候就会当即唤醒 next 方法处理。
Loop#loop 里面的死循环是为了防止退出的,而 MessageQueue#next() 的死循环是为了确认到底有没有消息 参考:对于 MessageQueue 的解读
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; } //为-1时,说明是第一次循环,在当前消息队列中没有MSG的状况下,须要处理注册的Handler int pendingIdleHandlerCount = -1; // -1 only during first iteration // 超时时间。即等待xxx毫秒后,该函数返回。若是值为0,则无须等待当即返回。若是为-1,则进入无限等待,直到有事件发生为止 int nextPollTimeoutMillis = 0; for (;;) { if (nextPollTimeoutMillis != 0) {//??? Binder.flushPendingCommands(); } // 该函数提供阻塞操做。若是nextPollTimeoutMillis为0,则该函数无须等待,当即返回。 //若是为-1,则进入无限等待,直到有事件发生为止。 //在第一次时,因为nextPollTimeoutMillis被初始化为0,因此该函数会当即返回 //从消息链的头部获取消息 nativePollOnce(ptr, nextPollTimeoutMillis); 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) {//message不为空,但没有执行者 // Stalled by a barrier. Find the next asynchronous message in the queue. do {//寻找Asynchronous的消息 prevMsg = msg; msg = msg.next; } while (msg != null && !msg.isAsynchronous()); } if (msg != null) { if (now < msg.when) { //判断头节点所表明的message执行的时间是否小于当前时间 //若是小于,让loop()函数执行message分发过程。不然,须要让线程再次等待(when–now)毫秒 // 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;//将队列设置为非 blocked 状态 if (prevMsg != null) { prevMsg.next = msg.next; } else { mMessages = msg.next; } msg.next = null; if (false) Log.v("MessageQueue", "Returning message: " + msg); msg.markInUse(); //将消息设置为 inuse return msg; } } else { //若是头节点为空,消息链中无消息,设置nextPollTimeoutMillis为-1,让线程阻塞住, //直到有消息投递(调用enqueueMessage方法),并利用nativeWake方法解除阻塞 // No more messages. nextPollTimeoutMillis = -1; } // Process the quit message now that all pending messages have been handled. if (mQuitting) { dispose(); return null; } // If first time idle, then get the number of idlers to run. // Idle handles only run if the queue is empty or if the first message // in the queue (possibly a barrier) is due to be handled in the future. if (pendingIdleHandlerCount < 0 && (mMessages == null || now < mMessages.when)) { //第一次进入,当前无消息,或还须要等待一段时间消息才能分发,得到idle handler的数量 pendingIdleHandlerCount = mIdleHandlers.size(); } if (pendingIdleHandlerCount <= 0) { //若是没有idle handler须要执行,阻塞线程进入下次循环 // No idle handlers to run. Loop and wait some more. mBlocked = true; continue; } if (mPendingIdleHandlers == null) { mPendingIdleHandlers = new IdleHandler[Math.max(pendingIdleHandlerCount, 4)]; } mPendingIdleHandlers = mIdleHandlers.toArray(mPendingIdleHandlers); } // Run the idle handlers. // We only ever reach this code block during the first iteration. for (int i = 0; i < pendingIdleHandlerCount; i++) { final IdleHandler idler = mPendingIdleHandlers[i]; mPendingIdleHandlers[i] = null; // release the reference to the handler boolean keep = false; try { keep = idler.queueIdle(); } catch (Throwable t) { Log.wtf("MessageQueue", "IdleHandler threw exception", t); } //若是keep=false,代表此idler只执行一次,把它从列表中删除。若是返回true,则表示下次空闲时,会再次执行 if (!keep) { synchronized (this) { mIdleHandlers.remove(idler); } } } //pendingIdleHandlerCount=0,是为了不第二次循环时,再一次通知listeners //若是想剩余的listeners再次被调用,只有等到下一次调用next()函数了 // Reset the idle handler count to 0 so we do not run them again. pendingIdleHandlerCount = 0; // nextPollTimeoutMillis=0,是为了不在循环执行idler.queueIdle()时,有消息投递。 //因此nextPollTimeoutMillis=0后,第二次循环在执行nativePollOnce时,会当即返回 //若是消息链中仍是没有消息,那么将会在continue;处执行完第二次循环,进行第三次循环,而后进入无限等待状态 // While calling an idle handler, a new message could have been delivered // so go back and look again for a pending message without waiting. nextPollTimeoutMillis = 0; } }
重点:
死循环最多执行三次:
enqueueMessage 有排序功能,按照时间入队。
boolean enqueueMessage(Message msg, long when) { 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) {//若是正在退出,就不能插入消息 IllegalStateException e = new IllegalStateException( msg.target + " sending message to a Handler on a dead thread"); Log.w("MessageQueue", e.getMessage(), e); msg.recycle();//把这个消息放回到消息池中 //得到msg时,先去消息池中看看有没有被回收的msg,若是有,就不用建立新的msg了 return false; } msg.markInUse(); msg.when = when;//从消息队列中取出绝对时间戳 Message p = mMessages;//指向队首 boolean needWake; //若是当前的消息链为空,或者要插入的MSG为QUIT消息,或者要插入的MSG时间小于消息链的第一个消息 //在队首插入 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 { //不然,咱们须要遍历该消息链,将该MSG插入到合适的位置 // Inserted within the middle of the queue. Usually we don't have to wake // up the event queue unless there is a barrier at the head of the queue // and the message is the earliest asynchronous message in the queue. 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; } //neekWake=mBlocked, 若是mBlocked为ture,表面当前线程处于阻塞状态,即nativePollOnce处于阻塞状态 //当经过enqueueMessage插入消息后,就要把状态改成非阻塞状态,因此经过执行nativeWake方法,触发nativePollOnce函数结束等待 // We can assume mPtr != 0 because mQuitting is false. if (needWake) { nativeWake(mPtr); } } return true; }
本文由博客群发一文多发等运营工具平台 OpenWrite 发布