Android 消息机制深入源码分析 [ 一 ]
Android 消息机制之 ThreadLocal 深入源码分析 [ 二 ]
Android 消息机制之 Looper 深入源码分析 [ 三 ]
Android 消息机制之 Message 与消息对象池的深入源码分析 [ 四 ]
Android 消息机制之 MessageQueue 深入源码分析 [ 五 ]
Android 消息机制之初识Handler [ 六 ]
Android 消息机制之 Handler 发送消息的深入源码分析 [ 七 ]
Android 消息机制之 MessageQueue.next() 消息取出的深入源码分析 [ 八 ]
Android 消息机制之消息的其他处理深入源码分析 [ 九 ]
Android 消息机制总结 [ 十 ]
上一章学习了消息机制中的 ThreadLocal, 本章接着来学习消息机制中的 Looper. 开篇也是先抛出几个问题.
1. 问题
- 可以在一个线程多次执行 Looper. prepare() 吗? 为什么 ?
- Looper.prepareMainLooper 是用来做什么的. 为什么我们在主线程可以直接使用 Handler, 而不需要调用 Looper. prepare() ?
- Looper.quit 与 Looper.quitSafely() 有什么区别.
2. 例
先来一个典型的关于 Looper 的例子. (因为本章只分析 Looper, Handler 会放到后面章节分析.)
class LooperThread extends Thread{
public Handler mHandler;
public void run(){
//分析1
Looper.prepare();
mHandler = new Handler(){
public void HhandleMessage(Message msg){
Message msg = Message.obtain();
}
};
//分析 2
Looper.loop();
}
}
3. 分析 1
进入 Looper.prepare()
方法. 代码在 Looper.java
97行.
public static void prepare() {
prepare(true);
}
- 官方注释:
初始化当前线程和
Looper
, 这样可以在实际开始启动循环(Loop()
) 之前创建一个Handler
, 并且关联一个Looper
. 要确保最先调用这个方法, 然后才调用loop()
, 并且通过调用quit
结束.
这也正对应了我们例子中写的, 要先调用 Looper.prepare()
, 然后创建一个 Handler
, 最后才调用 Looper.loop()
启动循环.
继续回到 public static void prepare()
, 在方法内部又调用了一个 private static void prepare(boolean quitAllowed)
, 注意, 这两个方法名是相同的, 不过一个是 public
无参的, 一个是 private
有参的. 而我们调用的是 public
无参的, 在这个方法内部, Android 系统又调用了 private
有参的, 并且传入的参数为 true
, 实际上这个参数一直向下传递到了 MessageQueue
的构造函数中, 表示允许退出消息队列. 也就是说我们在外部调用 Looper.prepare()
后, 系统为我们创建的消息队列是允许退出的.
进入到 private static void Looper.prepare(true)
static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();
private static void prepare(boolean quitAllowed) {
if (sThreadLocal.get() != null) {
throw new RuntimeException("Only one Looper may be created per thread");
}
sThreadLocal.set(new Looper(quitAllowed));
}
- 解析:
又看到了
ThreadLocal
, 所以我们需要先学习了第二章, 再来看这个就会明白是什么意思了. 这个sThreadLocal
中存储的是Looper
对象.
调用ThreadLocal.get()
看获取到的值是否为null
, 如果为不为null
说明当前线程已经创建过Looper
对象了. 那么就会直接抛出异常. [每个线程只能执行一次 Looper], 那么这就是问题1 的答案,
如果获取到值为null
, 说明当前线程还未创建Looper
, 就会创建一个Looper
对象, 并放到ThreadLocal
中.
接着我们进入 Looper
的构造函数, 看看都做了些什么.
进入到 Looper(boolean quitAllowed)
private Looper(boolean quitAllowed) {
mQueue = new MessageQueue(quitAllowed);
mThread = Thread.currentThread();
}
- 解析
看到构造函数内部创建了一个
MessageQueue
, 传入boolean quitAllowed
设置是否允许退出, 然后赋值给本地变量mQueue
, 然后获取当前线程, 也赋值给本地变量mThread
.
这样这个创建的
Looper
内就持有MessageQueue
消息队列 与Thread
当前线程的引用. 也就实现了 Looper 与 MessageQueue 和 Thread 的关联 ()
通过 Looper
的构造函数可以知道, Looper
是无法被直接创建的, (构造函数是 private
类型的). 我们必须通过 Looper
的两个静态方法, prepare()/prepareMainLooper()
来间接的创建. 那么 prepareMainLooper
是用来做什么的呢, 一起来看一下
进入到 prepareMainLooper()
Looper.java 114 行
public static void prepareMainLooper() {
//设置不允许退出的 Looper
prepare(false);
synchronized (Looper.class) {
//主线程有且只能调用一次 prepareMainLooper()
if (sMainLooper != null) {
throw new IllegalStateException("The main Looper has already been prepared.");
}
//赋值给 sMainlooper
sMainLooper = myLooper();
}
}
- 官方注释
初始化当前线程的
looper
. 并且标记为一个程序的主Looper
, 由Android
环境来创建应用程序的主Looper
, 因此这个方法不能由自己调用,只能是Android
自己调用
什么意思呢, 简单来说就是初始化主线程的 Looper
, 而且只能由 Android
系统调用.
- 解析
一开始也调用了
private static void prepare(boolean quitAllowed)
传入了 false, 结合上面的分析, 是创建了一个Looper
对象, 然后set
到当前线程的ThreadLocal
中, 并且在Looper
构造函数中创建的MessageQueue
是无法退出的.
接着做了sMainLooper
的非空判断,sMainLooper
看名字就知道这是什么了, 就是主线程的 Looper, 如果非空, 就直接抛出异常, 所以这个sMainLooper
一开始必须是空, 因为主线程有且只能调用一次prepareMainLooper()
, 如果sMainLooper
非空, 说明prepareMainLooper()
已经被系统调用过了. 为空, 就调用myLooper()
方法, 将返回的Looper
对象赋值给sMainLooper
. 那么prepareMainLooper()
是在什么时候被调用的呢, 答案是在ActivityThread,java
中的main
方法.
那么问题 2 的答案也有了:
prepareMainLooper()
是用来为主线程创建Looper
的, 系统默认为我们创建好了, 所以我们可以在主线程中直接使用Handler
进入到 myLooper()
Looper.java 的 254行
public static @Nullable Looper myLooper() {
return sThreadLocal.get();
}
- 解析
从当前线程的
ThreadLocal
中取出Looper
对象并返回. 这里的sThreadLocal.get();
与prepare(boolean)
方法里面的sThreadLocal.set(new Looper( quitAllowed))
对应的, 一个设置值一个取值.
现在完整的分析 1, 已经分析完了, 主要就是为当前线程创建一个Looper
对象放入到当前线程的 ThreadLocal
中, 并同时持有 MessageQueue
对象与当前线程对象. 其中创建的消息队列, 是可以退出的. 也知道了我们为什么可以在主线程直接使用 Handler
, 就是因为在ActivityThread.main
方法中调用了 Looper.prepareMainLooper()
方法, 系统已经为主线程创建好了 Looper
.
Looper
已经创建好了, 那么接下来就是开始循环了. 接下来开始看分析 2 Looper.loop()
.
4. 分析 2
Looper.java 137 行, 进入 Looper.loop()
方法
public static void loop() {
//获取当前线程 ThreadLocal 存储的 Looper
final Looper me = myLooper();
if (me == null) {
//没有 Looper 直接抛出异常
throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");
}
//获取当前 Looper 持有的消息队列
final MessageQueue queue = me.mQueue;
//确保权限检查基于本地进程, 而不是基于最初调用进程
Binder.clearCallingIdentity();
final long ident = Binder.clearCallingIdentity();
//进入 loop 主循环方法
//一个死循环, 不听的处理消息队列中的消息,消息的获取是通过 MessageQueue 的 next 方法
for (;;) {
//可能会阻塞
Message msg = queue.next();
//注: 这里的 msg = null, 并不是说没消息了, 而是如果消息队列正在关闭的情况下, 会返回 null.
if (msg == null) {
return;
}
...
//用于分发消息,调用 Message 的 target 变量(也就是 Handler)的 dispatchMessage方法处理消息
try {
msg.target.dispatchMessage(msg);
dispatchEnd = needEndTime ? SystemClock.uptimeMillis() : 0;
} finally {
if (traceTag != 0) {
Trace.traceEnd(traceTag);
}
}
...
...
//将 Message 回收标记后放入消息池.
msg.recycleUnchecked();
}
}
- 解析
简单来说就是 Loop() 进入循环模式, 不断的重复下面的操作, 直到没有消息时, 退出.
- 读取
MessageQueue
的下一条数据. 并赋值给Message
.- 调用把
Handler.dispatchMessage()
方法把Message
分发给相对应的target
也就是Handler
.- 把分发后的
Message
打上标记后放到消息池回收. 以便重复使用.
这里面有几个重要的方法, 后面文章会解释到.
-
MessageQueue.next
方法,读取下一条Message
, 有阻塞 -
Message.Handler.dispatchMessage(Message msg)
方法:消息分发 -
Message.recycleUnchecked()
方法:消息放入消息池
5. Looper.quit 与 Looper.quitSafely 问题 3 的答案.
上面只是开启了循环方法, Looper 也提供了两个退出循环的方法, 分别是 quit
与 quitSafely
我们调用的 Looper 的两个退出循环的方法.
public void quit() {
mQueue.quit(false);
}
public void quitSafely() {
mQueue.quit(true);
}
MessageQueue.quit(boolean safe)
void quit(boolean safe) {
//当 mQuitAllowed 为 false,表示不允许退出,强行调用 quit 会有异常
//mQuitAllowed 是在 Looper 构造函数里面构造 MessageQueue() 以参数传入的.
if (!mQuitAllowed) {
throw new IllegalStateException("Main thread not allowed to quit.");
}
//同步代码块
synchronized (this) {
//防止多次执行退出操作
if (mQuitting) {
return;
}
mQuitting = true;
if (safe) {
//移除所有尚未触发的所有消息
removeAllFutureMessagesLocked();
} else {
//移除所有消息
removeAllMessagesLocked();
}
// We can assume mPtr != 0 because mQuitting was previously false.
nativeWake(mPtr);
}
}
- quit: 会将消息队列中的所有消息移除 (延迟消息和非延迟消息)
-
Looper
的quit()
方法内部的本质是调用MessageQueue
的quit(boolean safe)
方法. 传入参数是false
.
-
- quitSafely: 会将消息队列所有延迟消息移除, 非延迟消息则派发出去让
Handler
处理.-
Looper
的quitSafely()
方法内部调用的本质也是MessageQueue
的quit(boolean safe)
方法. 只不过传入参数是true
.
-
-
quitSafely
相比于quit
方法安全支出在于清空消息之前会派发出去所有的非延迟消息.
到这里. 相信对 Looper 都有了自己的认识, 比如为什么要先调用 Looper.prepare(), 再调用 Looper.loop(), 以及为什么主线程可以直接使用Hander, 而不需要调用 Looper.prepare(). 还有 loop() 循环中是怎么发送消息的. 下一章接着分析消息机制中的 Message.