首先,关于Handler相关机制,可以参考我之前整理的[Android] Handler消息传递机制。
然后我们分解问题,一点点去看这个问题。
Looper.loop和主线程的关系
分析Handler和应用启动的时候讲过,在创建ActivityThread时,主线程会创建Looper,并且进入Loop循环:
public static void main(String[] args) {
Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "ActivityThreadMain");
SamplingProfilerIntegration.start();
// CloseGuard defaults to true and can be quite spammy. We
// disable it here, but selectively enable it later (via
// StrictMode) on debug builds, but using DropBox, not logs.
CloseGuard.setEnabled(false);
Environment.initForCurrentUser();
// Set the reporter for event logging in libcore
EventLogger.setReporter(new EventLoggingReporter());
// Make sure TrustedCertificateStore looks in the right place for CA certificates
final File configDir = Environment.getUserConfigDirectory(UserHandle.myUserId());
TrustedCertificateStore.setDefaultUserDirectory(configDir);
Process.setArgV0("");
// 这里sMainLooper赋值
Looper.prepareMainLooper();
ActivityThread thread = new ActivityThread();
thread.attach(false);
if (sMainThreadHandler == null) {
sMainThreadHandler = thread.getHandler();
}
if (false) {
Looper.myLooper().setMessageLogging(new
LogPrinter(Log.DEBUG, "ActivityThread"));
}
// End of event ActivityThreadMain.
Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
Looper.loop(); // Looper进入循环
throw new RuntimeException("Main thread loop unexpectedly exited");
}
loop()函数代码就不贴了,就是一个while(true)
的死循环。
所以Looper和主线程的关系是,主线程创建的时候会创建一个MainLooper,并且进入循环。
Looper.prepareMainLooper();
Looper.loop();
主线程阻塞
关于死循环
主线程进入一个死循环,是不是就会被阻塞?
首先,思考一下,如果我们创建一个线程做定时检查某个状态,是不是也会给这个子线程做一个死循环,不断地去循环检查状态。当不需要这个线程的时候,改变flag让这个子线程退出循环并销毁。
想到这就理解,主线程也是一个线程,它也要维持自己的周期,所以也是需要一个死循环的。所以死循环并不是那么让人担心。
关于阻塞
这就涉及到Looper的作用了,Looper里持有一个MessageQueue,这个消息队列存放着外部发来的消息,当有消息过来的时候,Looper就会按顺序把消息一个一个拿出来,进行处理。Looper的loop循环就是一个拿消息的循环。
也就是说,如果你给我发消息,我会立即去拿消息并且做响应。你不给我消息,我就会阻塞,减少CPU消耗(涉及到epoll)。
那么主线程会响应什么消息呢?在ActivityThread里有一个命名为H的handler,它处理所有Activity生命周期有关的事件。因为主线程就是UI线程,当UI发生变化,相关消息就会传进来,Looper就会处理消息。
所以:
Looper的阻塞,前提是没有输入事件,此时MessageQueue是空的,Looper进入空闲,线程进入阻塞,释放CPU,等待输入事件的唤醒。
聊聊ANR
其实担心这个问题的人很多都是被ANR搞怕了,因为ANR就是UI线程做耗时操作了导致卡死状态,然后很多人就在想是不是UI线程进入Loop死循环后,就出现卡死,其实这两个并不是一个问题。
先上结论和上面的做个对比:
UI耗时导致卡死,前提是要有输入事件,此时MessageQueue不是空的,Looper正常轮询,线程并没有阻塞,但是该事件执行时间过长(一般5秒),而且与此期间其他的事件(按键按下,屏幕点击..)都没办法处理(卡死),然后就ANR异常了。
ANR机制的目的还是为了监测主线程的耗时操作,譬如密集CPU运算、大量IO、复杂界面布局等,因为这些都会降低应用程序的响应能力。所以从理念上也能理解,loop死循环只是简单地处理轻量的消息操作,和ANR并没有关系。