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.
