在android中如果主线程(UI线程)中进行耗时操作会引发ANR(Application Not Responding)异常,产生ANR的原因一般有两种:
当前的事件没有机会得到处理(即主线程正在处理前一个事件,没有及时的完成或者looper被某种原因阻塞住了)
当前的事件正在处理,但没有及时完成
比如onCreate()中进行了耗时操作,导致点击、触摸等不响应,就会产生ANR。为了避免ANR异常,android使用了Handler消息处理机制,让耗时操作在子线程运行,需要UI线程进行处理的操作给UI线程发送消息。
我们知道Handler发消息给UI线程就可以处理消息,UI线程维护着一个Looper和一个消息队列,Looper不停的拿消息队列的消息去分发处理。到这里问题来了:如果这么做的话,UI线程岂不是要一直死循环轮询消息队列拿消息?死循环不是造成ANR吗?
=>是的,确实是这样。
ActivityThread的main()
public static void main(String[] args) { Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "ActivityThreadMain"); ... Looper.loop(); throw new RuntimeException("Main thread loop unexpectedly exited"); }
Looper.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; // Make sure the identity of this thread is that of the local process, // and keep track of what that identity token actually is. Binder.clearCallingIdentity(); final long ident = Binder.clearCallingIdentity(); for (;;) { Message msg = queue.next(); // might block ... msg.target.dispatchMessage(msg); ... msg.recycleUnchecked(); } }
Looper.loop()是在死循环处理消息,如果main方法中没有looper进行循环,那么主线程一运行完毕就会退出,那才不正常了呢!?
所以,ActivityThread的main方法主要就是做消息循环,一旦退出消息循环,那么你的应用也就退出了。
那么问题重新问一遍:那为什么这个死循环不会造成ANR异常呢?
其实原因是显然的,因为Android是由事件驱动的,Looper.loop() 不断地接收事件、处理事件,每一个点击触摸或者说Activity的生命周期都是运行在 Looper.loop() 的控制之下,如果它停止了,应用也就停止了。只能是某一个消息或者说对消息的处理阻塞了 Looper.loop(),而不是 Looper.loop() 阻塞它。
也就说我们的代码其实就是在这个循环里面去执行的,当然不会阻塞了。
而且主线程Looper从消息队列读取消息,当读完所有消息时,主线程阻塞。子线程往消息队列发送消息,并且往管道文件写数据,主线程即被唤醒,从管道文件读取数据,主线程被唤醒只是为了读取消息,当消息读取完毕,再次睡眠。因此loop的循环并不会对CPU性能有过多的消耗。
总结:Looer.loop()方法可能会引起主线程的阻塞,但只要它的消息循环没有被阻塞,能一直处理事件就不会产生ANR异常。