经典例子
首先是创建一个 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
创建过程