经典例子
首先是创建一个 Handler
1 | private Handler mHandler = new Handler() { |
在子线程中更新UI
1 | new Thread(new Runnable() { |
找到看源码的切入点
从这个经典的例子中来读源码,源码的切入点就可以从 Handler 的无参构造方法开始看起。
1 | public Handler() { |
这个无参构造方法调用了另外一个构造方法 this(null, false);。去除方法体里面其他的无关代码,得到关键代码如下
1 | /** |
看一下这个两句话是什么意思
1 | mLooper = Looper.myLooper(); |
看一下 Looper.myLooper() 的实现
1 | /** |
只是返回一个 Looper 对象而已,也就是获取了当前线程保存的 Looper 实例,看一下 sThreadLocal 这个变量是什么东西,用 CTRL-F 查找一下代码,整个 Looper 类中只有这几个地方有用到 sThreadLocal 变量
1 | // sThreadLocal.get() will return null unless you've called prepare(). |
主要看 prepare(boolean quitAllowed) 这个方法就可以了。sThreadLocal 是一个 ThreadLocal 对象,可以在一个线程中存储变量。
1 | 1 public static final void prepare() { |
第 5 行新建一个 Looper 对象放入 sThreadLocal 对象。2~4 行判断 sThreadLocal 中是否已经有 Looper 对象了,如果有就会抛出异常,说明一个线程只能有一个 Looper 对象。
看一下 new Looper(true) 这句话做了什么事情
1 | private Looper(boolean quitAllowed) { |
创建了一个消息队列 MessageQueue,还有获取了当前线程的实例。
到这里就不知道要看什么了,那就看一下类的注释好了,说不定有线索
1 | /** |
注释说 Looper 类是用来为一个线程创建一个消息循环用的。线程默认是没有消息循环的,可以调用 prepare() 创建消息循环,调用 loop() 去处理消息。通常情况下是用 Handler 类和消息循环交互。
这段注释提到了 prepare() 和 loop() 方法。prepare() 方法刚才已经看过了,那就看一下 loop() 方法做了什么。去掉一些无关的语句,得到
1 | /** |
这段代码的大概意思是
无限循环去遍历消息队列,取出队列中的
Message对象,然后调用msg.target.dispatchMessage(msg);,然后再把Message回收了
msg.target.dispatchMessage(msg); 这句话不明白,进入 Message 类看一下是什么东西。
1 | Handler target; |
从这里可以知道 Handler 和 Message 有一腿。
再看一下 Handler 类中 dispatchMessage(msg) 方法的实现
1 | /** |
这里应该就是调用各种回调方法了。从刚才写的经典例子中,我没有写任何的回调方法,因此这个方法体最后就进入了 handleMessage(msg); 方法中。看一下这个方法做了什么
1 | /** |
这个方法就是经典例子中重写的 handlerMessage 方法
1 | private Handler mHandler = new Handler() { |
现在梳理一下
- Looper 在
prepare()方法中创建一个Looper实例和一个消息队列 - Looper 在
loop()方法中不断地遍历消息队列,调用MessageQueue.next()取出Message,然后调用和Message绑定的Handler的dispatchMessage方法,去调用各种回调方法
现在几个疑问
Message是怎么创建的?Message是怎么进入Looper的消息队列的?Handler是怎么和Message绑定在一起的?
Message 是怎么创建的?
在使用 Handler 异步更新 UI 的时候,都会创建 Message 携带我们想要传递的数据。Message 有几个属性可以携带数据:
what、arg1、arg2、obj 。用法如下
1 | Message msg = mHandler.obtainMessage(); |
看一下 mHandler.obtainMessage() 做了什么
1 | /** |
又调用了 Message.obtain(this),再进入看看
1 | /** |
可以看到,为了提高性能,先调用 Message m = obtain(); 从消息池中取出一个 Message,然后再将 Handler 实例赋值给 Message 的 target 属性,这样就将 Handler 和 Message 绑定在一起了。
Message是怎么和Handler绑定在一起的?
从上面的
1 | public static Message obtain(Handler h) { |
我们已经知道了
Message 是怎么进入 Looper 的消息队列的?
看一下刚才写的代码
1 | Message msg = mHandler.obtainMessage(); |
Message 已经通过 Handler.obtainMessage() 获取到了,然后就是调用 Handler.sendMessage(Message msg) 发出消息。看一下 mHandler.sendMessage(msg); 做了什么
1 | /** |
又调用了另外一个方法,再点进去看看
1 | /** |
怎么又调用了另外一个方法,再点进去看看
1 | /** |
从这个方法体可以看出,是把刚才我们创建的 Message 加入队列中去。这里有句话
1 | MessageQueue queue = mQueue; |
这个消息队列是哪里来的?Handler 类中怎么会有消息队列?用 CTRL-F 查找一下 mQueue = 在哪里出现过
1 | /** |
在 Handler 的这个构造方法中找到了 mQueue 被赋值的地方
1 | mLooper = Looper.myLooper(); |
它是通过调用 Looper 的静态方法 myLooper() 获取到 Looper 对象,然后再通过 Looper 对象进而获取到 Looper 的消息队列的。
知道了 Handler 中的 mQueue 是怎么获取到的,那就再接着往下看
1 | public boolean sendMessageAtTime(Message msg, long uptimeMillis) { |
这个方法又调用了 enqueueMessage(queue, msg, uptimeMillis),点进去看一下做了什么
1 | private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) { |
在这里,这个方法调用了消息队列 MessageQueue 的 enqueueMessage() 方法把 Message 放进去。
enqueueMessage() 这个方法的方法体代码太多,我看不懂。不过只需要知道一件事,这个方法的指责是将 Message 放入 MessageQueue 就好了。
梳理Looper、Handler、Message、MessageQueue关系
- Looper 在
prepare()方法中创建一个Looper实例和一个消息队列 - Looper 在
loop()方法中不断地遍历消息队列,调用MessageQueue.next()从消息队列中取出Message,然后调用和Message绑定的Handler的dispatchMessage方法,去调用各种回调方法 Handler的构造方法会获得当前线程中的Looper实例,进而和MessageQueue关联在一起Handler调用sendMessage(Message msg),会将msg.target赋值为自己,然后放入MessageQueue中- 我们在构造
Handler实例的时候,会重写handleMessage方法,也就是msg.target.dispatchMessage(msg)最终调用的方法
Looper.prepare()又是谁来调用的?
从刚才的源码中,可以知道是通过调用 Looper.prepare() 来创建 Looper 对象。同时,从经典例子中发现,我们没有去调用 Looper.prepare() 方法。那么 Looper.prepare() 又是谁来调用的?
查了下资料,Android 应用程序的入口是 ActivityThread 的main 函数,那就去看一下 main 函数是怎么实现的,可以看到,在方法体中有一句 Looper.prepareMainLooper();
1 | public final class ActivityThread { |
看一下这个方法的方法体
1 | /** |
它调用了 prepare() 创建了 Looper 实例。
以上就是 UI 线程 Looper 创建过程