第一次被问到这个问题的时候,就再想,为什么会问这问题呢?
回想了一遍关于Android Handler,Message, MessageQueue 和 Looper 的相关知识,才明白为什么会有这样的问题。
因为Looper.loop()
消息循环是个死循环,会不断的在这里处理MessageQueue
消息队列中的消息
/**
* Run the message queue in this thread. Be sure to call
* {@link #quit()} to end the loop.
*/
public static void loop() {
final Looper me = myLooper();
if (me == null) {
throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");
}
final MessageQueue queue = me.mQueue;
/*
* 删除了一些无关代码
*/
for (;;) {
// 这里循环从queue中获取Message消息,如果没有消息的话这里会阻塞
Message msg = queue.next(); // might block
if (msg == null) {
// No message indicates that the message queue is quitting.
return;
}
try {
//msg.target 对象是发送Message消息的Handler对象,通过Handler的dispatchMessage进行消息处理
msg.target.dispatchMessage(msg);
dispatchEnd = needEndTime ? SystemClock.uptimeMillis() : 0;
} finally {
if (traceTag != 0) {
Trace.traceEnd(traceTag);
}
}
/*
* 删除了一些无关代码
*/
// 消息处理完后,将Message放入对象池中,这样Message.obtain()获取Message时候可以减少对象的创建
msg.recycleUnchecked();
}
}
1, 这里涉及到Linux pipe/epoll机制
,简单说就是在主线程的MessageQueue没有消息时,便阻塞在loop的queue.next()中的nativePollOnce()方法里。此时主线程会释放CPU资源进入休眠状态,直到下个消息到达或者有事务发生,通过往pipe管道写端写入数据来唤醒主线程工作。这里采用的epoll机制,是一种IO多路复用机制,可以同时监控多个描述符,当某个描述符就绪(读或写就绪),则立刻通知相应程序进行读或写操作,本质同步I/O,即读写是阻塞的。
2,在进入死循环之前创建了新binder线程,在代码ActivityThread.main()中,
thread.attach(false);
便会创建一个Binder线程(具体是指ApplicationThread,Binder的服务端,用于接收系统服务AMS发送来的事件),该Binder线程通过Handler将Message发送给主线程.
public static void main(String[] args) {
....
//创建Looper和MessageQueue对象,用于处理主线程的消息
Looper.prepareMainLooper();
//创建ActivityThread对象
ActivityThread thread = new ActivityThread();
//建立Binder通道 (创建新线程)
thread.attach(false);
Looper.loop(); //消息循环运行
throw new RuntimeException("Main thread loop unexpectedly exited");
}
所以 在 Looper.loop()
中阻塞的时候不会卡死主线程。
这里同样是涉及到Linux pipe/epoll机制
,可参考不卡死主线程的原因。对Linux pipe/epoll机制
有兴趣可google相关的介绍
下面是ANR在官方文档的介绍:
ANR 如果 Android 应用的界面线程处于阻塞状态的时间过长,会触发“应用无响应”(ANR) 错误。如果应用位于前台,系统会向用户显示一个对话框。ANR 对话框会为用户提供强行退出应用的选项。
可以看到ANR的发生是在程序在处理Message
消息的时候,用的时间太长,导致 Looper.loop()
无法进入下一个循环处理后续的消息。
所以 ANR 和 Looper.loop()
的阻塞 是两个不同的概念。
Looper.loop()
阻塞是消息队列为空,在等待新的消息,然后进行处理。END!
参考: