当前位置: 首页 > article >正文

Android12的ANR解析

0. 参考:

ANR分析
深入理解 Android ANR 触发原理以及信息收集过程

1.ANR的触发分类:

ANR分为4类:

  • InputDispatchTimeout:输入事件分发超时5s,包括按键和触摸事件。
  • BroadcastTimeout:比如前台广播在10s内未执行完成,后台60s
  • ServiceTimeout:前台服务在20s内未执行完成,后台服务未在200s内完成。
  • ContentProviderTimeout:contenProvider在publish后超时10s

2.ANR的触发机制

2.1基本概念

  • ANR 的检测和处理逻辑主要写在 AMS(Activity Manager Service) 进程中
  • 当超时事件触发后,AMS 会记录下应用主线程的状态,并调用 ActivityManagerService.appNotResponding() 方法来启动 ANR 处理流程。
  • 当应用程序在UI线程阻塞太长时间,就会弹出系统弹框,询问我们继续等待还是关闭应用程序,此时发生了ANR。

3.Service,BroadCast,Provider触发ANR:

3.1 ANR是在发生这些调用的时候,就启用"定时炸弹"。

  • 1.APP侧:
    在Activity中调用startService后,调用链:ContextImpl.startService()->ContextImpl.startServiceCommon()
    1. AMS侧:
      ActivityManagerService.startService()
      ->ActiveServices.startServiceLocked()
      ->ActiveServices.startServiceInnerLocked()
      ->ActiveServices.bringUpServiceLocked()
      ->ActivieServices.realStartServiceLocked()
      ->ActivieServices.realStartServiceLocked
  • 2.1 其中realStartServiceLocked主要通过向AMS发送一个Handler的延迟消息告诉AMS这个调用超时了。
    private void realStartServiceLocked(ServiceRecord r, ProcessRecord app,
            IApplicationThread thread, int pid, UidRecord uidRecord, boolean execInFg,
            boolean enqueueOomAdj) throws RemoteException {  // 2
...............
        // 记录此调用的开始---原因是 "create"
        // 从此处开始倒计时--发送handler消息到AMS
        bumpServiceExecutingLocked(r, execInFg, "create", null /* oomAdjReason */);
................
            // 通知APP启动Service,执行 handleCreateService
            // 调用到 ApplicationThread.scheduleCreateService
            thread.scheduleCreateService(r, r.serviceInfo,
                    mAm.compatibilityInfoForPackage(r.serviceInfo.applicationInfo),
                    app.mState.getReportedProcState());
..........
  • 2.2 ActivieServices.bumpServiceExecutingLocked---->ActivieServices.scheduleServiceTimeoutLocked
    这个延迟时间就是1中说到的超时20s, 对应ActiveService的SERVICE_TIMEOUT变量;
    void scheduleServiceTimeoutLocked(ProcessRecord proc) {  // 2
...................
        Message msg = mAm.mHandler.obtainMessage(
                ActivityManagerService.SERVICE_TIMEOUT_MSG);
        msg.obj = proc;
        mAm.mHandler.sendMessageDelayed(msg, proc.mServices.shouldExecServicesFg()
                ? SERVICE_TIMEOUT : SERVICE_BACKGROUND_TIMEOUT);  // 发送延迟消息给AMS的Handler
    }
  • 2.3 scheduleCreateService
    会通过Binder调用回APP侧,最终会调用到ApplicationThread.scheduleCreateService
        public final void scheduleCreateService(IBinder token,
                ServiceInfo info, CompatibilityInfo compatInfo, int processState) {
            updateProcessState(processState, false);
            CreateServiceData s = new CreateServiceData();
            s.token = token;
            s.info = info;
            s.compatInfo = compatInfo;

            sendMessage(H.CREATE_SERVICE, s);  // 发送Handler消息
        }

// 找到处理Handler消息的地方:handleCreateService最终会调用到这个Service的onCreate方法上。
                case CREATE_SERVICE:
                    if (Trace.isTagEnabled(Trace.TRACE_TAG_ACTIVITY_MANAGER)) {
                        Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER,
                                ("serviceCreate: " + String.valueOf(msg.obj)));
                    }
                    handleCreateService((CreateServiceData)msg.obj);  // 执行Service的回调onCreate
                    Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
                    break;
  • 3 小结
    1)最开始调用了startService来触发AMS发送一个延迟为SERVICE_TIMEOUT(20s)的Handler事件,
    2)并最终回调到实现了这个Service的onCreate函数。
    3)如果超过了20s这个Handler消息还是没被取消,那么就会去触发AMS的ANR机制。

3.2 取消延迟发送的Handler消息, “取消炸弹”

  • 在上面描述的正常流程中,会调用到handleCreateService,最终会调用removeMessages取消3.1中的延迟发送的Handler消息
    private void handleCreateService(CreateServiceData data) { 
.....
        service.onCreate();   // 进入service的onCreate回调
....
        // Service启动完成,需要通知AMS
        ActivityManager.getService().serviceDoneExecuting(
                data.token, SERVICE_DONE_EXECUTING_ANON, 0, 0);
.....
    }

    public void serviceDoneExecuting(IBinder token, int type, int startId, int res) {
        synchronized(this) {
            if (!(token instanceof ServiceRecord)) {
                Slog.e(TAG, "serviceDoneExecuting: Invalid service token=" + token);
                throw new IllegalArgumentException("Invalid service token");
            }
            // 执行到ActivityManagerService的 serviceDoneExecuting方法
            mServices.serviceDoneExecutingLocked((ServiceRecord) token, type, startId, res, false);
        }
    }

// ActivityManagerService的 serviceDoneExecuting方法
    private void serviceDoneExecutingLocked(ServiceRecord r, boolean inDestroying,
            boolean finishing, boolean enqueueOomAdj) {
......
					// 将Handler消息取消
                    mAm.mHandler.removeMessages(ActivityManagerService.SERVICE_TIMEOUT_MSG, r.app);
..........
        }

3.3 ANR的触发, “炸弹引爆”

  • 就是延迟发送的Handler消息处理,
    在ActivityManagerService.java可看到处理SERVICE_TIMEOUT_MSG的代码
            case SERVICE_TIMEOUT_MSG: {
               // 调用到ActiveServices的serviceTimeout方法
                mServices.serviceTimeout((ProcessRecord) msg.obj);
            } break;

//com.android.server.am.ActiveServices.java
    void serviceTimeout(ProcessRecord proc) {
       ................
       // 计算是否超时,

        if (anrMessage != null) {  // 超时
        // 触发超时机制,mAm是AMS,mAnrHelper是AnrHelper
            mAm.mAnrHelper.appNotResponding(proc, anrMessage);  
        }
    }
  • 调用到AnrHelper的appNotResponding方法,最终是调用到AmrConsumerThread().start方法来处理ANR
    void appNotResponding(ProcessRecord anrProcess, String annotation) {
        appNotResponding(anrProcess, null /* activityShortComponentName */, null /* aInfo */,
                null /* parentShortComponentName */, null /* parentProcess */,
                false /* aboveSystem */, annotation);
    }

    void appNotResponding(ProcessRecord anrProcess, String activityShortComponentName,
            ApplicationInfo aInfo, String parentShortComponentName,
            WindowProcessController parentProcess, boolean aboveSystem, String annotation) {
        synchronized (mAnrRecords) {
            mAnrRecords.add(new AnrRecord(anrProcess, activityShortComponentName, aInfo,
                    parentShortComponentName, parentProcess, aboveSystem, annotation));
        }
        startAnrConsumerIfNeeded();
    }

    private void startAnrConsumerIfNeeded() {
        if (mRunning.compareAndSet(false, true)) {
            new AnrConsumerThread().start();  // 启动AnrConsumerThread线程处理ANR
        }
    }
private class AnrConsumerThread extends Thread {
    @Override
    public void run() {
        AnrRecord r;
        while ((r = next()) != null) {
            ......
            //这里的r就是AnrRecord
            r.appNotResponding(onlyDumpSelf);
            ......
        }
    }
}
private static class AnrRecord {
    void appNotResponding(boolean onlyDumpSelf) {
        //mApp是ProcessRecord,mErrorState是ProcessErrorStateRecord
            mApp.mErrorState.appNotResponding(mActivityShortComponentName, mAppInfo,
                    mParentShortComponentName, mParentProcess, mAboveSystem, mAnnotation,
                    onlyDumpSelf);
    }
}

调用到ProcessErrorStateRecord.appNotResponding来弹出无响应弹出。或者dump操作啥的,就是平时见到的ANR

3.4 ANR做了哪些事情

  • 主要就是记录ANR错误的相关信息 + 弹出ANR的弹窗
  • 最终会调用到ProcessErrorStateRecord的appNotResponding,下面来研究ProcessErrorStateRecord.java的这个函数
    void appNotResponding(String activityShortComponentName, ApplicationInfo aInfo,
            String parentShortComponentName, WindowProcessController parentProcess,
            boolean aboveSystem, String annotation, boolean onlyDumpSelf) {  // 
........

// 将ANR的相关信息记录到anr文件中
        StringBuilder info = new StringBuilder();
.....
        StringBuilder report = new StringBuilder();


....................

// 弹出ANR的Dialog
            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(mApp, aInfo, aboveSystem);
                mService.mUiHandler.sendMessageDelayed(msg, anrDialogDelayMs);
            }
        }
    }


  • 小结:
    1)在调用得时候发送了延迟HANDLER消息:SERVICE_TIMEOUT_MSG
    2)到了延时时间,还未remove掉此handler消息,那么就会触发SERVICE_TIMEOUT_MSG的处理。
    3)最终调用到AnrHelper的appNotResponding来处理ANR

4.Input触发ANR:

4.1 从linux侧获取input事件

  • 如inputFlinger 讲解的内容,InputReader线程通过EventHub监听/dev/input读取输入事件,监听到事件则把消息传到InputDispatcher(即InputReader把消息写入mInBoundQueue队列)
  • InputDispatcher负责把输入事件分发给 目标应用窗口。

4.2 ANR相关

  • InputDispatcher的流程:
    input的ANR流程

4.3 ANR触发 代码流程梳理

  • InputDispatcher中的方法调用:
    1.有等待获取焦点的应用:当前时间超过Timeout,调用processNoFocusedWindowAnrLocked() 进一步确认
    2.存在window:当前时间超过事件响应的超时时间。调用onAnrLocked() 进一步确认。
> frameworks/native/services/inputflinger/dispatcher/InputDispatcher.cpp
  
// 检查是否有任何连接的等待队列具有太旧的事件。如果我们等待事件被确认的时间超过窗口超时,
// 请引发 ANR。返回我们下次应该醒来的时间。
nsecs_t InputDispatcher::processAnrsLocked() {
    const nsecs_t currentTime = now();
    nsecs_t nextAnrCheck = LONG_LONG_MAX; // 下一次检查anr的时间
    // 检查我们是否正在等待一个聚焦窗口出现。如果等待时间过长就报 ANR
    if (mNoFocusedWindowTimeoutTime.has_value() && mAwaitedFocusedApplication != nullptr) {
        if (currentTime >= *mNoFocusedWindowTimeoutTime) {
            // 场景1: 触发noFocusedWindow的anr
            processNoFocusedWindowAnrLocked();
            mAwaitedFocusedApplication.reset();
            mNoFocusedWindowTimeoutTime = std::nullopt;
            return LONG_LONG_MIN;
        } else {
            // 请继续等待。我们将在mNoFocusedWindowTimeoutTime到来时放弃该事件。
            nextAnrCheck = *mNoFocusedWindowTimeoutTime;
        }
    }
    // 检查是否有任何连接 ANR 到期,mAnrTracker 中保存所有已分发事件(未被确认消费的事件)的超时时间
    nextAnrCheck = std::min(nextAnrCheck, mAnrTracker.firstTimeout());
    if (currentTime < nextAnrCheck) { // 最有可能的情况
        // 一切正常,在 nextAnrCheck 再检查一次
        return nextAnrCheck;
    }
    // 如果我们到达这里,则连接无响应。
    sp<Connection> connection = getConnectionLocked(mAnrTracker.firstToken());
    // 停止为此无响应的连接唤醒
    mAnrTracker.eraseToken(connection->inputChannel->getConnectionToken());
    // 场景2: 触发ANR
    onAnrLocked(connection);
    return LONG_LONG_MIN;
}

4.4 ANR在InputDispatcher的调用栈

dispatchOnce findFocusedWindowTargetsLocked processAnrsLocked processNoFocusedWindowAnrLocked onAnrLocked postCommandLocked updateLastAnrStateLocked processConnectionUnresponsiveLocked sendMonitorUnresponsiveCommandLocked sendWindowUnresponsiveCommandLocked runCommandsLockedInterruptible command 赋值mNoFocusedWindowTimeoutTime return 触发ANR检查 case 1: 失去焦点时间(mNoFocusedWindowTimeoutTime)过长 onAnrLocked(application) 向mCommandQueue中写入数据 return case 2: 连接过期,触发ANR, onAnrLocked(connection) onAnrLocked(connection) return 记录ANR相关 case 1 case 2 return 捕获 ANR 时 InputDispatcher 状态的记录。 处理postCommandLocked写入的 mCommandQueue 调用command处理 dispatchOnce findFocusedWindowTargetsLocked processAnrsLocked processNoFocusedWindowAnrLocked onAnrLocked postCommandLocked updateLastAnrStateLocked processConnectionUnresponsiveLocked sendMonitorUnresponsiveCommandLocked sendWindowUnresponsiveCommandLocked runCommandsLockedInterruptible command

6.排查思路:

  1. adb shell getevent后,点击屏幕,如果有打印,代表驱动是可以拿到点击事件的。如果没有则需要内核的同事查看。如果有则代表是Android的问题。


http://www.kler.cn/a/394334.html

相关文章:

  • 项目风险管理的3大要素
  • 微信小程序的主体文件和页面文件介绍
  • git配置远程仓库的认证信息
  • 844.比较含退格的字符串
  • 【计算机网络】【传输层】【习题】
  • 【算法一周目】双指针(2)
  • 防爆增安型电机与防爆无火花型电机的区别
  • Agent熔断:助力构建更健壮的IT监控系统
  • 【代码随想录】刷题记录(29)-用栈实现队列
  • Web性能优化:从基础到高级
  • 引入了JUnit框架 却报错找不到:java.lang.ClassNotFoundException
  • 爬虫如何解决短效代理被封的问题?
  • 基于Spring Boot的电子商务系统设计
  • 海外媒体发稿:聚焦摩洛哥世界新闻 Morocco World News
  • 数字图像处理(c++ opencv):图像复原与重建-常见的滤波方法--统计排序滤波器
  • 机器学习—模型选择和训练交叉验证测试集
  • 鸿蒙HarmonyOS 网络请求获取数据Http
  • 2024-11-12 问AI: [AI面试题] 您将如何设计一个人工智能系统来预测电信公司的客户流失?
  • SpringBoot-自定义注解,拦截器
  • Prometheus面试内容整理-Exporters
  • docker之容器设置开机自启(4)
  • 力扣 LeetCode 242. 有效的字母异位词(Day3:哈希表)
  • 天云数据联手举办“科学传播沙龙”活动,探讨Sora是否会带来新的科学革命
  • 镭速大文件传输软件向金融银行的文档管理提供高效的解决方案
  • Whalestudio助力西南某商业银行数据中台建设 | 实践探索
  • Vue3.js - 一文看懂Vuex