Android ANR 实现机制详解
Posted 嘴巴吃糖了
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Android ANR 实现机制详解相关的知识,希望对你有一定的参考价值。
一 ANR 概述
ANR(Application Not Responding),即应用程序无响应,android 系统指定某些事件需要在规定时间内完成,如果超过预定时间还能未能得到有效响应,就会造成 ANR。具体表现为,应用位于前台时,系统会向用户显示一个对话框,如下图所示。用户可以选择“wait”让程序继续运行,也可以选择“Close app”强制关闭。
二 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 对话框的唯一入口,查看其调用关系如如下图:
总结有以下 4 种场景的超时会引起 ANR:
-
Service Timeout: 组件 Service 执行超时
-
BroadcastReceiverTimeout:组件 BroadcastReceiver 执行超时
-
ContentProvider Timeout:组件 ContentProvider 执行超时
-
InputDispatching Timeout: 按键或触摸事件在特定时间内无响应
以下是 Android 原生系统对不同类型的超时阈值设置,各手机厂商或芯片厂商也会对此值进行自行定制。
类型 | TimeOut (sec) |
---|---|
Service | 前台:20,后台:200 |
Broadcast | 前台:10,后台:60 |
ContentProvider | 10 |
InputDispatching | 5 |
三 ANR 实现机制
3.1 ANR 触发流程
触发 ANR 的过程大致可分为三个步骤: 埋下炸弹 ->执行任务 ->引爆炸弹(或拆除炸弹)
其主体实现在系统层:
- 前文所述 4 种场景相关的事件都会经过系统进程(system_server) 调度,设置定时监控(即埋下炸弹)
- 然后,system_server 进程将任务派发到应用进程完成对消息的实际处理(执行任务)
- 最后,执行任务时间过长,在定时器超时前 system_server 还未收到任务完成的通知,触发 ANR(炸弹爆炸)
在第 3 步中,如果任务在规定时间内执行完成,通知 system_server 进程移除监控,则炸弹被拆除,不会发生 ANR 异常。
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 问题一般的分析步骤如下:
- 分析 trace.txt:查看是否有明显的异常,比如死锁、SystemServer 异常持锁等
- 死锁堆栈: 观察 Trace 堆栈,确认是否有明显问题,如主线程是否与其他线程发生死锁,如果是进程内部发生了死锁,只需找到与当前线程死锁的线程,问题即可解决
- 业务堆栈: 观察 Trace 堆栈,发现当前主线程堆栈正在执行业务逻辑,这时候需要分析对应的代码,查看是否真的有问题
- 重要:如果业务方觉得这里没有问题,需要进一步分析,因为 Trace 堆栈可能并不是真正耗时的地方,需要结合其他信息一起分析
- IPC Block 堆栈: 观察通过 Trace 堆栈,发现主线程堆栈是在跨进程(Binder)通信,这时候可以根据其他 Log、Binder Info 等信息,来分析 IPC 信息
- 大部分 IPC 都是在跟 SystemServer,如果没有 BinderInfo,可以搜索对应的接口关键字,在 SystemServer 进程查找是否有相关的堆栈
- 重要:如果业务方觉得这里没有问题,需要进一步分析,因为 Trace 堆栈可能并不是真正耗时的地方,需要结合其他信息一起分析
- 系统堆栈: 通过观察 Trace,发现当前堆栈只是简单的系统堆栈,比如 NativePollOnce,想要搞清楚是否发生严重耗时,以及进一步的分析定位
- 重要:大部分比较难分析的 ANR 基本上应用主线程堆栈都是 NativePollOnce 这个状态,之所以出现这种状态,可能有下面几个原因
- 确实没有消息在处理,可能进程被冻结,或者 No Focused Window 这种 ANR
- 刚好处理完某一个耗时消息,系统抓堆栈的时候,已经晚了,耗时的状态没有抓到
- 线程调度的原因,主线程没有机会执行
- 重要:大部分比较难分析的 ANR 基本上应用主线程堆栈都是 NativePollOnce 这个状态,之所以出现这种状态,可能有下面几个原因
- 分析 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 时间的 (提取有效信息到单独文件中)
- 分析 Android Log:看 MainLog(Android Log) 或者 SystemLog 查看 ANR 详细信息(搜索 ANR in),提取有效的信息 (提取有效信息到单独文件中)
- 发生 ANR 的时间
- 打印 ANR 日志的进程
- 发生 ANR 的进程
- 发生 ANR 的原因
- CPU 负载
- Memory 负载
- CPU 使用统计时间段
- 各进程的 CPU 使用率
- 总的 CPU 使用率
- 缺页次数 fault
- xxx minor 表示高速缓存中的缺页次数,可以理解为进程在做内存访问
- xxx major 表示内存的缺页次数,可以理解为进程在做 IO 操作
- CPU 使用汇总
- 配合 Main Log(Android Log) 和 EventLog 把 CPU 开始和结束的时间点内的所有有用信息提取出来到一个 文件中,搜索的主要关键字:pid,进程名,WindowManager、ActivityManager(关键字参考下一节的关键 Log 那里)
- 收集关键操作场景,比如解锁、安装应用、亮灭屏、应用启动等
- 收集异常和系统关键 Log
- 系统变慢 :比如 Slow operation、Slow dispatch、Slow delivery、dvm_lock_sample、binder_sample
- 进程变化 :am_kill、am_proc_died、lowmemorykiller、ANR、应用启动关系等
- 系统信息 :cpu info、meminfo、binder info(是否满了) 、iowait (是否过高)
- 消息监控:ANR 前的 ANR Message 打印,Block Message 信息,应用自己代码执行逻辑推断出的 Message 耗时等
- 收集 ANR 进程的所有关键线程的运行情况、线程优先级等
- 根据第四步提取出来的关键信息文件,进一步理出系统当时的情况、状态(推荐 vscode 或者 notepad ++ ,有 线索就全局搜索)),比如
- 是处于低内存频繁杀进程?
- 重启第一次解锁系统繁忙
- 还是短时间内多个应用启动系统繁忙
- 还是应用自己的逻辑等待?
- 针对不同的 ANR 类型,提取不同的信息
- 不行就加 Log 复现
作者:话唠扇贝
链接:https://juejin.cn/post/7128397546099048478
最后
如果想要成为架构师或想突破20~30K薪资范畴,那就不要局限在编码,业务,要会选型、扩展,提升编程思维。此外,良好的职业规划也很重要,学习的习惯很重要,但是最重要的还是要能持之以恒,任何不能坚持落实的计划都是空谈。
如果你没有方向,这里给大家分享一套由阿里高级架构师编写的《Android八大模块进阶笔记》,帮大家将杂乱、零散、碎片化的知识进行体系化的整理,让大家系统而高效地掌握Android开发的各个知识点。
相对于我们平时看的碎片化内容,这份笔记的知识点更系统化,更容易理解和记忆,是严格按照知识体系编排的。
一、架构师筑基必备技能
1、深入理解Java泛型
2、注解深入浅出
3、并发编程
4、数据传输与序列化
5、Java虚拟机原理
6、高效IO
……
二、Android百大框架源码解析
1.Retrofit 2.0源码解析
2.Okhttp3源码解析
3.ButterKnife源码解析
4.MPAndroidChart 源码解析
5.Glide源码解析
6.Leakcanary 源码解析
7.Universal-lmage-Loader源码解析
8.EventBus 3.0源码解析
9.zxing源码分析
10.Picasso源码解析
11.LottieAndroid使用详解及源码解析
12.Fresco 源码分析——图片加载流程
三、Android性能优化实战解析
- 腾讯Bugly:对字符串匹配算法的一点理解
- 爱奇艺:安卓APP崩溃捕获方案——xCrash
- 字节跳动:深入理解Gradle框架之一:Plugin, Extension, buildSrc
- 百度APP技术:Android H5首屏优化实践
- 支付宝客户端架构解析:Android 客户端启动速度优化之「垃圾回收」
- 携程:从智行 Android 项目看组件化架构实践
- 网易新闻构建优化:如何让你的构建速度“势如闪电”?
- …
四、高级kotlin强化实战
1、Kotlin入门教程
2、Kotlin 实战避坑指南
3、项目实战《Kotlin Jetpack 实战》
-
从一个膜拜大神的 Demo 开始
-
Kotlin 写 Gradle 脚本是一种什么体验?
-
Kotlin 编程的三重境界
-
Kotlin 高阶函数
-
Kotlin 泛型
-
Kotlin 扩展
-
Kotlin 委托
-
协程“不为人知”的调试技巧
-
图解协程:suspend
五、Android高级UI开源框架进阶解密
1.SmartRefreshLayout的使用
2.Android之PullToRefresh控件源码解析
3.Android-PullToRefresh下拉刷新库基本用法
4.LoadSir-高效易用的加载反馈页管理框架
5.Android通用LoadingView加载框架详解
6.MPAndroidChart实现LineChart(折线图)
7.hellocharts-android使用指南
8.SmartTable使用指南
9.开源项目android-uitableview介绍
10.ExcelPanel 使用指南
11.Android开源项目SlidingMenu深切解析
12.MaterialDrawer使用指南
六、NDK模块开发
1、NDK 模块开发
2、JNI 模块
3、Native 开发工具
4、Linux 编程
5、底层图片处理
6、音视频开发
7、机器学习
七、Flutter技术进阶
1、Flutter跨平台开发概述
2、Windows中Flutter开发环境搭建
3、编写你的第一个Flutter APP
4、Flutter开发环境搭建和调试
5、Dart语法篇之基础语法(一)
6、Dart语法篇之集合的使用与源码解析(二)
7、Dart语法篇之集合操作符函数与源码分析(三)
…
八、微信小程序开发
1、小程序概述及入门
2、小程序UI开发
3、API操作
4、购物商场项目实战……
全套视频资料:
一、面试合集
二、源码解析合集
三、开源框架合集
欢迎大家一键三连支持,若需要文中资料,直接点击文末CSDN官方认证微信卡片免费领取↓↓↓
以上是关于Android ANR 实现机制详解的主要内容,如果未能解决你的问题,请参考以下文章
Android 进阶——Framework 核心ANR( Applicatipon No Response)机制设计思想详解
Android 进阶——Framework 核心ANR( Applicatipon No Response)机制设计思想详解