ANR即Application Not Responding,顧名思義就是應用程序無響應。
在Android中,一般情況下,四大組件均是工作在主線程中的,Android中的Activity Manager和Window Manager會隨時監控應用程序的響應情況,如果因爲一些耗時操作(網絡請求或者IO操作)造成主線程阻塞一定時間(例如造成5s內不能響應用戶事件或者BroadcastReceiver的onReceive方法執行時間超過10s),那麼系統就會顯示ANR對話框提示用戶對應的應用處於無響應狀態。
一、概述
- Service Timeout:服務在20s內未執行完成;
- 對於前臺服務,則超時爲SERVICE_TIMEOUT = 20s;
- 對於後臺服務,則超時爲SERVICE_BACKGROUND_TIMEOUT = 200s
- BroadcastQueue Timeout:比如前臺廣播在10s內執行完成
- 對於前臺廣播,則超時爲BROADCAST_FG_TIMEOUT = 10s;
- 對於後臺廣播,則超時爲BROADCAST_BG_TIMEOUT = 60s;
- ContentProvider Timeout:內容提供者執行超時 10s
- inputDispatching Timeout: 輸入事件分發超時5s,包括 按鍵分發事件的超時。
觸發ANR的過程可分爲三個步驟: 埋延時炸彈, 拆延時炸彈, 引爆炸彈
- Service超時檢測機制:
- 超過一定時間沒有執行完相應操作來觸發移除延時消息,則會觸發anr;
- BroadcastReceiver超時檢測機制:
- 有序廣播的總執行時間超過 2* receiver個數 * timeout時長,則會觸發anr;
- 有序廣播的某一個receiver執行過程超過 timeout時長,則會觸發anr;
- 另外:
- 對於Service, Broadcast, Input發生ANR之後,最終都會調用AMS.appNotResponding;
- 對於provider,在其進程啓動時publish過程可能會出現ANR, 則會直接殺進程以及清理相應信息,而不會彈出ANR的對話框.
- appNotRespondingViaProvider()過程會走appNotResponding(), 這個就不介紹了,很少使用,由用戶自定義超時時間.
二、Service Timeout
Service Timeout觸發時機,簡單說就是AMS中的mHandler收到SERVICE_TIMEOUT_MSG消息時觸發。
對於Service有兩類:
- 對於前臺服務,則超時爲SERVICE_TIMEOUT = 20s;
- 對於後臺服務,則超時爲SERVICE_BACKGROUND_TIMEOUT = 200s
由變量ProcessRecord.execServicesFg來決定是否前臺啓動
2.1 AS.realStartServiceLocked埋延時炸彈
在前面文章startService流程分析詳細介紹Service啓動流程,在Service所在進程attach到system_server進程的過程中會調用realStartServiceLocked()方法
2.1.1 AS.realStartServiceLocked入口
[-> ActiveServices.java]
private final void realStartServiceLocked(ServiceRecord r,
ProcessRecord app, boolean execInFg) throws RemoteException {
...
//發送delay消息(SERVICE_TIMEOUT_MSG),【見小節2.1.2】
bumpServiceExecutingLocked(r, execInFg, "create");
try {
...
//最終執行服務的onCreate()方法
app.thread.scheduleCreateService(r, r.serviceInfo,
mAm.compatibilityInfoForPackageLocked(r.serviceInfo.applicationInfo),
app.repProcState);
} catch (DeadObjectException e) {
...
} finally {
if (!created) {
//這裏主要做兜底,正常來說在handle過程中會移除
//當service啓動完畢,則remove SERVICE_TIMEOUT_MSG消息【見小節2.1.3】
serviceDoneExecutingLocked(r, inDestroying, inDestroying);
...
}
}
}
2.1.2 AS.bumpServiceExecutingLocked發送延時消息
該方法的主要工作發送delay消息(SERVICE_TIMEOUT_MSG)
private final void bumpServiceExecutingLocked(ServiceRecord r, boolean fg, String why) {
...
scheduleServiceTimeoutLocked(r.app);
}
void scheduleServiceTimeoutLocked(ProcessRecord proc) {
if (proc.executingServices.size() == 0 || proc.thread == null) {
return;
}
long now = SystemClock.uptimeMillis();
Message msg = mAm.mHandler.obtainMessage(
ActivityManagerService.SERVICE_TIMEOUT_MSG);
msg.obj = proc;
//當超時後仍沒有remove該SERVICE_TIMEOUT_MSG消息,則執行service Timeout流程【見2.3.1】
mAm.mHandler.sendMessageAtTime(msg,
proc.execServicesFg ? (now+SERVICE_TIMEOUT) : (now+ SERVICE_BACKGROUND_TIMEOUT));
}
- 對於前臺服務,則超時爲SERVICE_TIMEOUT,即timeout=20s;
- 對於後臺服務,則超時爲SERVICE_BACKGROUND_TIMEOUT,即timeout=200s;
該方法的主要工作發送delay消息(SERVICE_TIMEOUT_MSG). 炸彈已埋下, 我們並不希望炸彈被引爆, 那麼就需要在炸彈爆炸之前拆除炸彈.
2.2 拆炸彈
在system_server進程AS.realStartServiceLocked()調用的過程會埋下一顆炸彈, 超時沒有啓動完成則會爆炸.
那麼什麼時候會拆除這顆炸彈的引線呢?
經過Binder等層層調用進入目標進程的主線程handleCreateService()的過程.
2.2.1 AT.handleCreateService
[-> ActivityThread.java]
private void handleCreateService(CreateServiceData data) {
...
java.lang.ClassLoader cl = packageInfo.getClassLoader();
Service service = (Service) cl.loadClass(data.info.name).newInstance();
...
try {
//創建ContextImpl對象
ContextImpl context = ContextImpl.createAppContext(this, packageInfo);
context.setOuterContext(service);
//創建Application對象
Application app = packageInfo.makeApplication(false, mInstrumentation);
service.attach(context, this, data.info.name, data.token, app,
ActivityManagerNative.getDefault());
//調用服務onCreate()方法
service.onCreate();
//拆除炸彈引線[見小節2.2.2]
ActivityManagerNative.getDefault().serviceDoneExecuting(
data.token, SERVICE_DONE_EXECUTING_ANON, 0, 0);
} catch (Exception e) {
...
}
}
在這個過程會創建目標服務對象,以及回調onCreate()方法, 緊接再次經過多次調用回到system_server來執行serviceDoneExecuting.
2.2.2 AS.serviceDoneExecutingLocked移除延時信息
該方法的主要工作是當service啓動完成,則移除service Timeout消息。
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) {
//當前服務所在進程中沒有正在執行的service
mAm.mHandler.removeMessages(ActivityManagerService.SERVICE_TIMEOUT_MSG, r.app);
...
}
...
}
該方法的主要工作是當service啓動完成,則移除服務超時消息SERVICE_TIMEOUT_MSG
2.3 引爆炸彈
如果在炸彈倒計時結束之前成功拆卸炸彈,那麼就沒有爆炸的機會, 但是世事難料. 總有些極端情況下無法即時拆除炸彈,導致炸彈爆炸, 其結果就是App發生ANR.
2.3.1 SERVICE_TIMEOUT_MSG 延時消息響應
到此不難理解,當SERVICE_TIMEOUT_MSG消息成功發送時,則AMS中的mHandler收到該消息則觸發調用serviceTimeout。
final class MainHandler extends Handler {
public void handleMessage(Message msg) {
switch (msg.what) {
case SERVICE_TIMEOUT_MSG: {
...
//【見小節2.3.2】
mServices.serviceTimeout((ProcessRecord)msg.obj);
} break;
...
}
...
}
}
2.3.2 AS.serviceTimeout
void serviceTimeout(ProcessRecord proc) {
String anrMessage = null;
synchronized(mAm) {
if (proc.executingServices.size() == 0 || proc.thread == null) {
return;
}
final long now = SystemClock.uptimeMillis();
final long maxTime = now -
(proc.execServicesFg ? SERVICE_TIMEOUT : SERVICE_BACKGROUND_TIMEOUT);
ServiceRecord timeout = null;
long nextTime = 0;
for (int i=proc.executingServices.size()-1; i>=0; i--) {
ServiceRecord sr = proc.executingServices.valueAt(i);
if (sr.executingStart < maxTime) {
timeout = sr;
break;
}
if (sr.executingStart > nextTime) {
nextTime = sr.executingStart;
}
}
if (timeout != null && mAm.mLruProcesses.contains(proc)) {
Slog.w(TAG, "Timeout executing service: " + timeout);
StringWriter sw = new StringWriter();
PrintWriter pw = new FastPrintWriter(sw, false, 1024);
pw.println(timeout);
timeout.dump(pw, " ");
pw.close();
mLastAnrDump = sw.toString();
mAm.mHandler.removeCallbacks(mLastAnrDumpClearer);
mAm.mHandler.postDelayed(mLastAnrDumpClearer, LAST_ANR_LIFETIME_DURATION_MSECS);
anrMessage = "executing service " + timeout.shortName;
}
}
if (anrMessage != null) {
//當存在timeout的service,則執行appNotResponding
mAm.appNotResponding(proc, null, null, false, anrMessage);
}
}
其中anrMessage的內容爲”executing service [發送超時serviceRecord信息]”;
三、BroadcastReceiver
BroadcastReceiver Timeout是位於”ActivityManager”線程中的BroadcastQueue.BroadcastHandler收到BROADCAST_TIMEOUT_MSG消息時觸發。
對於廣播隊列有兩個: foreground隊列和background隊列:
- 對於前臺廣播,則超時爲BROADCAST_FG_TIMEOUT = 10s;
- 對於後臺廣播,則超時爲BROADCAST_BG_TIMEOUT = 60s
3.1 埋炸彈
文章Android Broadcast廣播機制分析詳細介紹廣播啓動流程,通過調用 processNextBroadcast來處理廣播.其流程爲先處理並行廣播,再處理當前有序廣播,最後獲取並處理下條有序廣播.
3.1.1 processNextBroadcast 入口
[-> BroadcastQueue.java]
final void processNextBroadcast(boolean fromMsg) {
synchronized(mService) {
...
//part 2: 處理當前有序廣播
do {
r = mOrderedBroadcasts.get(0);
//獲取所有該廣播所有的接收者
int numReceivers = (r.receivers != null) ? r.receivers.size() : 0;
if (mService.mProcessesReady && r.dispatchTime > 0) {
long now = SystemClock.uptimeMillis();
if ((numReceivers > 0) &&
(now > r.dispatchTime + (2*mTimeoutPeriod*numReceivers))) {
//當廣播處理時間超時,則強制結束這條廣播【見小節3.3.2】
broadcastTimeoutLocked(false);
...
}
}
if (r.receivers == null || r.nextReceiver >= numReceivers
|| r.resultAbort || forceReceive) {
if (r.resultTo != null) {
//處理廣播消息消息
performReceiveLocked(r.callerApp, r.resultTo,
new Intent(r.intent), r.resultCode,
r.resultData, r.resultExtras, false, false, r.userId);
r.resultTo = null;
}
//拆炸彈【見小節3.2.2】
cancelBroadcastTimeoutLocked();
}
} while (r == null);
...
//part 3: 獲取下條有序廣播
r.receiverTime = SystemClock.uptimeMillis();
if (!mPendingBroadcastTimeoutMessage) {
long timeoutTime = r.receiverTime + mTimeoutPeriod;
//埋炸彈【見小節3.1.2】
setBroadcastTimeoutLocked(timeoutTime);
}
...
}
}
對於廣播超時處理時機:
- 首先在part3的過程中setBroadcastTimeoutLocked(timeoutTime) 設置超時廣播消息;
- 然後在part2根據廣播處理情況來處理:
- 當廣播接收者等待時間過長,則調用broadcastTimeoutLocked(false);
- 當執行完廣播,則調用cancelBroadcastTimeoutLocked;
3.1.2 setBroadcastTimeoutLocked發送延時消息
final void setBroadcastTimeoutLocked(long timeoutTime) {
if (! mPendingBroadcastTimeoutMessage) {
Message msg = mHandler.obtainMessage(BROADCAST_TIMEOUT_MSG, this);
mHandler.sendMessageAtTime(msg, timeoutTime);
mPendingBroadcastTimeoutMessage = true;
}
}
設置定時廣播BROADCAST_TIMEOUT_MSG,即當前往後推mTimeoutPeriod時間廣播還沒處理完畢,則進入廣播超時流程。
3.2 拆炸彈
broadcast跟service超時機制大抵相同,但有一個非常隱蔽的技能點,那就是通過靜態註冊的廣播超時會受SharedPreferences(簡稱SP)的影響。
3.2.1 sendFinished
關於廣播是否考慮SP的情況取決於如下代碼:
public final void finish() {
if (mType == TYPE_COMPONENT) {
final IActivityManager mgr = ActivityManager.getService();
if (QueuedWork.hasPendingWork()) {
//當SP有未同步到磁盤的工作,則需等待其完成,才告知系統已完成該廣播
QueuedWork.queue(new Runnable() {
public void run() {
sendFinished(mgr);
}
}, false);
} else {
sendFinished(mgr);
}
} else if (mOrderedHint && mType != TYPE_UNREGISTERED) {
final IActivityManager mgr = ActivityManager.getService();
sendFinished(mgr);
}
}
可見,只有XML靜態註冊的廣播超時檢測過程會考慮是否有SP尚未完成,動態廣播並不受其影響。
3.2.2 cancelBroadcastTimeoutLocked 取消延時消息
final void cancelBroadcastTimeoutLocked() {
if (mPendingBroadcastTimeoutMessage) {
mHandler.removeMessages(BROADCAST_TIMEOUT_MSG, this);
mPendingBroadcastTimeoutMessage = false;
}
}
3.3 引爆炸彈
3.3.1 BroadcastHandler.handleMessage 延時消息響應
[-> BroadcastQueue.java ::BroadcastHandler]
private final class BroadcastHandler extends Handler {
public void handleMessage(Message msg) {
switch (msg.what) {
case BROADCAST_TIMEOUT_MSG: {
synchronized (mService) {
//【見小節3.3.2】
broadcastTimeoutLocked(true);
}
} break;
...
}
...
}
}
3.3.2 broadcastTimeoutLocked
[-> BroadcastRecord.java]
//fromMsg = true
final void broadcastTimeoutLocked(boolean fromMsg) {
if (fromMsg) {
mPendingBroadcastTimeoutMessage = false;
}
if (mOrderedBroadcasts.size() == 0) {
return;
}
long now = SystemClock.uptimeMillis();
BroadcastRecord r = mOrderedBroadcasts.get(0);
if (fromMsg) {
if (mService.mDidDexOpt) {
mService.mDidDexOpt = false;
long timeoutTime = SystemClock.uptimeMillis() + mTimeoutPeriod;
setBroadcastTimeoutLocked(timeoutTime);
return;
}
if (!mService.mProcessesReady) {
return; //當系統還沒有準備就緒時,廣播處理流程中不存在廣播超時
}
long timeoutTime = r.receiverTime + mTimeoutPeriod;
if (timeoutTime > now) {
//如果當前正在執行的receiver沒有超時,則重新設置廣播超時
setBroadcastTimeoutLocked(timeoutTime);
return;
}
}
BroadcastRecord br = mOrderedBroadcasts.get(0);
if (br.state == BroadcastRecord.WAITING_SERVICES) {
//廣播已經處理完成,但需要等待已啓動service執行完成。當等待足夠時間,則處理下一條廣播。
br.curComponent = null;
br.state = BroadcastRecord.IDLE;
processNextBroadcast(false);
return;
}
r.receiverTime = now;
//當前BroadcastRecord的anr次數執行加1操作
r.anrCount++;
if (r.nextReceiver <= 0) {
return;
}
...
Object curReceiver = r.receivers.get(r.nextReceiver-1);
//查詢App進程
if (curReceiver instanceof BroadcastFilter) {
BroadcastFilter bf = (BroadcastFilter)curReceiver;
if (bf.receiverList.pid != 0
&& bf.receiverList.pid != ActivityManagerService.MY_PID) {
synchronized (mService.mPidsSelfLocked) {
app = mService.mPidsSelfLocked.get(
bf.receiverList.pid);
}
}
} else {
app = r.curApp;
}
if (app != null) {
anrMessage = "Broadcast of " + r.intent.toString();
}
if (mPendingBroadcast == r) {
mPendingBroadcast = null;
}
//繼續移動到下一個廣播接收者
finishReceiverLocked(r, r.resultCode, r.resultData,
r.resultExtras, r.resultAbort, false);
scheduleBroadcastsLocked();
if (anrMessage != null) {
// [見小節3.3.3]
mHandler.post(new AppNotResponding(app, anrMessage));
}
}
- mOrderedBroadcasts已處理完成,則不會anr;
- 正在執行dexopt,則不會anr;
- 系統還沒有進入ready狀態(mProcessesReady=false),則不會anr;
- 如果當前正在執行的receiver沒有超時,則重新設置廣播超時,不會anr;
3.3.3 AppNotResponding
[-> BroadcastQueue.java]
private final class AppNotResponding implements Runnable {
...
public void run() {
// 進入ANR處理流程
mService.appNotResponding(mApp, null, null, false, mAnnotation);
}
}
四、 ContentProvider
ContentProvider Timeout是位於”ActivityManager”線程中的AMS.MainHandler收到CONTENT_PROVIDER_PUBLISH_TIMEOUT_MSG消息時觸發。
ContentProvider 超時爲CONTENT_PROVIDER_PUBLISH_TIMEOUT = 10s. 這個跟前面的Service和BroadcastQueue完全不同, 由Provider進程啓動過程相關.
4.1 埋炸彈
文章理解ContentProvider原理詳細介紹了Provider啓動流程. 埋炸彈的過程 其實是在進程創建的過程,進程創建後會調用attachApplicationLocked()進入system_server進程.
4.1.1 AMS.attachApplicationLocked 入口
private final boolean attachApplicationLocked(IApplicationThread thread, int pid) {
ProcessRecord app;
if (pid != MY_PID && pid >= 0) {
synchronized (mPidsSelfLocked) {
app = mPidsSelfLocked.get(pid); // 根據pid獲取ProcessRecord
}
}
...
//系統處於ready狀態或者該app爲FLAG_PERSISTENT進程則爲true
boolean normalMode = mProcessesReady || isAllowedWhileBooting(app.info);
List<ProviderInfo> providers = normalMode ? generateApplicationProvidersLocked(app) : null;
//app進程存在正在啓動中的provider,則超時10s後發送CONTENT_PROVIDER_PUBLISH_TIMEOUT_MSG消息
if (providers != null && checkAppInLaunchingProvidersLocked(app)) {
Message msg = mHandler.obtainMessage(CONTENT_PROVIDER_PUBLISH_TIMEOUT_MSG);
msg.obj = app;
mHandler.sendMessageDelayed(msg, CONTENT_PROVIDER_PUBLISH_TIMEOUT);
}
thread.bindApplication(...);
...
}
10s之後引爆該炸彈
4.2 拆炸彈
當provider成功publish之後,便會拆除該炸彈.
4.2.1 AMS.publishContentProviders
public final void publishContentProviders(IApplicationThread caller, List<ContentProviderHolder> providers) {
...
synchronized (this) {
final ProcessRecord r = getRecordForAppLocked(caller);
final int N = providers.size();
for (int i = 0; i < N; i++) {
ContentProviderHolder src = providers.get(i);
...
ContentProviderRecord dst = r.pubProviders.get(src.info.name);
if (dst != null) {
ComponentName comp = new ComponentName(dst.info.packageName, dst.info.name);
mProviderMap.putProviderByClass(comp, dst); //將該provider添加到mProviderMap
String names[] = dst.info.authority.split(";");
for (int j = 0; j < names.length; j++) {
mProviderMap.putProviderByName(names[j], dst);
}
int launchingCount = mLaunchingProviders.size();
int j;
boolean wasInLaunchingProviders = false;
for (j = 0; j < launchingCount; j++) {
if (mLaunchingProviders.get(j) == dst) {
//將該provider移除mLaunchingProviders隊列
mLaunchingProviders.remove(j);
wasInLaunchingProviders = true;
j--;
launchingCount--;
}
}
//成功pubish則移除該消息
if (wasInLaunchingProviders) {
mHandler.removeMessages(CONTENT_PROVIDER_PUBLISH_TIMEOUT_MSG, r);
}
synchronized (dst) {
dst.provider = src.provider;
dst.proc = r;
//喚醒客戶端的wait等待方法
dst.notifyAll();
}
...
}
}
}
}
4.3 引爆炸彈
在system_server進程中有一個Handler線程, 名叫”ActivityManager”.當倒計時結束便會向該Handler線程發送 一條信息CONTENT_PROVIDER_PUBLISH_TIMEOUT_MSG,
4.3.1 MainHandler.handleMessage 延時消息響應
[-> ActivityManagerService.java ::MainHandler]
final class MainHandler extends Handler {
public void handleMessage(Message msg) {
switch (msg.what) {
case CONTENT_PROVIDER_PUBLISH_TIMEOUT_MSG: {
...
ProcessRecord app = (ProcessRecord)msg.obj;
synchronized (ActivityManagerService.this) {
//【見小節4.3.2】
processContentProviderPublishTimedOutLocked(app);
}
} break;
...
}
...
}
}
4.3.2 AMS.processContentProviderPublishTimedOutLocked
private final void processContentProviderPublishTimedOutLocked(ProcessRecord app) {
//[見4.3.3]
cleanupAppInLaunchingProvidersLocked(app, true);
//[見小節4.3.4]
removeProcessLocked(app, false, true, "timeout publishing content providers");
}
4.3.3 AMS.cleanupAppInLaunchingProvidersLocked
boolean cleanupAppInLaunchingProvidersLocked(ProcessRecord app, boolean alwaysBad) {
boolean restart = false;
for (int i = mLaunchingProviders.size() - 1; i >= 0; i--) {
ContentProviderRecord cpr = mLaunchingProviders.get(i);
if (cpr.launchingApp == app) {
if (!alwaysBad && !app.bad && cpr.hasConnectionOrHandle()) {
restart = true;
} else {
//移除死亡的provider
removeDyingProviderLocked(app, cpr, true);
}
}
}
return restart;
}
-
removeDyingProviderLocked()的功能跟進程的存活息息相關:詳見ContentProvider引用計數 小節4.5
- 對於stable類型的provider(即conn.stableCount > 0),則會殺掉所有跟該provider建立stable連接的非persistent進程.
- 對於unstable類的provider(即conn.unstableCount > 0),並不會導致client進程被級聯所殺.
4.3.4 AMS.removeProcessLocked
private final boolean removeProcessLocked(ProcessRecord app, boolean callerWillRestart, boolean allowRestart, String reason) {
final String name = app.processName;
final int uid = app.uid;
//移除mProcessNames中的相應對象
removeProcessNameLocked(name, uid);
if (mHeavyWeightProcess == app) {
mHandler.sendMessage(mHandler.obtainMessage(CANCEL_HEAVY_NOTIFICATION_MSG,
mHeavyWeightProcess.userId, 0));
mHeavyWeightProcess = null;
}
boolean needRestart = false;
if (app.pid > 0 && app.pid != MY_PID) {
int pid = app.pid;
synchronized (mPidsSelfLocked) {
mPidsSelfLocked.remove(pid);
mHandler.removeMessages(PROC_START_TIMEOUT_MSG, app);
}
...
boolean willRestart = false;
if (app.persistent && !app.isolated) {
if (!callerWillRestart) {
willRestart = true;
} else {
needRestart = true;
}
}
app.kill(reason, true); //殺進程
handleAppDiedLocked(app, willRestart, allowRestart);
if (willRestart) {
removeLruProcessLocked(app);
addAppLocked(app.info, false, null /* ABI override */);
}
} else {
mRemovedProcesses.add(app);
}
return needRestart;
}
五、inputDispatching
底層檢測,通知上層
[-> InputManagerService.java]
private long notifyANR(InputApplicationHandle inputApplicationHandle,
InputWindowHandle inputWindowHandle, String reason) {
//【見小節2.4.2】
return mWindowManagerCallbacks.notifyANR(
inputApplicationHandle, inputWindowHandle, reason);
}