Handler 的原理看这张图就可以知道
Handler sendMessage(msg) 会调用 sendMessageAtTime(msg, uptimeMillis), 在这里会调 enqueueMessage(queue, msg, uptimeMillis), 其中 queue 是 handler 的全局变量 mQueue, 在构造函数里,有这两行代码
mLooper = Looper.myLooper();
mQueue = mLooper.mQueue;
最终会调用 queue.enqueueMessage(msg, uptimeMiilis), 在这个方法里会设置 msg.target = this, 然后把该 msg 插入 queue 中。
Looper.loop() 方法会循环取 mQueue 中的值,取到消息就分发执行。
思考:
1. 为什么子线程中直接 new Handler() 会报异常呢?
mLooper = Looper.myLooper();
if (mLooper == null) {
throw new RuntimeException(
"Can't create handler inside thread that has not called Looper.prepare()");
}
上面一段代码是 Handler 的构造方法中的一段代码,创建 Handler 时会取 Looper.myLooper(), 返回的是当前线程的 Looper 对象,由于子线程中还没有 Looper 对象,所以会报异常。主线程是在 ActivityThread 的 main 方法中创建的 Looper 对象。
注:子线程先调用 Looper.prepare() 之后也可以创建 Handler, 但是更新 UI 时不建议这样做,因为这样还是在子线程中更新的 UI, 还是线程不安全的。
2. 一个线程只能有一个 Looper 对象?
创建 Looper 对象是调用 Looper.prepare, 其源码如下:
if (sThreadLocal.get() != null) {
throw new RuntimeException("Only one Looper may be created per thread");
}
sThreadLocal.set(new Looper(quitAllowed));
这里使用 ThreadLocal 保证了一个线程只有一个 Looper 对象。
3. 主线程的 Looper.loop 无限循环为什么不会造成 ANR?
for (;;) {
Message msg = queue.next(); // might block
if (msg == null) {
// No message indicates that the message queue is quitting.
return;
}
}
之所以不会造成 ANR 就是因为消息的阻塞和唤醒机制,重点在 queue.next() 获取新的消息时,
Message next() {
// ...
for (;;) {
//...
nativePollOnce(ptr, nextPollTimeoutMillis); // 重点在这里,
}
}
nativePollOnce 会引起阻塞消息队列,在唤醒时间到的时候再唤醒。
在此处会唤醒,还有在 enqueueMessage 时,如果有在消息队列头部插入的话,就会调用唤醒 nativeWake(mPtr).
推荐这篇文章https://my.oschina.net/youranhongcha/blog/492591,讲的很清楚。
4. Handler发送延迟消息的原理
发送延迟消息,最终一样都是调用 sendMessageAtTime,给设置一个唤醒的时间,在时间到了就唤醒。
5. 是否可以停止消息循环
Looper 提供了一个 quit() 方法,那么主线程的 Looper 能否停止循环呢,试图调 Looper.getMainLooper().quit() 会抛出异常 “Main thread not allowed to quit.”,原因:
// MessageQueue
void quit(boolean safe) {
/* mQuitAllowed 是在创建 MessageQueue 时指定的,主线程创建 Looper 是调用 Looper.prepareMainLoop(), 传进来的是 false, 所以停止主线程的 Looper 会抛异常 */
if (!mQuitAllowed) {
throw new IllegalStateException("Main thread not allowed to quit.");
}
// ...
}
子线程的 Looper 创建是调 Looper.prepare, 传的 mQuitAllowed 是 true, 所以子线程是可以停止的