ANR問(wèn)題類(lèi)型及產(chǎn)生原理
ANR(Application Not Responding):即應(yīng)用無(wú)響應(yīng). 在日常使用安卓手機(jī)的過(guò)程中, 對(duì)最anr最直接的印象就是手機(jī)彈框顯示應(yīng)用未響應(yīng). 選擇繼續(xù)等待或者關(guān)閉.
如果應(yīng)用程序的主線程在規(guī)定的時(shí)間內(nèi), 沒(méi)有完成特定操作和事件, 就會(huì)發(fā)生ANR.
四種ANR類(lèi)型
- KeyDispatchTimeout : input事件在5S內(nèi)沒(méi)有處理完成發(fā)生ANR
-
ServiceTimeout : bind,create,start,unbind等操作,前臺(tái)Service在20s內(nèi),后臺(tái)
Service在200s內(nèi)沒(méi)有處理完成發(fā)生ANR -
BroadcastTimeout : BroadcastReceiver onReceiver處理事務(wù)時(shí)前臺(tái)廣播在10S內(nèi),后臺(tái)廣播在60s內(nèi) (應(yīng)用程序應(yīng)該避免在BroadcastReceiver里做耗時(shí)的操作或計(jì)算。如果響應(yīng)Intent廣播需要執(zhí)行一個(gè)耗時(shí)的動(dòng)作的話勉躺,應(yīng)用程序應(yīng)該啟動(dòng)一個(gè) Service).
沒(méi)有處理完成發(fā)生ANR -
ProcessContentProviderPublishTimedOutLocked : ContentProvider publish在10s內(nèi)沒(méi)有處理完成發(fā)
生ANR
其中第四種ANR發(fā)生的概率最小.
ANR產(chǎn)生的常見(jiàn)原因
- 主線程耗時(shí)操作,如復(fù)雜的layout,龐大的for循環(huán),IO等. (實(shí)際APP開(kāi)發(fā)時(shí)開(kāi)發(fā)者會(huì)避開(kāi)這種, 沒(méi)有見(jiàn)到過(guò)這種問(wèn)題產(chǎn)生ANR);
- 主線程被子線程同步鎖block. (當(dāng)子線程先拿著鎖, 主線程等待這把鎖的時(shí)候, 子線程太耗時(shí). 導(dǎo)致主線程一直被阻塞, 從而ANR)
- 主線程被Binder對(duì)端阻塞
- Binder被占滿導(dǎo)致主線程無(wú)法和SystemServer通信
- 得不到系統(tǒng)資源(CPU/RAM/IO) (耗時(shí)的動(dòng)畫(huà)需要大量的計(jì)算工作癌瘾,可能導(dǎo)致CPU負(fù)載過(guò)重.)
ANR觸發(fā)機(jī)制
ANR有四種類(lèi)型, 所以可以從這四種類(lèi)型去了解ANR觸發(fā)機(jī)制.
1 Service發(fā)生ANR的機(jī)制
Service Timeout是AMS中MainHandler收到SERVICE_TIMEOUT_MSG消息時(shí)觸發(fā)。
由ServiceTimeout原因發(fā)生的ANR有兩種, 前臺(tái)服務(wù)未響應(yīng)(20s) 和 后臺(tái)服務(wù)未響應(yīng)(200s).
1.1 startService()觸發(fā)ANR流程:
- 在用戶進(jìn)程A中, 當(dāng)發(fā)起startService的時(shí)候, 通過(guò)binder通信, 通過(guò)IActivityManager#startService調(diào)用AMS中
startService()
方法. (這一步從用戶進(jìn)程跨越到system_server進(jìn)程, 調(diào)用AMS的方法)
備注:IActivityManager對(duì)象保存在進(jìn)程A的單例Singleton<IActivityManager>
中饵溅,進(jìn)程啟動(dòng)時(shí)查詢ServiceManager#getService獲得妨退,具有緩存作用
-
在AMS中, 當(dāng)realStartServiceLocked()啟動(dòng)服務(wù)的時(shí)候, 其內(nèi)部會(huì)操作AMS的MainHandler調(diào)用
sendMessageAtTime()
方法發(fā)送SERVICE_TIMEOUT_MSG
消息埋下炸彈, 并通過(guò)參數(shù)指定消息發(fā)送的時(shí)間. (當(dāng)AMS埋好炸彈之后, 通過(guò)binder把目標(biāo)服務(wù)創(chuàng)建所需要的信息傳遞給目標(biāo)服務(wù)進(jìn)程B, 進(jìn)行服務(wù)的創(chuàng)建)
在這里插入圖片描述 在服務(wù)進(jìn)程B中, 服務(wù)進(jìn)程將AMS傳遞的過(guò)來(lái)的服務(wù)創(chuàng)建信息進(jìn)行打包, 然后ApplicationThread內(nèi)部類(lèi)使用消息傳遞機(jī)制讓ActivityThread外部類(lèi)異步執(zhí)行service.onCreate回調(diào). 當(dāng)service創(chuàng)建完畢之后, 服務(wù)進(jìn)程B會(huì)再次binder通信, 告訴AMS調(diào)用
serviceDoneExecuting()
, 該方法最后會(huì)執(zhí)行Handler.removeMessages()
把之前埋下的炸彈拆除.
所以, 回調(diào)完Service的onCreate()
方法之后便會(huì)移除啟動(dòng)服務(wù)超消息SERVICE_TIMEOUT_MSG
.
Service啟動(dòng)過(guò)程出現(xiàn)ANR,”executing service [發(fā)送超時(shí)serviceRecord信息]”
蜕企, 這往往是service的onCreate()回調(diào)方法執(zhí)行時(shí)間過(guò)長(zhǎng)咬荷。-
當(dāng)炸彈沒(méi)有及時(shí)被拆除. 當(dāng)
SERVICE_TIMEOUT_MSG
爆炸消息被AMS的MainHandler收到的時(shí)候, 就會(huì)發(fā)生ANR.
在這里插入圖片描述
注: 1: 當(dāng)啟動(dòng)進(jìn)程A發(fā)起startService
的時(shí)候, AMS如果發(fā)現(xiàn)目標(biāo)服務(wù)的進(jìn)程還沒(méi)有啟動(dòng), 會(huì)啟動(dòng)一個(gè)進(jìn)程. 但是啟動(dòng)這個(gè)進(jìn)程的時(shí)間不算在ANR的爆炸時(shí)間內(nèi). 因?yàn)橹挥挟?dāng)AMS真正調(diào)用realStartServiceLocked()
之后, 才會(huì)埋下炸彈.
注2: 在ActivityManagerService實(shí)例化的時(shí)候, AMS會(huì)新開(kāi)的一條HandlerThread線程,在這個(gè)線程里會(huì)準(zhǔn)備好一個(gè)Looper. 然后使用有參構(gòu)造指定之前線程準(zhǔn)備好的Looper,實(shí)例化一個(gè)Handler.
該Handler就是AMS中的mHandler.
所有埋炸彈的Message都會(huì)在這個(gè)Looper中的, MessageQueue中進(jìn)行入隊(duì). 最終回調(diào)MainHandler.handleMessage()
方法.
注3: AMS和ActivityService
在ActivityManagerService實(shí)例化的時(shí)候, 會(huì)實(shí)例化一個(gè)ActiveService成員變量.
在實(shí)例化ActiveService的時(shí)候, AMS會(huì)把this傳給ActiveService, 然后ActiveService也有一個(gè)AMS的成員變量.
AMS.mServices和AS.mAm.
注4: ApplicationThread是ActivityThread的普通內(nèi)部類(lèi), 當(dāng)system_server進(jìn)程通過(guò)binder通信調(diào)用ApplicationThread中的scheduleCreateService()
方法時(shí), 在ApplicationThread該方法內(nèi)部往外部ActivityThread發(fā)送消息, 當(dāng)ActivityThread進(jìn)行handle調(diào)用的時(shí)候, 創(chuàng)建服務(wù).
1.2 產(chǎn)生ANR的源碼中與"埋炸彈", "拆炸彈"有關(guān)的代碼
1) 埋炸彈
[ActiveSErvices.java]
private final void realStartServiceLocked(ServiceRecord r, ProcessRecord app, boolean execInFg) throws RemoteException {
...
//發(fā)送delay消息(SERVICE_TIMEOUT_MSG)
bumpServiceExecutingLocked(r, execInFg, "create");
...
}
private final void bumpServiceExecutingLocked(ServiceRecord r, boolean fg, String why) {
...
scheduleServiceTimeoutLocked(r.app);
}
void scheduleServiceTimeoutLocked(ProcessRecord proc) {
...
//當(dāng)超時(shí)后仍沒(méi)有remove該SERVICE_TIMEOUT_MSG消息,則執(zhí)行service Timeout流程
mAm.mHandler.sendMessageAtTime(msg,
proc.execServicesFg ? (now+SERVICE_TIMEOUT) : (now+ SERVICE_BACKGROUND_TIMEOUT));
}
2) 拆炸彈
在目標(biāo)進(jìn)程的服務(wù)創(chuàng)建之后, 會(huì)remove掉AMS, MessageQueue之中的超時(shí)消息, 當(dāng)這個(gè)消息被remove掉之后, 自然不會(huì)再被MainHandler處理, 也就不會(huì)發(fā)生ANR.
[ActivityThread.java]
private void handleCreateService(CreateServiceData data) {
...
java.lang.ClassLoader cl = packageInfo.getClassLoader();
Service service = (Service) cl.loadClass(data.info.name).newInstance();
...
try {
//創(chuàng)建ContextImpl對(duì)象
ContextImpl context = ContextImpl.createAppContext(this, packageInfo);
context.setOuterContext(service);
//創(chuàng)建Application對(duì)象
Application app = packageInfo.makeApplication(false, mInstrumentation);
service.attach(context, this, data.info.name, data.token, app,
ActivityManagerNative.getDefault());
//調(diào)用服務(wù)onCreate()方法
service.onCreate();
//拆除炸彈引線
ActivityManagerNative.getDefault().serviceDoneExecuting(
data.token, SERVICE_DONE_EXECUTING_ANON, 0, 0);
} catch (Exception e) {
...
}
}
[ActivityThread.java]
private void serviceDoneExecutingLocked(ServiceRecord r, boolean inDestroying, boolean finishing) {
...
if (r.executeNesting <= 0) {
if (r.app != null) {
r.app.execServicesFg = false;
r.app.executingServices.remove(r);
if (r.app.executingServices.size() == 0) {
//當(dāng)前服務(wù)所在進(jìn)程中沒(méi)有正在執(zhí)行的service
mAm.mHandler.removeMessages(ActivityManagerService.SERVICE_TIMEOUT_MSG, r.app);
...
}
...
}
2 BroadcastReceiver發(fā)生ANR的機(jī)制
BroadcastReceiver Timeout是AMS中的BroadcastQueue.BroadcastHandler
收到BROADCAST_TIMEOUT_MSG
消息時(shí)觸發(fā).
其中, 前臺(tái)廣播超時(shí)mTimeoutPeriod
為10s, 后臺(tái)廣播超時(shí)mTimeoutPeriod
為 60s.
廣播發(fā)送的時(shí)候有兩種方式, 一種是 "默認(rèn)廣播" (并行廣播), 第二種是 "有序廣播" (串行廣播).
只有串行廣播才需要考慮超時(shí)轻掩,因?yàn)榻邮照呤谴刑幚淼男移梗耙粋€(gè)receiver處理慢,會(huì)影響后一個(gè)receiver唇牧;并行廣播 通過(guò)一個(gè)循環(huán)一次性向所有的receiver分發(fā)廣播事件罕扎,所以不存在彼此影響的問(wèn)題,則沒(méi)有廣播超時(shí)丐重;
串行廣播超時(shí)情況1:某個(gè)廣播總處理時(shí)間 > 2* receiver總個(gè)數(shù) * mTimeoutPeriod
, 其中mTimeoutPeriod
串行廣播超時(shí)情況2:某個(gè)receiver的執(zhí)行時(shí)間超過(guò)mTimeoutPeriod
Broadcast的ANR流程如下圖所示:
圖中1,2,3 步從用戶進(jìn)程通過(guò)binder通信調(diào)用AMS的
broadcastIntentLocked()
方法.
在該方法中, 首先進(jìn)行了一系列的廣播驗(yàn)證.
然后先并行處理動(dòng)態(tài)注冊(cè)的廣播, 然后合并動(dòng)態(tài)注冊(cè)廣播和靜態(tài)注冊(cè)的廣播receivers進(jìn)行串行處理. (原因: 讓靜態(tài)注冊(cè)的廣播串行化腔召,能防止出現(xiàn)瞬間啟動(dòng)大量進(jìn)程的噴井效應(yīng)。)
由于我們ANR只會(huì)在處理串行廣播的時(shí)候發(fā)生, 接下來(lái)我們只關(guān)注串行廣播處理的流程.圖中第4,5步, 在第4步中AMS按照并行廣播和串行廣播兩種, 分別要發(fā)送的廣播入相應(yīng)的隊(duì)列, 然后再通過(guò)
scheduleBroadcastLocked()
方法 內(nèi)部使用BroadcastHandler實(shí)例 sendMessage(BroadcastHandler是BroadcastQueue類(lèi)的一個(gè)普通內(nèi)部類(lèi))
.
備注:這里的BroadcastHandler實(shí)例, 是在AMS啟動(dòng)的時(shí)候, AMS構(gòu)造方法中進(jìn)行實(shí)例化的, 在new這個(gè)handler的時(shí)候, 把AMS構(gòu)造器之前的準(zhǔn)備好Looper給了該Handler.圖中第6, 7步, 當(dāng)系統(tǒng)回調(diào)
handlerMessage()
方法的時(shí)候, 其內(nèi)部進(jìn)行廣播的處理. 當(dāng)然我們關(guān)注的是處理串行廣播的流程.第9步, 當(dāng)前廣播超時(shí), 強(qiáng)制結(jié)束廣播.
10,11 通過(guò)binder通信告訴ActivityThread, 讓他去sendMessage(Receiver), ActivityThread處理這個(gè)消息的時(shí)候回調(diào)BroadcastReceiver的
onReceiver()
方法, 完成廣播接受處理.BroadcastReceiver完成
onReceiver()
回調(diào). 而B(niǎo)roadcastQueue向ActivityThread發(fā)起B(yǎng)inder通信之后, 在BroadcastQueue的processNextBroadcast()
方法中執(zhí)行cancelBroadcastTimeoutLocked()
, handler會(huì)發(fā)送removeMessages的消息, 把之前埋下的炸彈拆除.
綜上所訴: 當(dāng)分析ANR的問(wèn)題時(shí), 如果log顯示是BroadcastReceiver的ANR, 可以首先懷疑BroadCastReceiver.onRecieve()
的問(wèn)題; 如果是Service的ANR, 可以首先懷疑是Service.onCreate()方法耗時(shí)的問(wèn)題.