ANR-application not response,应用无响应,应用开发者一般是关注自己的APP进程有没有出现,系统开发者会关注当前系统运行起来后整体上所有的APP进程有没有出现ANR,从这句话可以知道,只有应用进程的主线程(UI线程)超时可能导致ANR,我们从系统整体角度来阐述问题,顺便也能包含应用出现的ANR bug。
从现象来看,ANR出现时,会弹出一个应用无响应的弹框,当然也可能不弹,一是因为现在的设备厂商觉得这弹框难看,给去掉了,二是有些ANR本身就只有log记录,不会出现弹框。一直听到的就是不能在主线程做耗时操作,否则可能造成ANR,这里首先要明确:主线程做了大量耗时操作后,还得有事件产生,按键也好、点击触摸的touch事件也好,这样才会出现,整体上可以归成几类:
1、应用进程主线程导致,这里主要表现的就是activity-5s超时(会弹框),broadcast-10s超过,service-20s超时,三类超时后,再产生事件,这个事件无法传递给焦点窗口就会上报ANR
2、CPU耗尽
3、内存耗尽
简单来说,ANR就是产生的事件长时间没机会执行,Google就设计了这样一种提示用户的交互流程,防止用户以为设备手机坏成板砖了,如果没有这样的提醒,用户可能会说,这什么破手机、破系统,进而扣电池砸手机,有个弹框就能把问题聚焦到具体的某个APP上。因此CPU或者内存耗尽,它显然也会导致ANR的出现,恰好此时的APP上某个控件点击事件他就是无法执行(CPU功耗高,UI线程一直抢不到执行的时间片)而超时。内存耗尽也是同样的逻辑。可以看到,出现ANR的进程中,有时候是自身进程里导致的,也有是受了池鱼之灾,其他进程占用CPU太多,导致自己完全无法执行而出现。
二、ANR怎么入手分析上面说了ANR是怎么一回事,以及ANR为什么会产生,接着我们来分析,出现ANR应该怎么查看,怎么追踪问题是哪里产生。出现ANR时(怎么让设备出现ANR日志,最简单的办法是shell命令:adb shell kill 3 pid)," data/anr/ “目录下会产生类似"anr_2022-03-29-15-45-53-142"这样命名的文件(老版本时ANR产生的日志叫” data/anr/ traces.txt",老版本只会记录最近一次ANR发生时的系统堆栈,高版本改了,默认可以存储64个文件,每发生一次ANR,就会生成一个文件),因此在产生ANR时取出日志,我们可以人为的制造一次ANR,很容易-activity的oncreate()周期里来个死循环,然后跳转这个activity后,点击屏幕,OK-anr_2022-03-29-15-45-53-142文件产生,下面来看如何分析ANR日志:
2.1、Traces文件详细分析----- pid 30675 at 2022-03-29 15:45:53 -----
Cmd line: com.android.test
开头就是发生ANR的进程pid、时间、以及进程名,从开头的信息,系统开发人员就能把这个bug分给具体某个应用负责人了,当然有些时候发生ANR的进程可能没问题,是被别人给耽误了,所以接着往下看,
Build fingerprint: ‘/ int32_t InputDispatcher::findTouchedWindowTargetsLocked(nsecs_t currentTime, const MotionEntry* entry, std::vector
& inputTargets, nsecs_t* nextWakeupTime, bool* outConflictingPointerActions) { ... // Step1、判断触摸过的窗口是否都可以接收新的事件,这里就是判断这些窗口环境是否搭建好 for (const TouchedWindow& touchedWindow : mTempTouchState.windows) { if (touchedWindow.targetFlags & InputTarget::FLAG_FOREGROUND) { // Check whether the window is ready for more input. std::string reason = checkWindowReadyForMoreInputLocked(currentTime, touchedWindow.windowHandle, entry, "touched"); if (!reason.empty()) { //Step2、出现模块的环境未配置好, injectionResult = handleTargetsNotReadyLocked(currentTime, entry, nullptr, touchedWindow.windowHandle, nextWakeupTime, reason.c_str()); goto Unresponsive; } } } for (const TouchedWindow& touchedWindow : mTempTouchState.windows) { //Step3、成功为当前的事件找到符合的窗口,将其添加到inputTarget中 addWindowTargetLocked(touchedWindow.windowHandle, touchedWindow.targetFlags, touchedWindow.pointerIds, inputTargets); } for (const TouchedMonitor& touchedMonitor : mTempTouchState.gestureMonitors) { addMonitoringTargetLocked(touchedMonitor.monitor, touchedMonitor.xOffset, touchedMonitor.yOffset, inputTargets); } ... } int32_t InputDispatcher::handleTargetsNotReadyLocked(nsecs_t currentTime, const EventEntry* entry, const sp & applicationHandle, const sp & windowHandle, nsecs_t* nextWakeupTime, const char* reason) { ... if (currentTime >= mInputTargetWaitTimeoutTime) { onANRLocked(currentTime, applicationHandle, windowHandle, entry->eventTime, mInputTargetWaitStartTime, reason);//很明显,ANR // Force poll loop to wake up immediately on next iteration once we get the // ANR response back from the policy. *nextWakeupTime = LONG_LONG_MIN; return INPUT_EVENT_INJECTION_PENDING; } ... } void InputDispatcher::onANRLocked( nsecs_t currentTime, const sp & applicationHandle, const sp & windowHandle, nsecs_t eventTime, nsecs_t waitStartTime, const char* reason) { float dispatchLatency = (currentTime - eventTime) * 0.000001f; float waitDuration = (currentTime - waitStartTime) * 0.000001f; ALOGI("Application is not responding: %s. " "It has been %0.1fms since event, %0.1fms since wait started. Reason: %s", getApplicationWindowLabel(applicationHandle, windowHandle).c_str(), dispatchLatency, waitDuration, reason); // Capture a record of the InputDispatcher state at the time of the ANR. time_t t = time(nullptr); struct tm tm; localtime_r(&t, &tm); char timestr[64]; strftime(timestr, sizeof(timestr), "%F %T", &tm); mLastANRState.clear(); mLastANRState += INDENT "ANR:n"; mLastANRState += StringPrintf(INDENT2 "Time: %sn", timestr); mLastANRState += StringPrintf(INDENT2 "Window: %sn", getApplicationWindowLabel(applicationHandle, windowHandle).c_str()); mLastANRState += StringPrintf(INDENT2 "DispatchLatency: %0.1fmsn", dispatchLatency); mLastANRState += StringPrintf(INDENT2 "WaitDuration: %0.1fmsn", waitDuration); mLastANRState += StringPrintf(INDENT2 "Reason: %sn", reason); dumpDispatchStateLocked(mLastANRState); CommandEntry* commandEntry = postCommandLocked( & InputDispatcher::doNotifyANRLockedInterruptible); commandEntry->inputApplicationHandle = applicationHandle; commandEntry->inputChannel = windowHandle != nullptr ? getInputChannelLocked(windowHandle->getToken()) : nullptr; commandEntry->reason = reason; } void InputDispatcher::doNotifyANRLockedInterruptible( CommandEntry* commandEntry) { mLock.unlock(); nsecs_t newTimeout = mPolicy->notifyANR( commandEntry->inputApplicationHandle, commandEntry->inputChannel ? commandEntry->inputChannel->getToken() : nullptr, commandEntry->reason); mLock.lock(); resumeAfterTargetsNotReadyTimeoutLocked(newTimeout, commandEntry->inputChannel); } 注意:
mPolicy->notifyANR后(jni机制调用),流程走到IMS的notifyANR,//com_android_server_input_InputManagerService.cpp nsecs_t NativeInputManager::notifyANR(const sp& inputApplicationHandle, const sp & token, const std::string& reason) { jlong newTimeout = env->CallLongMethod(mServiceObj, gServiceClassInfo.notifyANR, tokenObj, reasonObj); if (checkAndClearExceptionFromCallback(env, "notifyANR")) { newTimeout = 0; // abort dispatch } else { assert(newTimeout >= 0); } return newTimeout; } //InputManagerService.java private long notifyANR(IBinder token, String reason) { return mWindowManagerCallbacks.notifyANR( token, reason); } //InputManagerCallback.java @Override public long notifyANR(IBinder token, String reason) { //输入事件log打印,不同情况打印不一样。关键字Input event dispatching timed out synchronized (mService.mGlobalLock) { Slog.i(TAG_WM, "Input event dispatching timed out " + ...); } ... } else if (windowState != null) { // Notify the activity manager about the timeout and let it decide whether // to abort dispatching or keep waiting. long timeout = mService.mAmInternal.inputDispatchingTimedOut( windowState.mSession.mPid, aboveSystem, reason); if (timeout >= 0) { // The activity manager declined to abort dispatching. // Wait a bit longer and timeout again later. return timeout * 1000000L; // nanoseconds } } return 0; // abort dispatching } //ActivityManagerService.java @VisibleForTesting public final class LocalService extends ActivityManagerInternal { @Override public long inputDispatchingTimedOut(int pid, boolean aboveSystem, String reason) { synchronized (ActivityManagerService.this) { return ActivityManagerService.this.inputDispatchingTimedOut( pid, aboveSystem, reason); } } } long inputDispatchingTimedOut(int pid, final boolean aboveSystem, String reason) { synchronized (this) { timeout = proc != null ? proc.getInputDispatchingTimeout() : KEY_DISPATCHING_TIMEOUT_MS; } if (inputDispatchingTimedOut(proc, null, null, null, null, aboveSystem, reason)) { return -1; } return timeout; } boolean inputDispatchingTimedOut(ProcessRecord proc, String activityShortComponentName, ApplicationInfo aInfo, String parentShortComponentName, WindowProcessController parentProcess, boolean aboveSystem, String reason) { if (proc != null) { proc.appNotResponding(activityShortComponentName, aInfo, parentShortComponentName, parentProcess, aboveSystem, annotation); } return true; } 流程很明确,最后调用到appNotResponding函数(frameworks/base/services/core/java/com/android/server/am/ProcessRecord.java),
void appNotResponding(String activityShortComponentName, ApplicationInfo aInfo, String parentShortComponentName, WindowProcessController parentProcess, boolean aboveSystem, String annotation) { mWindowProcessController.appEarlyNotResponding(annotation, () -> kill("anr", true)); long anrTime = SystemClock.uptimeMillis(); if (isMonitorCpuUsage()) { mService.updateCpuStatsNow(); } synchronized (mService) { // PowerManager.reboot() can block for a long time, so ignore ANRs while shutting down. if (mService.mAtmInternal.isShuttingDown()) { Slog.i(TAG, "During shutdown skipping ANR: " + this + " " + annotation); return; } else if (isNotResponding()) { Slog.i(TAG, "Skipping duplicate ANR: " + this + " " + annotation); return; } else if (isCrashing()) { Slog.i(TAG, "Crashing app skipping ANR: " + this + " " + annotation); return; } else if (killedByAm) { Slog.i(TAG, "App already killed by AM skipping ANR: " + this + " " + annotation); return; } else if (killed) { Slog.i(TAG, "Skipping died app ANR: " + this + " " + 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. setNotResponding(true); // Log the ANR to the event log. EventLog.writeEvent(EventLogTags.AM_ANR, userId, pid, processName, info.flags, annotation); // Dump thread traces as quickly as we can, starting with "interesting" processes. firstPids.add(pid); // Don't dump other PIDs if it's a background ANR if (!isSilentAnr()) { int parentPid = pid; if (parentProcess != null && parentProcess.getPid() > 0) { parentPid = parentProcess.getPid(); } if (parentPid != pid) firstPids.add(parentPid); if (MY_PID != pid && MY_PID != parentPid) firstPids.add(MY_PID); for (int i = getLruProcessList().size() - 1; i >= 0; i--) { ProcessRecord r = getLruProcessList().get(i); if (r != null && r.thread != null) { int myPid = r.pid; if (myPid > 0 && myPid != pid && myPid != parentPid && myPid != MY_PID) { if (r.isPersistent()) { firstPids.add(myPid); if (DEBUG_ANR) Slog.i(TAG, "Adding persistent proc: " + r); } else if (r.treatLikeActivity) { firstPids.add(myPid); if (DEBUG_ANR) Slog.i(TAG, "Adding likely IME: " + r); } else { lastPids.put(myPid, Boolean.TRUE); if (DEBUG_ANR) Slog.i(TAG, "Adding ANR proc: " + r); } } } } } } // Log the ANR to the main log. StringBuilder info = new StringBuilder(); info.setLength(0); info.append("ANR in ").append(processName); if (activityShortComponentName != null) { info.append(" (").append(activityShortComponentName).append(")"); } info.append("n"); info.append("PID: ").append(pid).append("n"); if (annotation != null) { info.append("Reason: ").append(annotation).append("n"); } if (parentShortComponentName != null && parentShortComponentName.equals(activityShortComponentName)) { info.append("Parent: ").append(parentShortComponentName).append("n"); } ProcessCpuTracker processCpuTracker = new ProcessCpuTracker(true); ArrayListnativePids = null; // don't dump native PIDs for background ANRs unless it is the process of interest String[] nativeProc = null; if (isSilentAnr()) { for (int i = 0; i < NATIVE_STACKS_OF_INTEREST.length; i++) { if (NATIVE_STACKS_OF_INTEREST[i].equals(processName)) { nativeProc = new String[] { processName }; break; } } int[] pid = nativeProc == null ? null : Process.getPidsForCommands(nativeProc); if(pid != null){ nativePids = new ArrayList<>(pid.length); for (int i : pid) { nativePids.add(i); } } } else { nativePids = Watchdog.getInstance().getInterestingNativePids(); } //Step1、生成Traces文件 File tracesFile = ActivityManagerService.dumpStackTraces(firstPids, (isSilentAnr()) ? null : processCpuTracker, (isSilentAnr()) ? null : lastPids, nativePids); String cpuInfo = null; if (isMonitorCpuUsage()) { mService.updateCpuStatsNow(); synchronized (mService.mProcessCpuTracker) { cpuInfo = mService.mProcessCpuTracker.printCurrentState(anrTime); } info.append(processCpuTracker.printCurrentLoad()); info.append(cpuInfo); } info.append(processCpuTracker.printCurrentState(anrTime)); Slog.e(TAG, info.toString()); if (tracesFile == null) { // There is no trace file, so dump (only) the alleged culprit's threads to the log Process.sendSignal(pid, Process.SIGNAL_QUIT); } StatsLog.write(StatsLog.ANR_OCCURRED, uid, processName, activityShortComponentName == null ? "unknown": activityShortComponentName, annotation, (this.info != null) ? (this.info.isInstantApp() ? StatsLog.ANROCCURRED__IS_INSTANT_APP__TRUE : StatsLog.ANROCCURRED__IS_INSTANT_APP__FALSE) : StatsLog.ANROCCURRED__IS_INSTANT_APP__UNAVAILABLE, isInterestingToUserLocked() ? StatsLog.ANROCCURRED__FOREGROUND_STATE__FOREGROUND : StatsLog.ANROCCURRED__FOREGROUND_STATE__BACKGROUND, getProcessClassEnum(), (this.info != null) ? this.info.packageName : ""); final ProcessRecord parentPr = parentProcess != null ? (ProcessRecord) parentProcess.mOwner : null; mService.addErrorToDropBox("anr", this, processName, activityShortComponentName, parentShortComponentName, parentPr, annotation, cpuInfo, tracesFile, null); if (mWindowProcessController.appNotResponding(info.toString(), () -> kill("anr", true), () -> { synchronized (mService) { mService.mServices.scheduleServiceTimeoutLocked(this); } })) { return; } synchronized (mService) { // mBatteryStatsService can be null if the AMS is constructed with injector only. This // will only happen in tests. if (mService.mBatteryStatsService != null) { mService.mBatteryStatsService.noteProcessAnr(processName, uid); } if (isSilentAnr() && !isDebugging()) { kill("bg anr", true); return; } // Set the app's notResponding state, and look up the errorReportReceiver makeAppNotRespondingLocked(activityShortComponentName, annotation != null ? "ANR " + annotation : "ANR", info.toString()); // mUiHandler can be null if the AMS is constructed with injector only. This will only // happen in tests. if (mService.mUiHandler != null) { // Bring up the infamous App Not Responding dialog Message msg = Message.obtain(); msg.what = ActivityManagerService.SHOW_NOT_RESPONDING_UI_MSG; msg.obj = new AppNotRespondingDialog.Data(this, aInfo, aboveSystem); mService.mUiHandler.sendMessage(msg); } } } ActivityManagerService.dumpStackTraces,再调回到AMS里,由dumpStackTraces函数来创建并往文件中写入内容(data/anr/traces_**.txt),至此ANR分析完成。
参考文档:
top命令:https://www.cnblogs.com/lisa2016/p/12751070.html
Android事件传递:https://www.cnblogs.com/fanglongxiang/p/14091511.html



