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.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));
}
- 解析:
接着我们进入 Looper
的构造函数, 看看都做了些什么.
进入到 Looper(boolean quitAllowed)
private Looper(boolean quitAllowed) {
mQueue = new MessageQueue(quitAllowed);
mThread = Thread.currentThread();
}
- 解析
通过 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
, 而且只能由 Android
系统调用.
- 解析
进入到 myLooper()
Looper.java 的 254行
public static @Nullable Looper myLooper() {
return sThreadLocal.get();
}
- 解析
现在完整的分析 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();
}
}
- 解析
这里面有几个重要的方法, 后面文章会解释到.
-
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.