Handler中Looper死迴圈為什麼不會導致應用卡死?
歡迎關注專欄:
裡面定期分享Android和Flutter架構技術知識點及解析,還會不斷更新的BATJ面試專題,歡迎大家前來探討交流,如有好的文章也歡迎投稿。
Flutter跨平臺開發終極之選
應用卡死,也就是ANR所產生的原因?
1、5秒鐘之內沒有響應輸入的事件,比如按鍵、螢幕觸控等。
2、廣播接收器在10秒內沒有執行完畢。
為什麼說應用所有的操作都是在loop()中來管理?
首先,我們的每一個應用都存在於自己的虛擬機器中,也就是說每一個應用都有自己的一個main函式,這個main函式就是ActivityThread。java的main()函式。
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(“
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();
throw new RuntimeException(“Main thread loop unexpectedly exited”);
}
為什麼每一個應用會有自己的一個main函式呢?
當我們在launcher介面啟動一個應用的時候,這時候,系統就會用zygote給我們分配一個虛擬機器,然後,這個應用就會執行在這個虛擬機器上面。
應用執行到虛擬機器之後,首先它要執行的就是啟動ActivityThread,在ActivityThread中,它又會啟動它的main()函式。
在main()函式中,它最重要的兩行程式碼:
public static void main(String[] args) {
。。。
Looper。prepareMainLooper();
。。。
Looper。loop();
}
所以在程式執行的時候,主執行緒所有的程式碼都執行在這個Looper裡面。
也就是說應用所有生命週期的函式(包括Activity、Service所有生命週期)都執行在這個Looper裡面,而且,它們都是以訊息的方式存在的。
假如說一個Activity啟動,要走onResume()函式的時候,它就會在Activity的H裡面執行RESUME_ACTIVITY。
case RESUME_ACTIVITY: return “RESUME_ACTIVITY”;
它傳送了一個Resume的訊息,再接著看下這個Resume這個訊息做了什麼事情,程式碼在ActivityThread。java的handleMessage中。
case RESUME_ACTIVITY:
Trace。traceBegin(Trace。TRACE_TAG_ACTIVITY_MANAGER, “activityResume”);
SomeArgs args = (SomeArgs) msg。obj;
handleResumeActivity((IBinder) args。arg1, true, args。argi1 != 0, true,
args。argi3, “RESUME_ACTIVITY”);
Trace。traceEnd(Trace。TRACE_TAG_ACTIVITY_MANAGER);
break;
進入handleResumeActivity()方法。
// TODO Push resumeArgs into the activity for consideration
r = performResumeActivity(token, clearHide, reason);
它觸發了Activity的管理機制,在Activity的管理機制裡面,他就會發送一個訊息,這個訊息,就是對Activity進行Resume的操作。
r。activity。performResume();
接著往performResume()方法裡面走,就進入了Activity。java。
final void performResume() {
。。。。
mFragments。dispatchResume();
mFragments。execPendingActions();
。。。。
}
再後面就呼叫了Fragemet的管理機制。
public void dispatchResume() {
mHost。mFragmentManager。dispatchResume();
}
因為類的繼承,到最終是繼承自了FragmentActivity,所以最後又變成了對Fragment的Resume。
既然Handler的訊息全都是loop來的,為什麼我們沒有ANR問題?
其實產生ANR問題的不是Looper。loop(),哪怕主執行緒正在等待(block)。
for (;;) {
Message msg = queue。next(); // might block
。。。
}
因為這個時候(阻塞)的時候,說明主執行緒在休眠。
之前不是說5秒鐘不相應就會出現阻塞問題嗎,為什麼休眠個好長時間也並不會被ANR呢?
瞭解這個問題,先看喚醒主執行緒的方式有哪些:
1、輸入事件
主執行緒雖然被block了,但與ANR的問題是沒有關係的,只要輸入事件有響應,他會喚醒,就不會被block了。
所以,產生ANR的問題不是因為主執行緒睡眠了,而是因為輸入事件沒有響應,輸入事件沒有響應他就沒有辦法喚醒這個Looper,才加了這個5秒的限制。
2、往Looper裡面新增訊息的時候,它會喚醒這個Looper。
因為應用中不管是Activity,還是Service,所有的操作都是在各自的生命週期中執行的,所以它所有的操作都逃不出生命週期。所以,所有的操作都執行在ActivityThread。java中的loop()裡面,所以,應用所有的操作都是在這個loop()中來管理的,也正是因為這個原因,主執行緒的loop()是不能夠退出去的。
只有一種情況,我們在一個應用的一個介面下不動,這個應用沒有任何事件發生,也沒有任何別的事件要處理,這個時候,我們的Looper就處於一個block狀態;當點選一下這個螢幕,他就會觸發喚醒這個Looper。
總結
為什麼沒有導致應用卡死?
因為應用卡死壓根與這個Looper沒有關係,應用在沒有訊息需要處理的時候,它是在睡眠,釋放執行緒;卡死是ANR,而Looper是睡眠。
卡死是在主執行緒中執行一個耗時的操作,loop()會一直在處理一個訊息,而for迴圈中有很多訊息需要被處理,而這一個訊息就要處理很久,這一個訊息的處理時間,會轉變成其他的點選事件沒有響應。
因為主執行緒在接受到其他訊息的時候沒有時間去響應,它的時間都在處理那一個耗時的操作,造成點選事件沒有辦法響應,點選事件沒有辦法響應就容易出現ANR。
原文作者:Seas。Su
原文連結:
https://
blog。csdn。net/yichen97/
java/article/details/106367067