Android ANR(二)-触发原理

作者: Stan_Z | 来源:发表于2019-08-04 16:19 被阅读7次

    接之前的文章:Android ANR问题(一)-基本分析方法,这篇文章总结一下ANR触发原理。

    一、Service ANR

    发生原因:主线程执行Service各个生命周期函数在规定时间内(前台20s,后台200s)没有完成。

    提示语:Reason: Executing service +包名

    Service ANR触发流程

    service执行流程

    如何检测Service超时:

    这里先发送一个超时消息,如果在阀值时间范围内完成服务任务,会调用到serviceDoneExecuting则会移除消息,否则就触发ANR流程。

    二、Broadcast ANR

    发生原因:主线程在执行 BroadcastReceiver 的 onReceive 函数时在规定时间内(前台10s,后台60s)没有执行完。

    提示语:Reason: Broadcast of Intent { act=XXX flg=XXX cmp=XXX }

    Broadcast ANR触发流程

    发送和接收广播流程 如何检测Broadcast超时:
    frameworks/base/services/core/java/com/android/server/am/BroadcastQueue.java
    
    final void processNextBroadcast(boolean fromMsg) {
    …
    do {
        if (mOrderedBroadcasts.size() == 0) {
            //串行广播为空直接return了,广播ANR只针对串行广播
            return;
       }
    //从串行广播中取出一个
        r = mOrderedBroadcasts.get(0);
    ...
       if (mService.mProcessesReady && r.dispatchTime > 0) {
            long now = SystemClock.uptimeMillis();
           if ((numReceivers > 0) &&
                    (now > r.dispatchTime + (2*mTimeoutPeriod*numReceivers))) {
                //开始超时检测
               broadcastTimeoutLocked(false); // forcibly finish this broadcast
               forceReceive = true;
               r.state = BroadcastRecord.IDLE;
           }
        }
    …
    //do while循环判断广播消息是否处理完毕,如果是则从mOrderedBroadcasts中移除,重新循环处理下一条,否则满足条件跳出循环则走ANR逻辑。
        if (r.receivers == null || r.nextReceiver >= numReceivers
                || r.resultAbort || forceReceive) {
    …
           cancelBroadcastTimeoutLocked();
    ...
            mOrderedBroadcasts.remove(0);
    ...
           continue;
       }
    } while (r == null);
    …
    满足条件则发送超时广播,这里同样是发送TIME_OUT的消息
    setBroadcastTimeoutLocked(timeoutTime);
    }
    

    三、ContentProvider ANR

    发生原因:主线程在执行 ContentProvider 相关操作时没有在规定的时间内(10s)执行完毕。

    提示语:Reason: timeout publishing content providers(不会报告 ANR弹框)。

    ContentProvider ANR触发流程

    ContentProvider执行流程

    如何检测ContentProvider超时:

    四、Input

    发生原因:主线程对当前输入事件在规定时间内(5S)没有处理完毕,并且下一个Input时间又来了在等它处理完(没有下一个Input的情况下,超时也不会发生)。

    提示语:Reason: Input dispatching timed out(reason 这里理由就有很多了)

    这部分具体分析可参考之前的文章:Android Input(八)- ANR原理分析

    最后来看看ANR的执行:
    前面各大组件包括Input到最后都是发送一个超时的消息,经过一系列判断后,最终调用AppErrors的appNotResponding方法执行ANR处理。

    frameworks/base/services/core/java/com/android/server/am/AppErrors.java
    
    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 immediately
                int 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;
            // Log the ANR to the event log.
            EventLog.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 ANR
            isSilentANR = !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);
                            }
                        }
                    }
                }
            }
        }
        // Log the ANR to the main log.
        StringBuilder info = new StringBuilder();
        info.setLength(0);
        info.append("ANR in ").append(app.processName);
        if (activity != null && activity.shortComponentName != null) {
            info.append(" (").append(activity.shortComponentName).append(")");
        }
        info.append("\n");
        info.append("PID: ").append(app.pid).append("\n");
        if (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 interest
        String[] 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 = mService.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));
        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(app.pid, Process.SIGNAL_QUIT);
        }
        mService.addErrorToDropBox("anr", app, app.processName, activity, parent, annotation,
                cpuInfo, tracesFile, null);
        if (mService.mController != null) {
            try {
                // 0 == show dialog, 1 = keep waiting, -1 = kill process immediately
                int res = mService.mController.appNotResponding(
                        app.processName, app.pid, info.toString());
                if (res != 0) {
                    if (res < 0 && app.pid != MY_PID) {
                        app.kill("anr", true);
                    } else {
                        synchronized (mService) {
                            mService.mServices.scheduleServiceTimeoutLocked(app);
                        }
                    }
                    return;
                }
            } catch (RemoteException e) {
                mService.mController = null;
                Watchdog.getInstance().setActivityController(null);
            }
        }
        synchronized (mService) {
            mService.mBatteryStatsService.noteProcessAnr(app.processName, app.uid);
            if (isSilentANR) {
                app.kill("bg anr", true);
                return;
            }
            // Set the app's notResponding state, and look up the errorReportReceiver
            makeAppNotRespondingLocked(app,
                    activity != null ? activity.shortComponentName : null,
                    annotation != null ? "ANR " + annotation : "ANR",
                    info.toString());
            // Bring up the infamous App Not Responding dialog
            Message msg = Message.obtain();
            HashMap<String, Object> map = new HashMap<String, Object>();
            msg.what = ActivityManagerService.SHOW_NOT_RESPONDING_UI_MSG;
            msg.obj = map;
            msg.arg1 = aboveSystem ? 1 : 0;
            map.put("app", app);
            if (activity != null) {
                map.put("activity", activity);
            }
            mService.mUiHandler.sendMessage(msg);
        }
    }
    

    这里主要就是ANR操作,主要内容为main log中ANR信息收集,执行dump操作,添加DropBox,弹窗提示ANR等。

    另外推荐一篇总结得非常好的文章:
    彻底理解安卓应用无响应机制

    相关文章

      网友评论

        本文标题:Android ANR(二)-触发原理

        本文链接:https://www.haomeiwen.com/subject/ludydctx.html