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() -
- AMS侧:
ActivityManagerService.startService()
->ActiveServices.startServiceLocked()
->ActiveServices.startServiceInnerLocked()
->ActiveServices.bringUpServiceLocked()
->ActivieServices.realStartServiceLocked()
->ActivieServices.realStartServiceLocked
- AMS侧:
- 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消息到AMSbumpServiceExecutingLocked(r, execInFg, "create", null /* oomAdjReason */);
................// 通知APP启动Service,执行 handleCreateService// 调用到 ApplicationThread.scheduleCreateServicethread.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的回调onCreateTrace.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启动完成,需要通知AMSActivityManager.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.javavoid serviceTimeout(ProcessRecord proc) {................// 计算是否超时,if (anrMessage != null) { // 超时// 触发超时机制,mAm是AMS,mAnrHelper是AnrHelpermAm.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 {@Overridepublic void run() {AnrRecord r;while ((r = next()) != null) {......//这里的r就是AnrRecordr.appNotResponding(onlyDumpSelf);......}}
}
private static class AnrRecord {void appNotResponding(boolean onlyDumpSelf) {//mApp是ProcessRecord,mErrorState是ProcessErrorStateRecordmApp.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的Dialogif (mService.mUiHandler != null) {// Bring up the infamous App Not Responding dialogMessage 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的流程:

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的时间// 检查我们是否正在等待一个聚焦窗口出现。如果等待时间过长就报 ANRif (mNoFocusedWindowTimeoutTime.has_value() && mAwaitedFocusedApplication != nullptr) {if (currentTime >= *mNoFocusedWindowTimeoutTime) {// 场景1: 触发noFocusedWindow的anrprocessNoFocusedWindowAnrLocked();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: 触发ANRonAnrLocked(connection);return LONG_LONG_MIN;
}
4.4 ANR在InputDispatcher的调用栈
6.排查思路:
- adb shell getevent后,点击屏幕,如果有打印,代表驱动是可以拿到点击事件的。如果没有则需要内核的同事查看。如果有则代表是Android的问题。
相关文章:
Android12的ANR解析
0. 参考: ANR分析 深入理解 Android ANR 触发原理以及信息收集过程 1.ANR的触发分类: ANR分为4类: InputDispatchTimeout:输入事件分发超时5s,包括按键和触摸事件。BroadcastTimeout:比如前台广播在10s内未执行完成࿰…...
初学人工智不理解的名词3
TTS领域的名词 from gpt-4o 在 TTS(文本到语音合成) 领域,以下是 CFM、One-Step 蒸馏 和 ReFlow 的含义和作用的详细解释: 1. CFM(Consistent Flow Matching) Consistent Flow Matching(一致流…...
ADS项目笔记 1. 低噪声放大器LNA天线一体化设计
在传统射频结构的设计中,天线模块和有源电路部分相互分离,两者之间通过 50 Ω 传输线级联,这种设计需要在有源电路和天线之间建立无源网络,包括天线模块的输入匹配网络以及有源电路的匹配网络。这些无源网络不仅增加了系统的插入损…...
J.U.C - 深入解读阻塞队列实现原理源码
文章目录 Pre生产者-消费者模式阻塞队列 vs 普通队列JUC提供的7种适合与不同应用场景的阻塞队列插入操作:添加元素到队列中移除操作:从队列中移除元素。 ArrayBlockingQueue源码解析类结构指定初始容量及公平/非公平策略的构造函数根据已有集合初始化队列…...
【大语言模型学习】LORA微调方法
LORA: Low-Rank Adaptation of Large Language Models 摘要 LoRA (Low-Rank Adaptation) 提出了一种高效的语言模型适应方法,针对预训练模型的适配问题: 目标:减少下游任务所需的可训练参数,降低硬件要求。方法:冻结预训练模型权重,注入低秩分解矩阵,从而在不影响推理…...
Spring Boot【一】
Spring Boot全局配置文件 application.properties 是 Spring Boot 的标准配置文件,用于集中管理应用程序的配置属性。它的主要作用是将配置信息与代码分离,使得应用程序更具可维护性和可配置性。 Application.yaml配置文件 YAML文件格式是JSON超集文件…...
H.265流媒体播放器EasyPlayer.js H.264/H.265播放器chrome无法访问更私有的地址是什么原因
EasyPlayer.js H5播放器,是一款能够同时支持HTTP、HTTP-FLV、HLS(m3u8)、WS、WEBRTC、FMP4视频直播与视频点播等多种协议,支持H.264、H.265、AAC、G711A、MP3等多种音视频编码格式,支持MSE、WASM、WebCodec等多种解码方…...
【大数据学习 | HBASE高级】rowkey的设计,hbase的预分区和压缩
1. rowkey的设计 RowKey可以是任意字符串,最大长度64KB,实际应用中一般为10~100bytes,字典顺序排序,rowkey的设计至关重要,会影响region分布,如果rowkey设计不合理还会出现region写热点等一系列问题。 …...
Dart:字符串
字符串:单双引号 String c hello \c\; // hello c,单引号中使用单引号,需要转义\ String d "hello c"; // hello c,双引号中使用单引号,不需要转义 String e "hello \“c\”"; // hell…...
平衡二叉搜索树之 红黑 树的模拟实现【C++】
文章目录 红黑树的简单介绍定义红黑树的特性红黑树的应用 全部的实现代码放在了文章末尾准备工作包含头文件类的成员变量和红黑树节点的定义 构造函数和拷贝构造swap和赋值运算符重载析构函数findinsert【重要】第一步:按照二叉搜索树的方式插入新节点第二步&#x…...
2:Vue.js 父子组件通信:让你的组件“说话”
上一篇我们聊了如何用 Vue.js 创建一个简单的组件,这次咱们再往前走一步,讲讲 Vue.js 的父子组件通信。组件开发里,最重要的就是让组件之间能够“说话”,数据能流通起来。废话不多说,直接开干! 父组件传数据…...
6. Keepalived配置Nginx自动重启,实现7x24提供服务
一. Keepalived配置Nginx自动重启,实现7x24提供服务 1.编写不停的检查nginx服务器状态,停止并重启,重启失败后则停止keepalived脚本 cd /etc/keepalived/ vim check_nginx_alive_or_not.sh #---内容如下:--------------- #!/bin/bash A=`ps -C nginx --no-header |wc -l...
【PS】蒙版与通道
内容1: 、选择蓝色通道并复制,对复制的蓝色通道ctrli进行反向选择,然后ctrll调整色阶。 、选择载入选区,然后点击rgb。 、点击蒙版 、点击云彩图层调整位置 、点击色相/饱和度,适当调整 、最后使用滤镜等功能添加光圈…...
C++创建型模式之生成器模式
解决的问题 生成器模式(Builder Pattern)主要解决复杂对象的构建问题。当一个对象的创建过程非常复杂,涉及多个步骤和多个部件时,使用生成器模式可以将对象的构建过程与其表示分离,使得同样的构建过程可以创建不同的表…...
鸿蒙NEXT应用示例:切换图片动画
【引言】 在鸿蒙NEXT应用开发中,实现图片切换动画是一项常见的需求。本文将介绍如何使用鸿蒙应用框架中的组件和动画功能,实现不同类型的图片切换动画效果。 【环境准备】 电脑系统:windows 10 开发工具:DevEco Studio NEXT B…...
postgresql(功能最强大的开源数据库)继承特性和分区实现
PostgreSQL实现了表继承,在多重表继承下,对上亿条不同类别的数据条目进行按型号、按月份双层分区管理,既可在总表查阅所有条目的共有字段,也可在各类型字表查询附加字段,非常高效。 分区是通过继承的方式来实现的&…...
论文笔记(五十六)VIPose: Real-time Visual-Inertial 6D Object Pose Tracking
VIPose: Real-time Visual-Inertial 6D Object Pose Tracking 文章概括摘要I. INTRODACTIONII. 相关工作III. APPROACHA. 姿态跟踪工作流程B. VIPose网络 文章概括 引用: inproceedings{ge2021vipose,title{Vipose: Real-time visual-inertial 6d object pose tra…...
微服务治理详解
文章目录 什么是微服务架构为什么要使用微服务单体架构如何转向微服务架构服务治理服务治理治的是什么服务注册与发现服务熔断降级服务网关服务调用服务负载均衡服务配置中心 微服务解决方案SpringCloud体系EurekaHystrixGatewayOpenFeignRibbonConfig SpringCloud Alibaba体系…...
“南海明珠”-黄岩岛(民主礁)领海基线WebGIS绘制实战
目录 前言 一、关于岛屿的基点位置 1、领海基点 二、基点坐标的转换 1、最底层的左边转换 2、单个经纬度坐标点转换 3、完整的转换 三、基于天地图进行WebGIS展示 1、领海基点的可视化 2、重要城市距离计算 四、总结 前言 南海明珠黄岩岛,这座位于南海的…...
Oracle数据库 创建dblink的过程及其用法详解
前言 dblink是Oracle数据库中用于连接不同数据库实例的一种机制。通过dblink,用户可以在一个数据库实例中直接查询或操作另一个数据库实例中的表、视图或存储过程。 dblink的作用主要体现在以下几个方面: 跨数据库操作:允许用户…...
GA/T 1400视图库实战:从零部署Easy1400平台到设备级联全流程解析
1. 初识GA/T 1400与Easy1400平台 第一次接触GA/T 1400标准时,我完全被各种专业术语绕晕了。简单来说,这是一套专门针对视频监控领域的行业标准,规定了视频图像信息在采集、传输、存储等环节的技术要求。而Easy1400就是基于这个标准开发的一套…...
技术视角:分布式投票系统的异步解耦架构与多语言协同实践
技术视角:分布式投票系统的异步解耦架构与多语言协同实践 【免费下载链接】example-voting-app Example Docker Compose app 项目地址: https://gitcode.com/gh_mirrors/exa/example-voting-app 在当今企业级应用架构设计中,如何平衡高并发处理、…...
从0到1:手把手教你搭建VSCode(附避坑指南,拒绝报错),全程复制粘贴即可
🔥个人主页:北极的代码(欢迎来访) 🎬作者简介:java后端学习者 ❄️个人专栏:苍穹外卖日记,SSM框架深入,JavaWeb ✨命运的结局尽可永在,不屈的挑战却不可须臾或…...
多模态AI实战:基于OpenGVLab/Ask-Anything构建视觉问答系统
1. 项目概述:当视觉大模型学会“看图说话”最近在折腾多模态AI应用,发现了一个挺有意思的开源项目,叫OpenGVLab/Ask-Anything。简单来说,它就像一个给AI装上了“眼睛”和“嘴巴”的系统,你给它一张图片或一段视频&…...
百度网盘直链解析终极指南:如何实现高速下载的完整技术方案
百度网盘直链解析终极指南:如何实现高速下载的完整技术方案 【免费下载链接】baidu-wangpan-parse 获取百度网盘分享文件的下载地址 项目地址: https://gitcode.com/gh_mirrors/ba/baidu-wangpan-parse 在云存储服务普及的今天,百度网盘作为国内用…...
Bifrost:轻量高效的实时数据同步平台架构与实战
1. 项目概述:Bifrost,一个被低估的现代数据同步利器如果你正在处理跨数据库、跨数据源的数据同步任务,并且对传统ETL工具的笨重、配置复杂感到头疼,那么maximhq/bifrost这个项目绝对值得你花时间深入了解。我第一次接触Bifrost是在…...
C# AI开发实战:BotSharp框架构建企业级NLP应用指南
1. 项目概述:当C#开发者遇上AI应用开发如果你是一名长期深耕.NET生态的开发者,最近看着Python在AI领域风生水起,心里是不是有点痒,又有点不甘?总觉得为了跑个模型、搭个智能对话,就得切到另一个完全不同的技…...
基于Electron的ChatGPT桌面客户端开发:架构、功能与进阶实践
1. 项目概述:一个开源桌面客户端的诞生与价值如果你和我一样,在日常开发、写作或者处理一些需要深度思考的任务时,经常需要和ChatGPT这样的AI助手对话,那你一定对在浏览器里反复切换标签页、刷新页面、管理冗长的对话历史感到厌烦…...
深度神经网络参数安全与Hessian-aware训练防御技术
1. 深度神经网络参数安全威胁现状深度神经网络(DNN)在内存中的参数面临着严重的比特翻转安全威胁。这种威胁主要来自两个方面:自然发生的硬件故障和人为发起的攻击行为。在IEEE-754 32位浮点数表示中,一个比特的翻转可能导致参数值发生灾难性变化。例如&…...
避开这些坑,你的YOLO论文才能发得快!目标检测老鸟的实战避坑与效率工具清单
YOLO论文高效产出指南:目标检测老手的避坑策略与工具链实战 实验室的灯光在凌晨三点依然亮着,屏幕上YOLOv8的loss曲线却像心电图一样毫无规律地跳动着。这已经是本周第三次复现顶会论文失败,而距离截稿日期只剩三周。如果你也经历过这种"…...
