前言
为什么会有这么一篇网上有很多种解说版本的博客?因为我看懂了很多次,都没有把自己的想法记下来,然后就忘了。那样不仅浪费时间、而且还有点伤积极性。
从一个异常出发开始
在《第一行代码》中看到了关于异步处理消息的用法时,有没有想过可以在子线程中去new一个Handler?现在就开始着手,从一个子线程中去new一个Handler,看看会有什么发生。
1 | new Thread(new Runnable() { |
结果就出现了RuntimeException异常,仔细看它的信息说明。
java.lang.RuntimeException: Can't create handler inside thread that has not called Looper.prepare()
那么可知,在每个线程new Handler()
时,都必须先调用Looper.prepare()
或者调用一个能够达到相同效果的函数。那么在主线程中可以new Handler()
的原因,想必就是已经调用过了。以下代码位于AcitivityThread.java中,是一段初始化主线程的内容。
1 | Looper.prepareMainLooper(); |
其它的都忽略,就看Looper相关的。Looper.prepareMainLooper()
想必是达到了相同的效果吧。那么,这个效果到底是什么呢?让我们慢慢拨开云雾。
寻找那个异常
于是,我们很自然地在子线程中加入了Looper.prepare()
,并随手按着Ctrl,左键点击鼠标,进入了prepare()
函数中。
1 | /* |
当初的那个异常不就在眼前?但是,这sThreadLocal
又是什么?它是什么暂时抛开,这时我们知道了我们的线程中已经有了一个Looper
,并且为这个Looper设置好了一个MessageQueue。因为一个线程只能有一个Looper
,所以一个Looper也就只能拥有一个MessageQueue。但是AcitivityThread中,经过Looper.loop()
后就再也没有下文了?所以,这个loop()
又是干啥的呢?
繁忙的loop()
1 | /** Return the Looper object associated with the current thread. Returns null if the calling thread is not associated with a Looper.*/ |
因为其中一个大大的死循环,所以调用了loop()
之后,其后就没有实际代码了。这个死循环就是用来处理Message,不断地从队列中取,然后不断地进行分发到相应的Handler,进行处理。此时,这个for(;;)
所处的线程,就是你调用Looper.loop()时所在的线程。因此,它分发msg给了相应的Handler的handleMessage之后,还是在此线程中执行。然后,在想想,发送Message时所处在的线程,就焕然大悟这个异步操作了。
1 | /** |
handleMessage(msg)
不正是我们创建Handler时候,所覆盖的方法吗?
进一步思考,如果我只在主线程中new Handler,那么Looper就是主线程,所有的msg都会在主线程中被处理;那如果我想让msg在子线程中被处理呢?当然可以Looper.prepare()
巴拉巴拉,然后Looper.loop()
。但是Android还为我们提供了一个更为便捷的封装。那就是HandlerThread
。
子线程处理msg的封装HandlerThread
1 |
|
源代码比较短。它继承自Thread,并在run方法中初始化好了Looper,可以通过其getThreadHandler()
方法,获取到与该Looper所绑定的Handler,然后sendMessage(),最后在该线程中处理msg。
小结
因此,一个Thread可以有一个Looper和一个MessageQueue,一个Looper却可以与多个Handler绑定,但是一个Handler只能与一个Looper绑定。原因可以从Handler的构造方法中寻找的。