ANR源码分析之InputDispatcher Timeout

  在上篇文章中,介绍ANR产生的原因、ANR的分类以及ANR问题的分析。本篇文章接下来将从源码的角度来分析ANR产生的过程,首先介绍InputDispatcher Timeout产生的过程。在ANR产生时,最终都会调用到appNotResponding()方法,该方法在Android 7.0以前定义在ActivityManagerService.java类中,在Android 7.0中定义在AppErrors.java类中,本文将以Android 7.0源码来分析ANR的产生过程。首先来分析appNotResponding()方法,在该方法中将记录一些ANR的信息到event、system、trace日志文件中,并发送应用未响应Dialog给系统显示。ANR日志分析将在下一个小节中描述,appNotResponding()在frameworks/base/services/core/java/com/android/server/am/AppErrors.java文件中实现:

/*
* 该函数的主要作用是记录发生ANR的信息到日志文件中,包括event日志、system日志以及trace日志中,
* 同时在应用显示一个ANR Dialog通知应用发生了ANR
*/
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);
         ...........
        //记录发生ANR的时间
        long anrTime = SystemClock.uptimeMillis();
        if (ActivityManagerService.MONITOR_CPU_USAGE) {
            //第一次更新CPU的状态
            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;
        //在system日志中,打印ANR发生的原因
        synchronized (mService) {
            .........
            app.notResponding = true;

            //1.记录ANR日志到event日志中
            EventLog.writeEvent(EventLogTags.AM_ANR, app.userId, app.pid,
                    app.processName, app.info.flags, annotation);

            // 将应用pid添加到firstPids集合中
            firstPids.add(app.pid);

            isSilentANR = !showBackground && !app.isInterestingToUserLocked() && app.pid != MY_PID;
            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);//添加父进程的pid到firstPids集合中

                if (MY_PID != app.pid && MY_PID != parentPid) firstPids.add(MY_PID);//再将当前应用的pid添加到firstPids集合中

                //将最近使用的进程pid添加到firstPids和lastPids集合中
                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 {
                                lastPids.put(pid, Boolean.TRUE);
                                if (DEBUG_ANR) Slog.i(TAG, "Adding ANR proc: " + r);
                            }
                        }
                    }
                }
            }
        }


        // 2.记录ANR信息到system日志中
        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);

        String[] nativeProcs = NATIVE_STACKS_OF_INTEREST;
        // 3.记录ANR信息到trace日志文件中
        File tracesFile = null;
        if (isSilentANR) {
            tracesFile = mService.dumpStackTraces(true, firstPids, null, lastPids,
                null);
        } else {
            //调用AMS的dumpStackTraces记录ANR日志到trace文件中
            tracesFile = mService.dumpStackTraces(true, firstPids, processCpuTracker, lastPids,
                nativeProcs);
        }

        String cpuInfo = null;
        if (ActivityManagerService.MONITOR_CPU_USAGE) {
            //第二次更新CPU的状态
            mService.updateCpuStatsNow();
            synchronized (mService.mProcessCpuTracker) {
                //记录第一次CPU的信息
                cpuInfo = mService.mProcessCpuTracker.printCurrentState(anrTime);
            }

            info.append(processCpuTracker.printCurrentLoad());
            info.append(cpuInfo);
        }
        //记录第二次CPU的信息
        info.append(processCpuTracker.printCurrentState(anrTime));

        //记录ANR信息到system日志中
        Slog.e(TAG, info.toString());
        if (tracesFile == null) {
            // There is no trace file, so dump (only) the alleged culprit's threads to the log
            
  • 1
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
ANRApplication Not Responding)错误是指应用程序在主线程上执行了太多的工作,导致界面无法响应用户的事件,从而出现了“应用程序无响应”的错误提示。在这种情况下,如果您的应用程序正在执行服务,那么该错误可能是由于服务执行超时导致的。 以下是解决这个问题的一些方法: 1. 使用IntentService: IntentService 是一种专门用于执行异步任务的服务。它可以在后台执行任务,并在完成任务后自动停止。使用 IntentService 可以避免在主线程上执行任务,从而避免 ANR 错误。 2. 使用线程池:如果您的应用程序需要在服务中执行大量的异步任务,可以使用线程池来管理这些任务。线程池可以在后台执行任务,并控制任务的数量和执行顺序,从而避免在主线程上执行过多的工作。 3. 使用HandlerThread:HandlerThread 是一种特殊的线程,它可以接收消息并在消息队列中处理这些消息。使用 HandlerThread 可以在后台执行任务,并避免在主线程上执行过多的工作。 4. 使用JobScheduler:JobScheduler 是一种 Android 系统提供的调度服务,它可以在特定的时间间隔或特定的条件下执行任务。使用 JobScheduler 可以避免在主线程上执行任务,并在系统空闲时执行任务,从而避免 ANR 错误。 请注意,如果您的应用程序需要执行长时间运行的任务,最好将这些任务移到后台线程中执行,以避免 ANR 错误。同时,还应该尽可能地优化代码,避免在主线程上执行过多的工作。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值