栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > Java

ANR问题的分析与解决思路

Java 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

ANR问题的分析与解决思路

一、ANR介绍

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);
        ArrayList nativePids = 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

转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/999789.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号