Android中Handler的消息机制分析(二)

ps:阅读原文可获取 demo 源码,文章末尾有原文链接
ps:源码是基于 android api 27 来分析的,demo 是用 kotlin 语言写的。
上一篇Android中Handler的消息机制分析(一)主要分析的是 Handler 讲消息的发送和 Handler 将消息的处理,这一篇分析的是 Handler 其他常用的一些知识点。
我们来看看 Handler 的 post 方法,它也是属于发送消息的方法;
public final boolean post(Runnable r)
{

returnsendMessageDelayed(getPostMessage(r), 0);

}
从 post 方法可以看出,它调用了 Handler 的 sendMessageDelayed 方法,从Android中Handler的消息机制分析(一)这篇文章可以知道,Handler 的 sendMessageDelayed 方法最终调用了 Handler 的 enqueueMessage 方法,enqueueMessage 方法调用了 MessageQueue 的 enqueueMessage 方法,MessageQueue 的 enqueueMessage 方法最终完成 Handler 的消息发送(本质上是将消息插入到链表中);所以 Handler 的 postDelayed、sendMessage 等发送消息的方法最终调用 Handler 的 enqueueMessage 方法。
我们来看一下 Handler 的所有构造方法;
//1、 public Handler() { this(null, false); }//2、 public Handler(Handler.Callback callback) { this(callback, false); }//3、 public Handler(Looper looper) { this(looper, null, false); }//4、 public Handler(Looper looper, Handler.Callback callback) { this(looper, callback, false); }//5、 public Handler(boolean async) { this(null, async); }//6、 public Handler(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()); } }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; }//7、 public Handler(Looper looper, Handler.Callback callback, boolean async) { mLooper = looper; mQueue = looper.mQueue; mCallback = callback; mAsynchronous = async; }

我们来看注释7 的构造方法中的参数,因为这个构造方法的参数包含注释2 到注释6 构造方法的参数;looper 参数表示一个轮循器,looper 调用 loop 方法使得线程不断循环让 Handler 处理消息,looper 初始化和looper 调用 loop 方法以及 Handler 的初始化一定是在同一线程的,Handler 的初始化后一定会有 looper;callback 参数表示当 Handler 不重写 handleMessage 方法时,callback 接口的 handleMessage 方法就会代替 Handler 处理消息,具体用法可以看Android中Handler的消息机制分析(一)这篇文章;async 参数表示是否为异步消息。
1、这里就有一个疑问了,Handler 的初始化后一定会有 looper,但是我们一般在 Activity 中初始化 Handler 的时候调用的是 Handler 无参构造方法,looper 是空的啊?
首先 ActivityThread 的 main 方法是先比 Activity 启动先执行并执行在主线程的,也进行了 Looper 初始化和调用 Looper.loop 方法对线程进程循环;
public static void main(String[] args) {
...... Looper.prepareMainLooper(); ...... Looper.loop(); ......

}
Looper的prepareMainLooper 方法最终会调用 Looper 的 prepare(boolean quitAllowed) 方法;
private static void prepare(boolean quitAllowed) {
if (sThreadLocal.get() != null) { throw new RuntimeException("Only one Looper may be created per thread"); }//8、 sThreadLocal.set(new Looper(quitAllowed));

}
这时候注释8 的代码保存的是主线程的 Looper。
我们在 Activity 中初始化一个 Handler 时,Activity 是运行在主线程的,所以 Handler 的初始化也是运行在主线程的,我们看看 Handler 的无参构造方法;
public Handler() { this(null, false); } public Handler(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()); } }//9、 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 的无参构造方法调用 Handler(Handler.Callback callback, boolean async) 构造方法,并获取一个 Looper,我们看看注释9 的代码,也就是 Looper 的 myLooper 方法;
public static @Nullable Looper myLooper() {
return sThreadLocal.get();

}
由于 Handler 的无参构造方法是运行在主线程的,sThreadLocal.get 方法拿到的是主线程的 Looper,所以我们一般在 Activity 中初始化 Handler 的时候调用的是 Handler 无参构造方法,looper 不是空的。
【Android中Handler的消息机制分析(二)】我们在回顾注释6 的 Handler 的构造方法中的以下代码;
if (mLooper == null) {
throw new RuntimeException( "Can't create handler inside thread that has not called Looper.prepare()");

}
2、这就说明在 Handler 处理消息的线程进行 Looper 初始化。
2、1 演示一下在子线程初始化 Handler 时没有初始化 Looper 对象,看看报错结果。
inner class MyC : Handler.Callback {
override fun handleMessage(msg: Message?): Boolean { return true }

}
var myC: MyC = MyC()
class MyT: Thread() {
override fun run() { super.run() var h: Handler = Handler(myC) } } var myT: MyT = MyT() myT.start()

运行程序时,报以下错误
java.lang.RuntimeException: Can't create handler inside thread Thread[Thread-4,5,main] that has not called Looper.prepare()
at android.os.Handler.(Handler.java:205) at android.os.Handler.(Handler.java:132) at com.xe.handlerdemo2.MainActivity$MyT.run(MainActivity.kt:39)

3、这里有疑问,一个线程中有多少个 Looper?用多个 Handler 在同一个线程发送消息不会导致多个 Handler 处理消息混乱吗?
3、1我们先来解惑一个线程中有多少个 Looper
我们来看看 Looper 的 prepare(boolean quitAllowed) 方法,它是对 Looper 进行初始化;
private static void prepare(boolean quitAllowed) {
//10、 if (sThreadLocal.get() != null) { throw new RuntimeException("Only one Looper may be created per thread"); } sThreadLocal.set(new Looper(quitAllowed));

}
从注释10 的代码可以看出,当一个线程先初始化 Looper 之前,先从 sThreadLocal 变量取出值看看是否为空,如果不为空就会抛出异常,所以一个线程只能初始化一次 Looper。
3、2 我们再解惑用多个 Handler 在同一个线程发送消息会不会导致多个 Handler 处理消息混乱
首先我们用 Handler 发送消息过程中,会调用 Handler 的 enqueueMessage 方法;
private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
//11、 msg.target = this; if (mAsynchronous) { msg.setAsynchronous(true); } return queue.enqueueMessage(msg, uptimeMillis);

}
我们再看获取消息并分发消息的 Looper.loop 方法;
public static void loop() {
final Looper me = myLooper(); if (me == null) { throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread."); } final MessageQueue queue = me.mQueue; ...... for (; ; ) { Message msg = queue.next(); // might block ...... try {//12、 msg.target.dispatchMessage(msg); ...... } finally { ...... } ...... }

}
我们从注释11 可以看出,发送消息的时候 Message 的 target 属性持有 Handler,从注释12 可以看出,分发消息的时候用 Message 的 target 属性去分发,所以同一个线程中用不同的 Handler 去发送消息的时候,不会造成 Handler 之间处理消息混乱,即哪个 Handler 发送的消息,哪个 Handler 就会相应的处理;下面我们用代码演示一下;
var mH2: Handler? = null var mH3: Handler? = null companion object { var HANDLE_MESSAGE: Int = 1 var EXIT_THREAD: Int = 2 } inner class MyH2(looper: Looper): Handler(looper) { override fun handleMessage(msg: Message?) { super.handleMessage(msg) when(msg?.what) { HANDLE_MESSAGE -> { var s: String = msg.obj as String Log.d("MainActivity","--MyH2--" + s) } EXIT_THREAD -> { Looper.myLooper().quitSafely() } } } } inner class MyH3(looper: Looper): Handler(looper) { override fun handleMessage(msg: Message?) { super.handleMessage(msg) when(msg?.what) { HANDLE_MESSAGE -> { var s: String = msg.obj as String Log.d("MainActivity","--MyH3--" + s) } EXIT_THREAD -> { Looper.myLooper().quitSafely() } } } }inner class MyT2: Thread() { override fun run() { super.run() Looper.prepare() mH2 = MyH2(Looper.myLooper()) Looper.loop() } } inner class MyT3: Thread() { override fun run() { super.run() Looper.prepare() mH3 = MyH3(Looper.myLooper()) Looper.loop() } } inner class MyT4: Thread() { override fun run() { super.run() var m2: Message = Message.obtain(); m2.what = HANDLE_MESSAGE m2.obj = "222" var m3: Message = Message.obtain(); m3.what = HANDLE_MESSAGE m3.obj = "333" mH2?.sendMessage(m2) mH3?.sendMessage(m3) } } MyT2().start() MyT3().start() MyT4().start()

程序运行后,日志打印如下所示:
09-25 15:14:45.179 18159-18288/com.xe.handlerdemo2 D/MainActivity: --MyH2--222
09-25 15:14:45.179 18159-18289/com.xe.handlerdemo2 D/MainActivity: --MyH3--333
当我们不再使用线程时,要终止消息循环,Looper 提供了2个终止消息循环的方法,如下所示;
public void quit() { mQueue.quit(false); }public void quitSafely() { mQueue.quit(true); }

quit 方法会把 MessageQueue 消息池中全部的消息全部清空,不管是延迟消息还是非延迟消息;quitSafely 方法仅仅会清空 MessageQueue 消息池中全部的延迟消息,并将消息池中全部的非延迟消息派发出去让 Handler 去处理。
4、这里有最后一个疑问,主线程的死循环一直运行是不是特别消耗CPU资源?死循环导致卡死?
在主线程的 MessageQueue 没有消息时,主线程便阻塞在 loop 的 MessageQueue.next 方法中的 nativePollOnce 方法里,相当于 java 层的线程 waite 机制,此时主线程会释放 CPU 资源进入休眠状态,直到下个消息到达时调用 nativewake,很多时候主线程处于休眠状态,并不会大量消耗的 CPU 资源;首先主线程中的死循环不会导致卡死,它会使得主线程阻塞在 MessageQueue 的 next 中的 nativePollOnce 方法里,当有消息来时就会唤醒主线程进行消息处理,即使主线程在休眠的时候也有其他的线程在处理事件,死循环里面有一个阻塞,阻塞并不等于卡死,死循环和卡死并不是同一个概念。

    推荐阅读