引子:
正如我们所知,在android中如果主线程中进行耗时操作会引发ANR(Application Not Responding)异常。
造成ANR的原因一般有两种:
只有当应用程序的UI线程响应超时才会引起ANR,超时产生原因一般有两种
1. 当前的事件没有机会得到处理,例如UI线程正在响应另一个事件,当前事件由于某种原因被阻塞了。
3. 当前的事件正在处理,但是由于耗时太长没能及时完成。
一般造成ANR的场景:
根据ANR产生的原因不同,从本质上讲,产生ANR的原因有三种:
1. KeyDispatchTimeout:原因就是View的点击事件或者触摸事件在5s内无法得到响应。
3. BroadcastTimeout:原因是BroadcastReceiver的onReceive()函数运行在主线程中,在10s内无法完成处理。
4. ServiceTimeout:原因是Service的各个生命周期函数在20s内无法完成处理。
为了避免ANR异常,android使用了Handler消息处理机制。让耗时操作在子线程运行。
因此产生了一个问题,主线程中的Looper.loop()一直无限循环检测消息队列中是否有新消息为什么不会造成ANR?
ActivityThread.java 是主线程入口的类,这里你可以看到写Java程序中司空见惯的main方法,而main方法正是整个Java程序的入口。
ActivityThread源码
public static final void main(String[] args) {
...
//创建Looper和MessageQueue
Looper.prepareMainLooper();
...
//轮询器开始轮询
Looper.loop();
...
}
显而易见的,如果main方法中没有looper进行循环,那么主线程一运行完毕就会退出,此时app就退出了,无法继续与用户交互。
Looper.loop()方法
public static void loop() {
//拿到looper对象
final Looper me = myLooper();
...
//获取message queue
final MessageQueue queue = me.mQueue;
...
//循环取出message
for (;;) {
Message msg = queue.next(); // might block
if (msg == null) {
// No message indicates that the message queue is quitting.
return;
}
...
//获取到message后dispatch进行处理,接着执行for循环处理其他message
msg.target.dispatchMessage(msg);
...
msg.recycleUnchecked();
}
}
总结:因为Android 的是由事件驱动的,looper.loop() 不断地接收事件、处理事件,每一个点击触摸或者说Activity的生命周期都是运行在 Looper.loop() 的控制之下,如果它停止了,应用也就停止了。只能是某一个消息或者说对消息的处理阻塞了 Looper.loop()。
再来看一下ANR产生的本质原因:当前的事件没有机会得到处理;也就是说只要事件在特定时间内得到执行就不会造成ANR。
handleMessage方法部分源码
public void handleMessage(Message msg) {
if (DEBUG_MESSAGES) Slog.v(TAG, ">>> handling: " + codeToString(msg.what));
switch (msg.what) {
case LAUNCH_ACTIVITY: {
Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "activityStart");
final ActivityClientRecord r = (ActivityClientRecord) msg.obj;
r.packageInfo = getPackageInfoNoCheck(r.activityInfo.applicationInfo, r.compatInfo);
handleLaunchActivity(r, null);
Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
}
break;
case RELAUNCH_ACTIVITY: {
Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "activityRestart");
ActivityClientRecord r = (ActivityClientRecord) msg.obj;
handleRelaunchActivity(r);
Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
}
break;
case PAUSE_ACTIVITY:
Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "activityPause");
handlePauseActivity((IBinder) msg.obj, false, (msg.arg1 & 1) != 0, msg.arg2, (msg.arg1 & 2) != 0);
maybeSnapshot();
Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
break;
case PAUSE_ACTIVITY_FINISHING:
Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "activityPause");
handlePauseActivity((IBinder) msg.obj, true, (msg.arg1 & 1) != 0, msg.arg2, (msg.arg1 & 1) != 0);
Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
break;
...........
}
}
可以看见Activity的生命周期都是依靠主线程的Looper.loop,当收到不同Message时采用相应措施。
如果某个消息处理时间过长,比如你在onCreate(),onResume()里面处理耗时操作,那么下一次的消息比如用户的点击事件不能处理了,整个循环就会产生卡顿,时间一长就成了ANR。
而且主线程Looper从消息队列读取消息,当读完所有消息时,主线程阻塞。子线程往消息队列发送消息,并且往管道文件写数据,主线程即被唤醒,从管道文件读取数据,主线程被唤醒只是为了读取消息,当消息读取完毕,再次睡眠。因此loop的循环并不会对CPU性能有过多的消耗(参考:https://blog.csdn.net/broadview2006/article/details/8552148)。
总结:Looer.loop()方法可能会引起主线程的阻塞,但只要它的消息循环没有被阻塞,能一直处理事件就不会产生ANR异常。
参考:
https://www.zhihu.com/question/34652589
http://www.jianshu.com/p/cfe50b8b0a41
https://juejin.im/entry/597026806fb9a06bcb7fc660