引子:
正如我們所知胧奔,在android中如果主線程中進行耗時操作會引發(fā)ANR(Application Not Responding)異常。
造成ANR的原因一般有兩種:
- 當前的事件沒有機會得到處理(即主線程正在處理前一個事件预吆,沒有及時的完成或者looper被某種原因阻塞住了)
- 當前的事件正在處理龙填,但沒有及時完成
為了避免ANR異常,android使用了Handler消息處理機制拐叉。讓耗時操作在子線程運行岩遗。
因此產生了一個問題,主線程中的Looper.loop()一直無限循環(huán)檢測消息隊列中是否有新消息為什么不會造成ANR凤瘦?
本人面試網易的時候就被問到了T_T
源碼分析
ActivityThread.java 是主線程入口的類宿礁,這里你可以看到寫Java程序中司空見慣的main方法,而main方法正是整個Java程序的入口蔬芥。
ActivityThread源碼
public static final void main(String[] args) {
...
//創(chuàng)建Looper和MessageQueue
Looper.prepareMainLooper();
...
//輪詢器開始輪詢
Looper.loop();
...
}
Looper.loop()方法
while (true) {
//取出消息隊列的消息梆靖,可能會阻塞
Message msg = queue.next(); // might block
...
//解析消息,分發(fā)消息
msg.target.dispatchMessage(msg);
...
}
顯而易見的笔诵,如果main方法中沒有l(wèi)ooper進行循環(huán)返吻,那么主線程一運行完畢就會退出。這還玩?zhèn)€蛋昂跣觥测僵!
總結:ActivityThread的main方法主要就是做消息循環(huán),一旦退出消息循環(huán)谢翎,那么你的應用也就退出了捍靠。
我們知道了消息循環(huán)的必要性,那為什么這個死循環(huán)不會造成ANR異常呢岳服?
因為Android 的是由事件驅動的剂公,looper.loop() 不斷地接收事件、處理事件吊宋,每一個點擊觸摸或者說Activity的生命周期都是運行在 Looper.loop() 的控制之下纲辽,如果它停止了颜武,應用也就停止了。只能是某一個消息或者說對消息的處理阻塞了 Looper.loop()拖吼,而不是 Looper.loop() 阻塞它鳞上。
也就說我們的代碼其實就是在這個循環(huán)里面去執(zhí)行的,當然不會阻塞了吊档。
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()里面處理耗時操作鬼贱,那么下一次的消息比如用戶的點擊事件不能處理了,整個循環(huán)就會產生卡頓香璃,時間一長就成了ANR这难。
讓我們再看一遍造成ANR的原因,你可能就懂了葡秒。
造成ANR的原因一般有兩種:
- 當前的事件沒有機會得到處理(即主線程正在處理前一個事件姻乓,沒有及時的完成或者looper被某種原因阻塞住了)
- 當前的事件正在處理,但沒有及時完成
而且主線程Looper從消息隊列讀取消息眯牧,當讀完所有消息時蹋岩,主線程阻塞。子線程往消息隊列發(fā)送消息学少,并且往管道文件寫數據剪个,主線程即被喚醒,從管道文件讀取數據版确,主線程被喚醒只是為了讀取消息禁偎,當消息讀取完畢,再次睡眠阀坏。因此loop的循環(huán)并不會對CPU性能有過多的消耗。
總結:Looer.loop()方法可能會引起主線程的阻塞笆檀,但只要它的消息循環(huán)沒有被阻塞忌堂,能一直處理事件就不會產生ANR異常。