Android中ANR的分析和解决

一、认识ANR1、 ANR的定义
ANR , 是“Application Not Responding”的缩写 , 即“应用程序无响应” 。如果你应用程序在UI线程被阻塞太长时间 , 就会出现ANR , 通常出现ANR , 系统会弹出一个提示提示框 , 让用户知道 , 该程序正在被阻塞 , 是否继续等待还是关闭 。
2、ANR的类型
(1)KeyDispatchTimeout(常见)
input事件在5S内没有处理完成发生了ANR 。
logcat日志关键字:Input event dispatching timed out
(2)BroadcastTimeout
前台Broadcast:onReceiver在10S内没有处理完成发生ANR 。
后台Broadcast:onReceiver在60s内没有处理完成发生ANR 。
logcat日志关键字:Timeout of broadcast BroadcastRecord
(3)ServiceTimeout
前台Service:onCreate , onStart , onBind等生命周期在20s内没有处理完成发生ANR 。
后台Service:onCreate , onStart , onBind等生命周期在200s内没有处理完成发生ANR
logcat日志关键字:Timeout executing service
(4)ContentProviderTimeout
ContentProvider 在10S内没有处理完成发生ANR 。
logcat日志关键字:timeout publishing content providers
3、ANR出现的原因
(1)主线程频繁进行耗时的IO操作:如数据库读写
(2)多线程操作的死锁 , 主线程被block;
(3)主线程被Binder 对端block;
(4)System Server中WatchDog出现ANR;
(5)service binder的连接达到上线无法和和System Server通信
(6)系统资源已耗尽(管道、CPU、IO)
4、如何分析ANR
(1)日志分析:ANR发生时都会在log中输出错误信息 , 从log中可以获得ANR的类型 , CPU的使用情况 , CPU使用率过高有可能是CPU饥饿导致了ANR 。CPU使用率过低说明主线程被block了 , 如果IOwait高是因为主线程进行I/O操作造成的 。
(2)traces文件分析:除了log输出外 , 你会发现各个应用进程和系统进程的函数堆栈信息都输出到了一个/data/anr/traces.txt的文件中 , 这个文件是分析ANR原因的关键文件.要获取到该文件可使用adb指令进行赋权后拉出查看调用stack 。通过log、trace.text、代码结合分析ANR的成因(iowait?Memoryleak?Block?)
(3)traces文件无法分析的:不过还存在一些ANR问题 , trace文件是分析不了的,例如我们的系统上,人脸识别活体攻击的时候,native算法耗尽cpu资源导致其他app无法抢占cpu时间片导致anr,假如ANR的app是你开发的,估计查到死也找不到问题所在,类似这类问题也写过简要的分析文章:
接下来我们将一步一步分析ANR,这个过程能让我们进一步明白如何找到问题、分析问题以及解决问题 。
二、ANR发生时调用的关键代码当ANR发生的时候 , 系统会调用如下相关的关键函数代码 , 来将系统当前的关键信息保存到日志当中:

framework/base/services/core/JAVA/com/Android/server/am/AppErrors.java
Javaclass AppErrors {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 (mService.mController != null) {try {// 0 == continue, -1 = kill process immediatelyint res = mService.mController.appEarlyNotResponding(app.processName, app.pid, annotation);if (res < 0 && app.pid != MY_PID) {app.kill("anr", true);}} catch (RemoteException e) {mService.mController = null;Watchdog.getInstance().setActivityController(null);}}long anrTime = SystemClock.uptimeMillis();if (ActivityManagerService.MONITOR_CPU_USAGE) {mService.updateCpuStatsNow();}// Unless configured otherwise, swallow ANRs in background processes & kill the process.boolean showBackground = Settings.Secure.getInt(mContext.getContentResolver(),Settings.Secure.ANR_SHOW_BACKGROUND, 0) != 0;boolean isSilentANR;synchronized (mService) {// PowerManager.reboot() can block for a long time, so ignore ANRs while shutting down.if (mService.mShuttingDown) {Slog.i(TAG, "During shutdown skipping ANR: " + app + " " + annotation);return;} else if (app.notResponding) {Slog.i(TAG, "Skipping duplicate ANR: " + app + " " + annotation);return;} else if (app.crashing) {Slog.i(TAG, "Crashing app skipping ANR: " + app + " " + annotation);return;} else if (app.killedByAm) {Slog.i(TAG, "App already killed by AM skipping ANR: " + app + " " + annotation);return;} else if (app.killed) {Slog.i(TAG, "Skipping died app ANR: " + app + " " + annotation);return;}// In case we come through here for the same app before completing// this one, mark as anring now so we will bail out.app.notResponding = true;// 记录ANR到event logEventLog.writeEvent(EventLogTags.AM_ANR, app.userId, app.pid,app.processName, app.info.flags, annotation);// Dump thread traces as quickly as we can, starting with "interesting" processes.firstPids.add(app.pid);// Don't dump other PIDs if it's a background ANRisSilentANR = !showBackground && !isInterestingForBackgroundTraces(app);if (!isSilentANR) {int parentPid = app.pid;if (parent != null && parent.app != null && parent.app.pid > 0) {parentPid = parent.app.pid;}if (parentPid != app.pid) firstPids.add(parentPid);if (MY_PID != app.pid && MY_PID != parentPid) firstPids.add(MY_PID);for (int i = mService.mLruProcesses.size() - 1; i >= 0; i--) {ProcessRecord r = mService.mLruProcesses.get(i);if (r != null && r.thread != null) {int pid = r.pid;if (pid > 0 && pid != app.pid && pid != parentPid && pid != MY_PID) {if (r.persistent) {firstPids.add(pid);if (DEBUG_ANR) Slog.i(TAG, "Adding persistent proc: " + r);} else if (r.treatLikeActivity) {firstPids.add(pid);if (DEBUG_ANR) Slog.i(TAG, "Adding likely IME: " + r);} else {lastPids.put(pid, Boolean.TRUE);if (DEBUG_ANR) Slog.i(TAG, "Adding ANR proc: " + r);}}}}}}// 记录ANR 到 main logStringBuilder info = new StringBuilder();info.setLength(0);info.append("ANR in ").append(app.processName); //例如:ANR in com.xxxx.moblie (进程名)if (activity != null && activity.shortComponentName != null) {info.append(" (").append(activity.shortComponentName).append(")");//例如:(com.xxxx.moblie/.ui.MainActivity)}info.append("n");//换行info.append("PID: ").append(app.pid).append("n");//记录进程idif (annotation != null) {info.append("Reason: ").append(annotation).append("n");}if (parent != null && parent != activity) {info.append("Parent: ").append(parent.shortComponentName).append("n");}ProcessCpuTracker processCpuTracker = new ProcessCpuTracker(true);// don't dump native PIDs for background ANRs unless it is the process of interestString[] nativeProcs = null;if (isSilentANR) {for (int i = 0; i < NATIVE_STACKS_OF_INTEREST.length; i++) {if (NATIVE_STACKS_OF_INTEREST[i].equals(app.processName)) {nativeProcs = new String[] { app.processName };break;}}} else {nativeProcs = NATIVE_STACKS_OF_INTEREST;}int[] pids = nativeProcs == null ? null : Process.getPidsForCommands(nativeProcs);ArrayList<Integer> nativePids = null;if (pids != null) {nativePids = new ArrayList<Integer>(pids.length);for (int i : pids) {nativePids.add(i);}}// For background ANRs, don't pass the ProcessCpuTracker to// avoid spending 1/2 second collecting stats to rank lastPids.File tracesFile = ActivityManagerService.dumpStackTraces(true, firstPids,(isSilentANR) ? null : processCpuTracker,(isSilentANR) ? null : lastPids,nativePids);String cpuInfo = null;if (ActivityManagerService.MONITOR_CPU_USAGE) {mService.updateCpuStatsNow();synchronized (mService.mProcessCpuTracker) {cpuInfo = mService.mProcessCpuTracker.printCurrentState(anrTime);}info.append(processCpuTracker.printCurrentLoad());info.append(cpuInfo);}info.append(processCpuTracker.printCurrentState(anrTime));//将ANR相关的错误日志信息打印出来Slog.e(TAG, info.toString());...省略...}


推荐阅读