首页 > 其他分享 >Android ANR 实现机制详解

Android ANR 实现机制详解

时间:2023-06-22 11:07:33浏览次数:40  
标签:Log Service 进程 详解 ANR 堆栈 Android 超时


一 ANR 概述

ANR(Application Not Responding),即应用程序无响应,Android 系统指定某些事件需要在规定时间内完成,如果超过预定时间还能未能得到有效响应,就会造成 ANR。具体表现为,应用位于前台时,系统会向用户显示一个对话框,如下图所示。用户可以选择“wait”让程序继续运行,也可以选择“Close app”强制关闭。

Android ANR 实现机制详解_Android

二 ANR 触发场景

系统发生 ANR 时会在 system_server 进程调用 AppNotRespondingDialog.show() 方法,弹出对话框提示用户,对话框的调用链如下:

ProcessRecord.appNotResponding()
└── mService.mUiHandler.sendMessage(SHOW_NOT_RESPONDING_UI_MSG)
    └── AMS.UiHandler.handleMessage()
        └── AppErrors.handleShowAnrUi()
             └── AppNotRespondingDialog.show()

其中 AppErrors.appNotResponding() 方法是弹出 ANR 对话框的唯一入口,查看其调用关系如如下图:

Android ANR 实现机制详解_堆栈_02

总结有以下 4 种场景的超时会引起 ANR:

  1. Service Timeout: 组件 Service 执行超时
  2. BroadcastReceiverTimeout:组件 BroadcastReceiver 执行超时
  3. ContentProvider Timeout:组件 ContentProvider 执行超时
  4. InputDispatching Timeout: 按键或触摸事件在特定时间内无响应

以下是 Android 原生系统对不同类型的超时阈值设置,各手机厂商或芯片厂商也会对此值进行自行定制。

类型

TimeOut (sec)

Service

前台:20,后台:200

Broadcast

前台:10,后台:60

ContentProvider

10

InputDispatching

5

三 ANR 实现机制

3.1 ANR 触发流程

触发 ANR 的过程大致可分为三个步骤: 埋下炸弹 ->执行任务 ->引爆炸弹(或拆除炸弹)

其主体实现在系统层:

  1. 前文所述 4 种场景相关的事件都会经过系统进程(system_server) 调度,设置定时监控(即埋下炸弹)
  2. 然后,system_server 进程将任务派发到应用进程完成对消息的实际处理(执行任务)
  3. 最后,执行任务时间过长,在定时器超时前 system_server 还未收到任务完成的通知,触发 ANR(炸弹爆炸)

Android ANR 实现机制详解_android_03

在第 3 步中,如果任务在规定时间内执行完成,通知 system_server 进程移除监控,则炸弹被拆除,不会发生 ANR 异常。

Android ANR 实现机制详解_网络_04

3.2 Service超时机制源码解读

以 Service 超时为例,从源码来分析其超时机制引发 ANR 的流程。

Service Timeout 是位于 system_server 进程 -> ActivityManager 线程中 AMS.MainHandler 收到 SERVICE_TIMEOUT_MSG消息时触发。

对于 Service 有两类:

  • 对于前台服务,则超时为 SERVICE_TIMEOUT = 20s;
  • 对于后台服务,则超时为 SERVICE_BACKGROUND_TIMEOUT = 200s 由变量 ProcessRecord.execServicesFg来决定是否前台启动

Service ANR检测

Android 是通过设置定时消息实现的检测 Service 超时的。定时消息是由 AMS 的消息队列处理的(system_server 的 ActivityManager 线程)。AMS 有 Service 运行的上下文信息。

Service ANR 主体实现在 ActiveServices 中。 当 Service 的生命周期开始时,bumpServiceExecutingLocked() 会被调用,紧接着会调用 scheduleServiceTimeoutLocked()

frameworks/base/services/core/java/com/android/server/am/ActiveServices.java

void scheduleServiceTimeoutLocked(ProcessRecord proc) {
    if (proc.executingServices.size() == 0 || proc.thread == null) {
        return;
    }
    Message msg = mAm.mHandler.obtainMessage(
            ActivityManagerService.SERVICE_TIMEOUT_MSG);
    msg.obj = proc;
    mAm.mHandler.sendMessageDelayed(msg,
            proc.execServicesFg ? SERVICE_TIMEOUT : SERVICE_BACKGROUND_TIMEOUT);
}

当 Service 的生命周期结束时,会调用 serviceDoneExecutingLocked() 方法,之前抛出的 SERVICE_TIMEOUT_MSG 消息在这个方法中会被清除。 如果在超时时间内,SERVICE_TIMEOUT_MSG 没有被清除,那么,AMS.MainHandler 就会响应这个消息

frameworks/base/services/core/java/com/android/server/am/ActivityManagerService.java

case SERVICE_TIMEOUT_MSG: {
    mServices.serviceTimeout((ProcessRecord)msg.obj);
} break;

serviceTimeout 在 ActiveServices 启动打印信息

frameworks/base/services/core/java/com/android/server/am/ActiveServicesjava

if (timeout != null && mAm.mProcessList.mLruProcesses.contains(proc)) {
    Slog.w(TAG, "Timeout executing service: " + timeout);
    StringWriter sw = new StringWriter();
    PrintWriter pw = new FastPrintWriter(sw, false, 1024);
    pw.println(timeout);
    timeout.dump(pw, "    ");
    pw.close();
    mLastAnrDump = sw.toString();
    mAm.mHandler.removeCallbacks(mLastAnrDumpClearer);
    mAm.mHandler.postDelayed(mLastAnrDumpClearer, LAST_ANR_LIFETIME_DURATION_MSECS);
    anrMessage = "executing service " + timeout.shortInstanceName;
}

// 打印 ANR 信息
if (anrMessage != null) {
    mAm.mAnrHelper.appNotResponding(proc, anrMessage);
}

通过 ANRHelper 打印 ANR 信息

frameworks/base/services/core/java/com/androidserver/am/AnrHelper.java

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();
}

Service 的 ANR机制总结 通过定时消息跟踪 Service 的运行,当定时消息被响应时,说明 Service 还没有运行完成,这就意味着 Service ANR。

四 ANR 问题分析流程

ANR 问题主要有两种原因:应用自身的问题系统异常导致的问题。在分析 ANR 问题的时候,最主要的就是要确定是哪个原因导致的

ANR 问题一般的分析步骤如下:

  1. 分析 trace.txt:查看是否有明显的异常,比如死锁、SystemServer 异常持锁等
  1. 死锁堆栈: 观察 Trace 堆栈,确认是否有明显问题,如主线程是否与其他线程发生死锁,如果是进程内部发生了死锁,只需找到与当前线程死锁的线程,问题即可解决
  2. 业务堆栈: 观察 Trace 堆栈,发现当前主线程堆栈正在执行业务逻辑,这时候需要分析对应的代码,查看是否真的有问题
  1. 重要:如果业务方觉得这里没有问题,需要进一步分析,因为 Trace 堆栈可能并不是真正耗时的地方,需要结合其他信息一起分析
  1. IPC Block 堆栈: 观察通过 Trace 堆栈,发现主线程堆栈是在跨进程(Binder)通信,这时候可以根据其他 Log、Binder Info 等信息,来分析 IPC 信息
  1. 大部分 IPC 都是在跟 SystemServer,如果没有 BinderInfo,可以搜索对应的接口关键字,在 SystemServer 进程查找是否有相关的堆栈
  2. 重要:如果业务方觉得这里没有问题,需要进一步分析,因为 Trace 堆栈可能并不是真正耗时的地方,需要结合其他信息一起分析
  1. 系统堆栈: 通过观察 Trace,发现当前堆栈只是简单的系统堆栈,比如 NativePollOnce,想要搞清楚是否发生严重耗时,以及进一步的分析定位
  1. 重要:大部分比较难分析的 ANR 基本上应用主线程堆栈都是 NativePollOnce 这个状态,之所以出现这种状态,可能有下面几个原因
  1. 确实没有消息在处理,可能进程被冻结,或者 No Focused Window 这种 ANR
  2. 刚好处理完某一个耗时消息,系统抓堆栈的时候,已经晚了,耗时的状态没有抓到
  3. 线程调度的原因,主线程没有机会执行
  1. 分析 Event Log:看具体的 ANR 时间(搜索 am_anr),看看是否跟 ANR log 能够对上,以确定 ANR Log 是否有效,如果 ANR Log 有效,分析 ANR Log,提取有用信息:pid、tid、死锁等,遇到 ANR 问题,摆在我们面前的 trace 是不是第一案发现场,如果 ANR 发生的输出的信息很多,当时的 CPU 和 I/O 资源比较紧张,那么这段日志输出的时间点可能会延迟 10 秒到 20 秒都有可能,所以我们有时候需要提高警惕。不过正常情况下,EventLog 中的 am_anr 的输出时间是最早的,也是最接近 ANR 时间的 (提取有效信息到单独文件中)
  2. 分析 Android Log:看 MainLog(Android Log) 或者 SystemLog 查看 ANR 详细信息(搜索 ANR in),提取有效的信息 (提取有效信息到单独文件中)
  1. 发生 ANR 的时间
  2. 打印 ANR 日志的进程
  3. 发生 ANR 的进程
  4. 发生 ANR 的原因
  5. CPU 负载
  6. Memory 负载
  7. CPU 使用统计时间段
  8. 各进程的 CPU 使用率
  1. 总的 CPU 使用率
  2. 缺页次数 fault
  1. xxx minor 表示高速缓存中的缺页次数,可以理解为进程在做内存访问
  2. xxx major 表示内存的缺页次数,可以理解为进程在做 IO 操作
  1. CPU 使用汇总
  1. 配合 Main Log(Android Log) 和 EventLog 把 CPU 开始和结束的时间点内的所有有用信息提取出来到一个 文件中,搜索的主要关键字:pid,进程名,WindowManager、ActivityManager(关键字参考下一节的关键 Log 那里)
  1. 收集关键操作场景,比如解锁、安装应用、亮灭屏、应用启动等
  2. 收集异常和系统关键 Log
  1. 系统变慢 :比如 Slow operation、Slow dispatch、Slow delivery、dvm_lock_sample、binder_sample
  2. 进程变化 :am_kill、am_proc_died、lowmemorykiller、ANR、应用启动关系等
  3. 系统信息 :cpu info、meminfo、binder info(是否满了) 、iowait (是否过高)
  4. 消息监控:ANR 前的 ANR Message 打印,Block Message 信息,应用自己代码执行逻辑推断出的 Message 耗时等
  1. 收集 ANR 进程的所有关键线程的运行情况、线程优先级等
  2. 根据第四步提取出来的关键信息文件,进一步理出系统当时的情况、状态(推荐 vscode 或者 notepad ++ ,有 线索就全局搜索)),比如
  1. 是处于低内存频繁杀进程?
  2. 重启第一次解锁系统繁忙
  3. 还是短时间内多个应用启动系统繁忙
  4. 还是应用自己的逻辑等待?
  1. 针对不同的 ANR 类型,提取不同的信息
  1. 不行就加 Log 复现

作者:话唠扇贝


标签:Log,Service,进程,详解,ANR,堆栈,Android,超时
From: https://blog.51cto.com/u_16163453/6534402

相关文章

  • 史上最全Android性能优化方案解析
    Android中的性能优分为以下几个方面:布局优化网络优化安装包优化内存优化卡顿优化启动优化……一.布局优化布局优化的本质就是减少View的层级。常见的布局优化方案如下:在LinearLayout和RelativeLayout都可以完成布局的情况下优先选择LinearLayout,可以减少View的层级,但是注意相同组......
  • Android App运行核心,Handler,Looper,Message
    目标在手机屏幕上显示指定的区域两种方法在已存在的图片上,绘制矩形,查看图片在全屏透明悬浮窗上直接画矩形已有图片绘制矩形读取图片letimgFilepath=files.path("./chess.png");letimg=images.read(imgFilepath);设置绘制区域letrect={left:52,top:20,rig......
  • android RecyclerView嵌套 RecyclerView 子item 和 父item点击事件如何处理
    前言经常会遇到列表嵌套列表的场景,那么父item和子item会有点击重叠该怎么处理尼?先上效果父adapteropenclassStoreListAdapter(layoutResId:Int,data:MutableList<StoreGoodsBean>):BaseQuickAdapter<StoreGoodsBean,BaseViewHolder>(layoutResId,data){//子adapt......
  • Android开发必备——注解
    前言阅读官方源码以及各类第三方框架时可以发现,很多地方都有注解,作为一名Android程序员,掌握注解属于必不可少的一项技能。1.什么是注解注解是以@符号开头的用来标识如类、字段、方法等的工具。说到注解,就不得不提另外一个概念——注释,两者其实都是做解释的功能,只不过注释是面向开......
  • Android 屏幕适配基础
    Pixels和dp、sp的区别不同屏幕密度下,1p显示的物理长度不同1dp在不同屏幕上显示相同的物理长度sp只用在字体上,和dp一样为了让在不同设备上有一致的显示效果单位尺寸搞清楚屏幕的各种单位含义,是屏幕适配的基础屏幕尺寸含义:手机对角线的物理尺寸单位:英寸(inch),1英寸=2.54cm屏幕尺寸......
  • Android 多任务配置
    Android多任务配置本篇文章是为了了解安卓中的多任务系统,了解安卓中Task的栈结构,以及怎么配置app的多任务,模仿微信小程序。多任务配置在安卓手机上,当我们打开微信小程序,可以看到小程序其实是一个独立的任务,这是怎么配置的呢。其实很简单,我们只需要在安卓项目的配置文件中,对Activit......
  • 好家伙66万字,又一份牛逼的Android笔记面世了
    前言很久以前,凭借四大组件、Java基础等知识,便可开开心心的开发,轻松地上岗。而随着Android的不断发展完善,各种组件库越来越成熟,学习资料越来越多,我们却慢慢地看不到方向。信息爆炸的时代,到底该如何提升自己?如何才能找到真正利己的信息?如何才能看清前路的方向?为此我专门找了一份《And......
  • 四年Android开发,在拉勾上投了十几个简历,没有一个面试邀请......药丸了
    在浏览某论坛的时候看到一名程序员吐槽:坐标杭州,四年Android开发一枚,技术不顶尖也不算差吧,这边加班太猛了,在考虑换一个岗位。在拉勾上投了十几个简历,全都是不合适,没有一个面试邀请!!!简历在拉勾上是开放的,竟然没有一个感兴趣的公司打电话给我。前年这个时候,接到的电话还是很多的,这才过......
  • Android视图加载优化——Factory2设置方法
    前言Factory2是直接继承于Factory,继续跟踪下Factory的源码,比Factory的功能更加强大。当我们新建Activity的时候,大部分情况是继承AppCompatActivity。提供了向后兼容性。本文将深入探索AppCompatActivity的视图加载,探索将xml布局文件中的TextView替换成AppCompatTextVi......
  • 6轮面试辛苦拿到阿里Android开发offer,却从22k降到15k,在逗我?
    一小伙工作快3年了,拿到了阿里云Android开发岗位P6的offer,算HR面一起,加起来有6轮面试了,将近3个月的时间,1轮同级+1轮Android用人部门leader+1轮Android组leader+1轮项目CTO+1轮HR+1轮HRBP。一路上各种事件分发机制、自定义View、handler原理、多线程、hashmap、手写算法、......