您的位置 首页 > 数码极客

〈如何制造anr〉如何制造鹌鹑蛋孵蛋器

本文将从介绍什么是ANR,给出anr产生的几种触发点,分析这几种情况下是怎么产生anr的,然后给出优化的方法这几个方面进行讨论。

1.什么是ANR

ANR定义:在Android上,如果你的应用程序有一段时间响应不够灵敏,系统会向用户显示一个对话框,这个对话框称作应用程序无响应(ANR:Application Not Responding)对话框。

通俗的说就是在特定的时间没有完成任务。

ANR它是通过ActivityManager和WindowManager系统服务监视的。

细分可以分从两部分分:

  • 1.内部:阻塞了主进程,IOwait等

  • 2.外部:当前应用进程调用请求进程,其他进程长时间无反馈。 其他进程的CPU占用率高,使当前应用无法抢占CPU时间片。

2.ANR触发点

  1. service TimeOut:前台服务20s,后台200s

  2. BroadcastReceiver TimeOut : 前台广播10s后台广播20s

  3. 按键或触发时间超时:5s

  4. ContentProvider TimeOut:内容提供者不超过10s

3.ANR触发点原理分析

3.1. Service Timeout

Service Timeout触发时,简单说就是AMS中的mHandler收到SERVICE_TIMEOUT_msg消息时触发。

那我们需要知道这个消息是什么时候开始初始的?如果没有timeout是怎么处理这个消息的?timeout又做了什么操作?

Service startService开始以个service最终都会调用Ac的realStartServiceLocked方法

private final void realStartServiceLocked(ServiceRecord r, ProcessRecord app, boolean execInFg) throws RemoteException { ... //A.发送delay消息(SERVICE_TIMEOUT_MSG)初始化 bumpServiceExecutingLocked(r, execInFg, "create"); try { ... //最终执行服务的onCreate()方法 a(r, r.serviceInfo, mAm.compatibilityInfoForPackageLocked), a); } catch (DeadObjectException e) { ... } finally { if (!created) { //B.当service启动完毕,则remove SERVICE_TIMEOUT_MSG消息 serviceDoneExecutingLocked(r, inDestroying, inDestroying); ... } } }

A.bumpServiceExecutingLocked最终调用到延时发送消息流程如下:

// How long we wait for a service to finish executing. static final int SERVICE_TIMEOUT = 20*1000;// How long we wait for a service to finish executing. static final int SERVICE_BACKGROUND_TIMEOUT = SERVICE_TIMEOUT * 10; //这里设置了前台和后台超时的时间

B.service启动完毕调用serviceDoneExecutingLocked

private void serviceDoneExecutingLocked(ServiceRecord r, boolean inDestroying, boolean finishing) { ... if <= 0) { if != null) { r.a = false; r.a(r); if .executingServices.size() == 0) { //当前服务所在进程中没有正在执行的service mAm.mHandler.removeMessage, r.app); ... } ...}

C.AMS中会处理SERVICE_TIMEOUT_MSG

final class MainHandler extends Handler { public void handleMessage(Message msg) { switch ) { case SERVICE_TIMEOUT_MSG: { ... mServices.serviceTimeout((ProcessRecord); } break; ... } ... } }

D.Ac 调用serviceTimeout

void serviceTimeout(ProcessRecord proc) { String anrMessage = null; synchronized(mAm) { if () == 0 || == null) { return; } final long now = Sy(); final long maxTime = now - ? SERVICE_TIMEOUT : SERVICE_BACKGROUND_TIMEOUT); ServiceRecord timeout = null; long nextTime = 0; for (int i=()-1; i>=0; i--) { ServiceRecord sr = (i); if < maxTime) { timeout = sr; break; } if > nextTime) { nextTime = ; } } if (timeout != null && mAm.mLruProce(proc)) { Slog.w(TAG, "Timeout executing service: " + timeout); StringWriter sw = new StringWriter(); PrintWriter pw = new FastPrintWriter(sw, false, 1024); (timeout); (pw, " "); (); mLastAnrDump = (); mAm.mHandler.removeCallbacks(mLastAnrDumpClearer); mAm.mHandler.postDelayed(mLastAnrDumpClearer, LAST_ANR_LIFETIME_DURATION_MSECS); anrMessage = "executing service " + ; } else { Message msg = mAm.mHandler.obtainMessage( Ac); m = proc; mAm.mHandler.sendMessageAtTime(msg, ? (nextTime+SERVICE_TIMEOUT) : (nextTime + SERVICE_BACKGROUND_TIMEOUT)); } } if (anrMessage != null) { mAm.mA(proc, null, null, false, anrMessage); } }

3.2 BroadcastQueue Timeout

广播机制是用于进程/线程间通信的。分两类:

  • 静态广播接收者标签定义的)

  • 动态广播接收者(通过AMS.registerReceiver())

广播最终存放在如下队列中: mParallelBroadcasts(并行队列)mOrderedBroadcasts(串行队列)

广播的发送方式:

类型方法orderedsticky
普通广播sendBroadcastfalsefalse
有序广播sendOrderedBroadcasttruefalse
Sticky广播sendStickyBroadcastfalsetrue
  • 当发送串行广播(ordered=true)的情况下:

  1. 静态注册的广播接收者(receivers),采用串行处理

  2. 动态注册的广播接收者(registeredReceivers),采用串行处理

  • 当发送并行广播(ordered=false)的情况下:

  1. 静态注册的广播接收者(receivers),依然采用串行处理

  2. 动态注册的广播接收者(registeredReceivers),采用并行处理

为什么讨论这个内容了?

只有串行广播才需要考虑超时,因为接收者是串行处理的,前一个receiver处理慢,会影响后一个receiver;并行广播 通过一个循环一次性向所有的receiver分发广播事件,所以不存在彼此影响的问题,则没有广播超时

简单来说,静态注册的receivers始终采用串行方式来处理(processNextBroadcast); 动态注册的registeredReceivers处理方式是串行还是并行方式, 取决于广播的发送方式(processNextBroadcast)。

根据源码分析:广播最终是通过Broadca中的processNextBroadcast进行处理的。

1.processNextBroadcast执行过程分4步骤

  • A. 处理并行广播

  • B. 处理当前有序广播

  • C. 获取下条有序广播

  • D. 处理下条有序广播

final void processNextBroadcast(boolean fromMsg) { synchronized(mService) { //A.处理并行广播,忽略不需要处理超时 ... //B.处理当前有序广播 do { r = mOrderedBroadca(0); //获取所有该广播所有的接收者 int numReceivers = != null) ? r.receivers.size() : 0; if && r.dispatchTime > 0) { long now = Sy(); if ((numReceivers > 0) && (now > r.dispatchTime + (2*mTimeoutPeriod*numReceivers))) { //当广播处理时间超时,则强制结束这条广播 broadcastTimeoutLocked(false); ... } } if == null || r.nextReceiver >= numReceivers || r.resultAbort || forceReceive) { if != null) { //处理广播消息消息 performReceiveLocked, r.resultTo, new Inten), r.resultCode, r.resultData, r.resultExtras, false, false, r.userId); r.resultTo = null; } //取消BROADCAST_TIMEOUT_MSG消息 cancelBroadcastTimeoutLocked(); } } while (r == null); ... //C.获取下条有序广播 r.receiverTime = Sy(); if (!mPendingBroadcastTimeoutMessage) { long timeoutTime = r.receiverTime + mTimeoutPeriod; //设置广播超时时间,发送BROADCAST_TIMEOUT_MSG setBroadcastTimeoutLocked(timeoutTime); } ... } }

广播处理时机:

  • 1.在C的时候会setBroadcastTimeoutLocked(timeoutTime);

  • 2.在B串行处理的时候。

  • A.当广播接收者等待超时,这调用broadcastTimeoutLocked(false)

  • B.当处理结束的时候调用cancelBroadcastTimeoutLocked()

2.setBroadcastTimeoutLocked

final void setBroadcastTimeoutLocked(long timeoutTime) { if (! mPendingBroadcastTimeoutMessage) { Message msg = mHandler.obtainMessage(BROADCAST_TIMEOUT_MSG, this); mHandler.sendMessageAtTime(msg, timeoutTime); mPendingBroadcastTimeoutMessage = true; } }

long timeoutTime = r.receiverTime + mTimeoutPeriod mTimeoutPeriod中的时间是通过注册的是时候是前台广播或后台广播决定的。

static final int BROADCAST_FG_TIMEOUT = 10*1000;static final int BROADCAST_BG_TIMEOUT = 60*1000;

3.cancelBroadcastTimeoutLocked

final void cancelBroadcastTimeoutLocked() { if (mPendingBroadcastTimeoutMessage) { mHandler.removeMessages(BROADCAST_TIMEOUT_MSG, this); mPendingBroadcastTimeoutMessage = false; } }

4.BROADCAST_TIMEOUT_MSG处理

使用的内部定义的一个handle

private final class BroadcastHandler extends Handler { public BroadcastHandler(Looper looper) { super(looper, null, true); } @Override public void handleMessage(Message msg) { switch ) { case BROADCAST_INTENT_MSG: { if (DEBUG_BROADCAST) Slog.v( TAG_BROADCAST, "Received BROADCAST_INTENT_MSG"); processNextBroadcast(true); } break; case BROADCAST_TIMEOUT_MSG: { synchronized (mService) { broadcastTimeoutLocked(true); } } break; } } }

5.broadcastTimeoutLocked

final void broadcastTimeoutLocked(boolean fromMsg) { if (fromMsg) { mPendingBroadcastTimeoutMessage = false; } if () == 0) { return; } long now = Sy(); BroadcastRecord r = mOrderedBroadca(0); if (fromMsg) { if ) { //延迟timeouts直到dexopt结束 mService.mDidDexOpt = false; long timeoutTime = Sy() + mTimeoutPeriod; setBroadcastTimeoutLocked(timeoutTime); return; } if (!mService.mProcessesReady) { //当系统还没有准备就绪时,广播处理流程中不存在广播超时 return; } long timeoutTime = r.receiverTime + mTimeoutPeriod; if (timeoutTime > now) { //过早的timeout,重新设置广播超时 setBroadcastTimeoutLocked(timeoutTime); return; } } BroadcastRecord br = mOrderedBroadca(0); if == Broadca) { //广播已经处理完成,但需要等待已启动service执行完成。当等待足够时间,则处理下一条广播。 br.curComponent = null; br.state = Broadca; processNextBroadcast(false); return; } r.receiverTime = now; //当前BroadcastRecord的anr次数执行加1操作 r.anrCount++; if <= 0) { return; } ProcessRecord app = null; String anrMessage = null; Object curReceiver = r.receivers.get-1); //根据情况记录广播接收者丢弃的EventLog logBroadcastReceiverDiscardLocked(r); if (curReceiver instanceof BroadcastFilter) { BroadcastFilter bf = (BroadcastFilter)curReceiver; if != 0 && bf.receiverLi != Ac) { synchronized ) { app = mService.mPid( bf.receiverLi); } } } else { app = r.curApp; } if (app != null) { anrMessage = "Broadcast of " + r.in(); } if (mPendingBroadcast == r) { mPendingBroadcast = null; } //继续移动到下一个广播接收者 finishReceiverLocked(r, r.resultCode, r.resultData, r.resultExtras, r.resultAbort, false); scheduleBroadcastsLocked(); if (anrMessage != null) { mHandler.post(new AppNotResponding(app, anrMessage)); } }

3.3 ContentProvider Timeout

AMS.appNotRespondingViaProvider

public void appNotRespondingViaProvider(IBinder connection) { enforceCallingPermission( android.Mani, "appNotRespondingViaProvider()"); final ContentProviderConnection conn = (ContentProviderConnection) connection; if (conn == null) { return; } final ProcessRecord host = conn.; //无法找到provider所处的进程 if (host == null) { return; } final long token = Binder.clearCallingIdentity(); try { appNotResponding(host, null, null, false, "ContentProvider not responding"); } finally { Binder.restoreCallingIdentity(token); } }Con Con Ac AMP.appNotRespondingViaProvider AMS.appNotRespondingViaProvider

3.4 inputDispatching Timeout 和 keyDispatching Timeout

触发点在硬件

1.In 触发点在

// Native callback. private long notifyANR(InputApplicationHandle inputApplicationHandle, InputWindowHandle inputWindowHandle, String Reason) { return mWindowManagerCallbacks.notifyANR( inputApplicationHandle, inputWindowHandle, reason); } // mWindowManagerCallbacks为InputMonitor对象

2.InputMonitor

public long notifyANR(InputApplicationHandle inputApplicationHandle, InputWindowHandle inputWindowHandle, String reason) { AppWindowToken appWindowToken = null; WindowState windowState = null; boolean aboveSystem = false; synchronized ) { if (inputWindowHandle != null) { windowState = (WindowState) in; if (windowState != null) { appWindowToken = windowS; } } if (appWindowToken == null && inputApplicationHandle != null) { appWindowToken = (AppWindowToken; } //输出input事件分发超时log if (windowState != null) { Slog.i, "Input event dispatching timed out " + "sending to " + windowS() + ". Reason: " + reason); int systemAlertLayer = mService.mPolicy.windowTypeToLayerLw( WindowManager.Layou); aboveSystem = windowS > systemAlertLayer; } else if (appWindowToken != null) { Slog.i, "Input event dispatching timed out " + "sending to application " + a + ". Reason: " + reason); } else { Slog.i, "Input event dispatching timed out " + ". Reason: " + reason); } mService.saveANRStateLocked(appWindowToken, windowState, reason); } if (appWindowToken != null && a != null) { // A keyDispatching Timeout 设置 boolean abort = a.keyDispatchingTimedOut(reason); if (! abort) { return a; } } else if (windowState != null) { // B inputDispatching time out 设置 long timeout = Ac().inputDispatchingTimedOut( windowS, aboveSystem, reason); if (timeout >= 0) { return timeout * 1000000L; //转化为纳秒 } } return 0; }

A.keyDispatching Timeout 设置

  1. Ac

@Override public boolean keyDispatchingTimedOut(String reason, int windowPid) { ActivityRecord anrActivity; ProcessRecord anrApp; boolean windowFromSameProcessAsActivity; synchronized (service) { anrActivity = getWaitingHistoryRecordLocked(); anrApp = app; windowFromSameProcessAsActivity = app == null || a == windowPid || windowPid == -1; } if (windowFromSameProcessAsActivity) { return (anrApp, anrActivity, this, false, reason); } else { // In this case another process added windows using this activity token. So, we call the // generic service input dispatch timed out method so that the right process is blamed. return (windowPid, false /* aboveSystem */, reason) < 0; } }

调用AMS.inputDispatchingTimedOut

B.inputDispatching time out 设置 1.调用的是AMS.inputDispatchingTimedOut

ublic long inputDispatchingTimedOut(int pid, final boolean aboveSystem, String reason) { ... ProcessRecord proc; long timeout; synchronized (this) { synchronized (mPidsSelfLocked) { proc = mPid(pid); //根据pid查看进程record } timeout = getInputDispatchingTimeoutLocked(proc); } if (!inputDispatchingTimedOut(proc, null, null, aboveSystem, reason)) { return -1; } return timeout; }

设置时间

// How long we wait until we timeout on key dispatching. static final int KEY_DISPATCHING_TIMEOUT = 5*1000;public static long getInputDispatchingTimeoutLocked(ProcessRecord r) { if (r != null && != null || r.usingWrapper)) { return INSTRUMENTATION_KEY_DISPATCHING_TIMEOUT; } return KEY_DISPATCHING_TIMEOUT; }

2.调用的是AMS.inputDispatchingTimedOut

public boolean inputDispatchingTimedOut(final ProcessRecord proc, final ActivityRecord activity, final ActivityRecord parent, final boolean aboveSystem, String reason) { ... final String annotation; if (reason == null) { annotation = "Input dispatching timed out"; } else { annotation = "Input dispatching timed out (" + reason + ")"; } if (proc != null) { ... mHandler.post(new Runnable() { public void run() { appNotResponding(proc, activity, parent, aboveSystem, annotation); } }); } return true; }

4.ANR工作

调用后都触发了appNotResponding 下面来介绍下这个

ActivityManagerService appNotResponding

final void appNotResponding(ProcessRecord app, ActivityRecord activity, ActivityRecord parent, boolean aboveSystem, final String annotation) { ArrayList<Integer> firstPids = new ArrayList<Integer>(5); SparseArray<Boolean> lastPids = new SparseArray<Boolean>(20); if (mController != null) { try { // 0 == continue, -1 = kill process immediately int res = mCon, a, annotation); if (res < 0 && a != MY_PID) { a("anr", true); } } catch (RemoteException e) { mController = null; Wa().setActivityController(null); } } long anrTime = Sy(); if (MONITOR_CPU_USAGE) { updateCpuStatsNow(); } synchronized (this) { // PowerManager.reboot() 会阻塞很长时间,因此忽略关机时的ANR if (mShuttingDown) { Slog.i(TAG, "During shutdown skipping ANR: " + app + " " + annotation); return; } else if ) { Slog.i(TAG, "Skipping duplicate ANR: " + app + " " + annotation); return; } else if ) { Slog.i(TAG, "Crashing app skipping ANR: " + app + " " + annotation); return; } a = true; //记录ANR Even, a, a, a, a, annotation); // Dump thread traces as quickly as we can, starting with "interesting" processes. (a); int parentPid = a; if (parent != null && != null && parent.a > 0) parentPid = parent.a; if (parentPid != a) (parentPid); if (MY_PID != a && MY_PID != parentPid) (MY_PID); for (int i = mLruProce() - 1; i >= 0; i--) { ProcessRecord r = mLruProce(i); if (r != null && r.thread != null) { int pid = r.pid; if (pid > 0 && pid != a && pid != parentPid && pid != MY_PID) { if ) { (pid); } else { la(pid, Boolean.TRUE); } } } } } //输出ANR到main log. StringBuilder info = new StringBuilder(); in(0); in("ANR in ").append); if (activity != null && ac != null) { in(" (").append(ac).append(")"); } in("\n"); in("PID: ").append(a).append("\n"); if (annotation != null) { in("Reason: ").append(annotation).append("\n"); } if (parent != null && parent != activity) { in("Parent: ").append).append("\n"); } final ProcessCpuTracker processCpuTracker = new ProcessCpuTracker(true); //dump栈信息 File tracesFile = dumpStackTraces(true, firstPids, processCpuTracker, lastPids, NATIVE_STACKS_OF_INTEREST); String cpuInfo = null; if (MONITOR_CPU_USAGE) { updateCpuStatsNow(); synchronized (mProcessCpuTracker) { //输出各个进程的CPU使用情况 cpuInfo = mProce(anrTime); } //输出CPU负载 in()); in(cpuInfo); } in(proce(anrTime)); Slog.e(TAG, in()); if (tracesFile == null) { //发送signal 3来dump栈信息 Proce(a, Proce); } //将anr信息添加到dropbox addErrorToDropBox("anr", app, a, activity, parent, annotation, cpuInfo, tracesFile, null); if (mController != null) { try { // 0 == show dialog, 1 = keep waiting, -1 = kill process immediately int res = mCon, a, in()); if (res != 0) { if (res < 0 && a != MY_PID) { a("anr", true); } else { synchronized (this) { mServices.scheduleServiceTimeoutLocked(app); } } return; } } catch (RemoteException e) { mController = null; Wa().setActivityController(null); } } boolean showBackground = Se(), Se, 0) != 0; synchronized (this) { mBa, a); if (!showBackground && !a() && a != MY_PID) { a("bg anr", true); return; } // Set the app's notResponding state, and look up the errorReportReceiver makeAppNotRespondingLocked(app, activity != null ? ac : null, annotation != null ? "ANR " + annotation : "ANR", in()); //弹出ANR对话框 Message msg = Me(); HashMap<String, Object> map = new HashMap<String, Object>(); m = SHOW_NOT_RESPONDING_MSG; m = map; m = aboveSystem ? 1 : 0; map.put("app", app); if (activity != null) { map.put("activity", activity); } mUiHandler.sendMessage(msg); } }

5.优化方法

  • 1、运行在主线程里的任何方法都尽可能少做事情。特别是,Activity应该在它的关键生命周期方法(如onCreate()和onResume())里尽可能少的去做创建操作。(可以采用重新开启子线程的方式,然后使用Handler+Message的方式做一些操作,比如更新主线程中的ui等)

  • 2、应用程序应该避免在BroadcastReceiver里做耗时的操作或计算。但也不要在子线程里做这些任务(因为 BroadcastReceiver的生命周期短),如果响应Intent广播需要执行一个耗时的动作的话,应用程序应该启动一个 Service。

  • 3、避免在Intent Receiver里启动一个Activity,因为它会创建一个新的画面,并从当前用户正在运行的程序上抢夺焦点。如果你的应用程序在响应Intent广播时需要向用户展示什么,你应该使用Notification Manager来实现。

  • 4.anr异常也是在程序中自己经常遇到的问题,主要的解决办法自己最常用的就是不要在主线程中做耗时的操作,而应放在子线程中来实现,比如采用Handler+mesage的方式,或者是有时候需要做一些和网络相互交互的耗时操作就采用asyntask异步任务的方式(它的底层其实Handler+mesage有所区别的是它是线程池)等,在主线程中更新UI。

头条中编辑代码实在难看。有兴趣可以在简书上搜索

责任编辑: 鲁达

1.内容基于多重复合算法人工智能语言模型创作,旨在以深度学习研究为目的传播信息知识,内容观点与本网站无关,反馈举报请
2.仅供读者参考,本网站未对该内容进行证实,对其原创性、真实性、完整性、及时性不作任何保证;
3.本站属于非营利性站点无毒无广告,请读者放心使用!

“如何制造anr,如何制造鹌鹑蛋孵蛋器,如何制造暗器,如何制造安忍水,如何制造安静书”边界阅读