應用卡死,也就是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("<pre-initialized>");
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。