基于 Android 7.1.1 源码,分析 Looper 的架构和原理。
1 成员变量
1 | static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>(); |
线程本地变量,用于保存每个线程创建出来的 Looper 对象!
1 | private static Looper sMainLooper; |
ui 线程的 Looper 对象!
1 | final MessageQueue mQueue; |
消息队列,每一个 Looper 有一个消息队列,Looper 会循环遍历该消息队列,分发消息!
1 | final Thread mThread; |
当前线程,也就是该 Looper 所属的线程!
1 | private Printer mLogging; |
和调试监控相关的,我们不过多关注!
2 Looper.prepare
我们知道,如果要给一个指定的线程创建一个 handler,该线程必须要有一个 Looper,创建 looper 的方法就是 prepare:
1 | public static void prepare() { |
该方法会给当前的线程创建一个 Looper 对象!
注意第二个方法是私有方法!
2.1 new Looper
1 | private Looper(boolean quitAllowed) { |
quitAllowed 表示该队列是否可以推出!
2.2 Looper.prepareMainLooper
对于 ui 主线程,Looper 有一个方法方法专门用于创建其对应的 Looper:1
2
3
4
5
6
7
8
9public static void prepareMainLooper() {
prepare(false); // 调用 prepare 方法,创建 Looper
synchronized (Looper.class) {
if (sMainLooper != null) {
throw new IllegalStateException("The main Looper has already been prepared.");
}
sMainLooper = myLooper(); // 将 ui 线程的 Looper 保存到 sMainLooper 中!
}
}
可以看到,ui 线程的 MessageQueue 是不能退出的!
2.2.1 Looper.myLooper
这里调用了 myLooper 方法,用于返回和当前线程相关联的 Looper 对象!
1 | public static Looper myLooper() { |
方法很简单,不多说了!
3 Looper.loop
当 Looper 创建好后,需要调用 loop 方法,使其进入消息循环中:
1 | public static void loop() { |
其实方法流程很简单:
- 获得当前线程的 Looper 对象,如果为 null,那就要抛出异常
- 获得该 Looper 所有的消息队列
- 通过 MessageQueue.next 方法,获得下一个要分发的 message!
- 调用 msg.target.dispatchMessage 方法,分发消息!
- 回收 Message,我们知道 MessageQueue 中的 Message 可以复用的!
注意:
- queue.next() 方法会从消息队列中取出消息对象 Message,如果 MessageQueue 中没有任何 Message 的话,该方法将会阻塞等待新的消息。
- 当我们想要退出消息循环的时候,调用 quit 方法,那么 queue.next() 会返回一个 null 的 Message,这样就退出了!
4 Looper.quit
Looper 提供了 quit 方法,用于结束消息循环:
1 | public void quit() { |
最终都调用了 MessageQueue 的 quit 方法,来结束消息循环!
quit 和 quitSafely 的区别是,quit 方法是不安全的,而 quitSafely 会在停止 Looper 的时候把当前时间点之后的已经达到处理时间点的消息处理完后才停止 Looper!
5 其他方法
Looper 提供了一些其他的方法:
- 获得主线程的 Looper 对象!
1 | public static Looper getMainLooper() { |
- 获得 Looper 对象所在的线程!
1 | public Thread getThread() { |
- 获得 Looper 对象的消息队列!
1 | public MessageQueue getQueue() { |
- 判断 Looper 对象是否属于当前线程!
1
2
3public boolean isCurrentThread() {
return Thread.currentThread() == mThread;
}
总体来看,Looper 的实现并不复杂,接下来,我们来看看 MessageQueue!