找回密码
 会员注册
查看: 19|回复: 0

今日头条ANR优化实践系列-设计原理及影响因素

[复制链接]

4

主题

0

回帖

13

积分

新手上路

积分
13
发表于 2024-9-30 13:11:34 | 显示全部楼层 |阅读模式
写在前面ANR 问题,对于从事 Android 开发的同学来说并不陌生,日常开发中,经常会遇到应用乃至系统层面引起的各种问题,很多时候因为不了解其运行原理,在面对该类问题时可能会一头雾水。与此同时,因为现有监控能力不足或获取信息有限,使得这类问题如同镜中花水中月,让我们在追求真理的道路上举步维艰。如下图:工作中在帮助大家分析问题时,发现有不少同学问到,在哪里可以更加系统的学习?于是本人抱着“授人以鱼,不如授人以渔”的态度,结合个人理解和工作实践,接下来将从设计原理、影响要素、工具建设、分析思路,案例实战、优化探索等几个篇章,对 ANR 方向进行一次全面的总结,希望帮助大家在今后的工作中更好地理解和应对以下问题:什么是 ANR?系统是如何设计 ANR 的?发生 ANR 时系统都会获取哪些信息以及工作流程?导致 ANR 的原因有哪些?遇到这类问题该如何分析?如何能更加快速准确的定位问题?面对这类问题我们能主动做些什么?简述在正式分析 ANR 问题之前,先来看看下面这些问题:系统是如何设计 ANR 的,都有哪些服务或者组件会发生 ANR?发生 ANR 的时候,系统又是如何工作的,都会获取哪些信息?影响 ANR 的场景有哪些?我们是如何对其进行归类的?了解这些有助于我们在面对各种问题时,做到有的放矢,下面我们就来介绍并回答这些问题。ANR 设计原理ANR 全称 Applicatipon No Response;Android 设计 ANR 的用意,是系统通过与之交互的组件(Activity,Service,Receiver,Provider)以及用户交互(InputEvent)进行超时监控,以判断应用进程(主线程)是否存在卡死或响应过慢的问题,通俗来说就是很多系统中看门狗(watchdog)的设计思想。组件超时分类系统在通过 Binder 通信向应用进程发送上述组件消息或 Input 事件时,在 AMS 或 Input 服务端同时设置一个异步超时监控。当然针对不同类型事件,设置的超时时长也存在差别,以下是 Android 系统对不同类型的超时阈值设置:(图片仅供参考,国内厂商可能会有调整,每个厂商的标准也存在差异)Broadcast 超时原理举例在了解不同类型消息的超时阈值之后,我们再来了解一下超时监控的设计原理。以 BroadCastReceiver 广播接收超时为例,广播分为有序广播和无序广播,同时又有前台广播和后台广播之分;只针对有序广播设置超时监控机制,并根据前台广播和后台广播的广播类型决定了超时时长;例如后台广播超时时长 60S,前台广播超时时长只有 10S; 下面我们结合代码实现来看一下广播消息的发送过程。无序广播:对于无序广播,系统在搜集所有接收者之后一次性全部发送完毕,如下图;通过上图我们看到无序广播是没有设置超时监听机制的,一次性发送给所有接收者,对于应用侧何时接收和响应完全不关心(相当于 UDP 传输)。有序广播:再来看一下有序广播的发送和接收逻辑,同样在系统 AMS 服务中,BoradCastQueue 获取当前正在发送的广播消息,并取出下一个广播接收者,更新发送时间戳,以此时间计算并设置超时时间(但是系统在此进行了一些优化处理,以避免每次广播正常接收后,都需要取消超时监控然后又重新设置,而是采用一种对齐的方式进行复用)。最后将该广播发送给接收者,接收到客户端的完成通知之后,再发送下一个,整个过程如此反复。在客户端进程中,Binder 线程接收到 AMS 服务发送过来的广播消息之后,会将此消息进行封装成一个 Message,然后将 Message 发送到主线程消息队列(插入到消息队列当前时间节点的位置,也正是基于此类设计导致较多消息调度及时性的问题,后面我们将详细介绍),消息接收逻辑如下:正常情况下,很多广播请求都会在客户端及时响应,然后通知到系统 AMS 服务取消本次超时监控。但是在部分业务场景或系统场景异常的情况下,发送的广播未及时调度,没有及时通知到系统服务,便会在系统服务侧触发超时,判定应用进程响应超时。AMS 响应超时代码逻辑如下:finalvoidbroadcastTimeoutLocked(booleanfromMsg){......longnow=SystemClock.uptimeMillis();BroadcastRecordr=mOrderedBroadcasts.get(0);if(fromMsg){//我们刚才提到的时间对齐方式,避免频繁取消和设置消息超时longtimeoutTime=r.receiverTime+mTimeoutPeriod;if(timeoutTime>now){setBroadcastTimeoutLocked(timeoutTime);return;}}............ObjectcurReceiver;if(r.nextReceiver>0){//获取当前超时广播接收者curReceiver=r.receivers.get(r.nextReceiver-1);r.delivery[r.nextReceiver-1]=BroadcastRecord.DELIVERY_TIMEOUT;}else{curReceiver=r.curReceiver;}Slog.w(TAG,"Receiverduringtimeoutof"+r+":"+curReceiver);............if(app!=null){anrMessage="Broadcastof"+r.intent.toString();}......if(!debugging&anrMessage!=null){//开始通知AMS服务处理当前超时行为mHandler.post(newAppNotResponding(app,anrMessage));}}到这里,广播发送和超时监控逻辑的分析就基本结束了,通过介绍,我们基本知道了广播超时机制是如何设计和工作的,整体流程图示意图如下:ANR Trace Dump 流程上面我们以广播接收为例,介绍了系统监控原理,下面再来介绍一下,发生 ANR 时系统工作流程。ANR 信息获取:继续以广播接收为例,在上面介绍到当判定超时后,会调用系统服务 AMS 接口,搜集本次 ANR 相关信息并存档(data/anr/trace,data/system/dropbox),入口如下。进入系统服务 AMS 之后,AppError 先进行场景判断,以过滤当前进程是不是已经发生并正在执行 Dump 流程,或者已经发生 Crash,或者已经被系统 Kill 之类的情况。并且还考虑了系统是否正在关机等场景,如果都不符合上述条件,则认为当前进程真的发生 ANR。接下来系统再判断当前 ANR 进程对用户是否可感知,如后台低优先级进程(没有重要服务或者 Activity 界面)。然后开始统计与该进程有关联的进程,或系统核心服务进程的信息;例如与应用进程经常交互的 SurfaceFligner,SystemServer 等系统进程,如果这些系统服务进程在响应时被阻塞,那么将导致应用进程 IPC 通信过程被卡死。首先把自身进程(系统服务 SystemServer)加进来,逻辑如下:接着获取其它系统核心进程,因为这些服务进程是 Init 进程直接创建的,并不在 SystemServer 或 Zygote 进程管理范围。在搜集完第一步信息之后,接下来便开始统计各进程本地的更多信息,如虚拟机相关信息、Java 线程状态及堆栈。以便于知道此刻这些进程乃至系统都发生了什么情况。理想很丰满,现实很骨感,后面我们会重点讲述为何有此感受。系统为何要收集其它进程信息呢?因为从性能角度来说,任何进程出现高 CPU 或高 IO 情况,都会抢占系统资源,进而影响其它进程调度不及时的现象。下面从代码角度看看系统 dump 流程:privatestaticvoiddumpStackTraces(StringtracesFile,ArrayListfirstPids,ArrayListnativePids,ArrayListextraPids,booleanuseTombstonedForJavaTraces){............//考虑到性能影响,一次dump最多持续20S,否则放弃后续进程直接结束remainingTime=20*1000;try{......//按照优先级依次获取各个进程trace日志intnum=firstPids.size();for(inti=0;i
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 会员注册

本版积分规则

QQ|手机版|心飞设计-版权所有:微度网络信息技术服务中心 ( 鲁ICP备17032091号-12 )|网站地图

GMT+8, 2025-1-15 07:36 , Processed in 0.498845 second(s), 25 queries .

Powered by Discuz! X3.5

© 2001-2025 Discuz! Team.

快速回复 返回顶部 返回列表