如何建立微信扫雷群

安卓应用没有回应,你真的明白吗?

安卓应用没有回应,你真的明白吗?

介绍

无论从事安卓应用开发还是安卓系统开发,我们都应该遇到应用无响应的问题(ANR)。当应用程序在一段时间内未能及时响应时,将弹出一个ANR对话框,允许用户选择是继续等待还是强制关闭。

绝大多数人对ANR的理解只是停留在主线上。耗时或繁忙的中央处理器将导致ANR。在采访了众多候选人之后,几乎没有人能从制度层面真正理清ANR的前因后果。例如,哪些路径会触发ANR?当主线不耗时时,ANR也可能发生吗?如何更好地调试ANR?

没有对安卓框架源代码的深入研究,很难形成对ANR的全面、正确的理解。在研究了该系统的源代码和工作实践之后,我对其进行了改进,并用图解的方式向您进行了解释。我相信这会帮助你加深对ANR的了解。

ANR触发机制

在学习知识的过程中,一个人必须知道它是什么,为什么是,这样才能像熟练工人一样熟练。要深刻理解ANR,需要从根本上寻找答案,即ANR是如何引发的?

ANR是一个监控安卓应用响应是否及时的机制。ANR的发生可以比作炸弹爆炸。那么整个过程由三个部分组成:

公共ANR包括服务、广播、提供商和输入。欲了解更多详情,请参阅了解安卓ANR的触发原理,http://gityuan.com/2016/07/02/android-anr.下一步,本文将以图形形式进行解释。

服务超时机制

让我们来看看在整个startService启动过程中埋设炸弹和拆除炸弹的过程。

图表1:

有关更多详细信息,请参考http://gityuan.com/2016/03/06/start-service.的启动服务启动流程分析

展开全文

广播超时机制

广播和服务具有相同的超时机制。对于静态注册的广播,需要在超时检测期间检测服务提供商,如下图所示。

图2:

(注意:服务点使用8.0版本中名为“排队工作循环程序”的处理程序线程,并使用旧版本中由newSingleThreadExecutor创建的单线程线程池)

如果是动态广播或静态广播没有执行持久操作的服务点任务,则不需要通过“排队工作循环”线程,而是直接向中央控制系统报告,过程更简单,如下图所示:

可以看出,只有XML静态注册的广播超时检测过程才会考虑是否有尚未完成的服务点,动态广播不会受到其影响。服务点的应用程序将修改后的数据项更新到内存中,然后将数据异步同步到磁盘文件。因此,在许多地方,建议在主线程的调用中采用apply方法,以避免阻塞主线程。但是,静态广播超时检测过程要求所有服务点都保持在磁盘上。如果你过度使用申请,申请ANR的概率将会增加。详情请见http://gityuan.com/2017/06/18/SharedPreferences.

谷歌在这一设计中的初衷是确保在静态广播场景中进程终止之前,能够完成服务提供商的数据持久性。因为在向中央控制系统报告广播接收器的工作完成之前,进程的优先级是前台,所以高优先级下的进程不会被终止,而是将被分配更多的中央处理器时间片,以加速SP持久性的完成。

详情请参考http://gityuan.com/2016/06/04/broadcast-receiver.安卓广播播放机制分析

提供商超时机制

只有在首次启动提供程序进程时,才会检测到提供程序超时。当提供程序进程启动时,再次请求提供程序不会触发提供程序超时。

图3:

有关更多详细信息,请参见理解内容提供商原则,http://gityuan.com/2016/07/30/content-provider

输入超时机制

输入的超时检测机制与服务、广播和提供商完全不同。为了更好地理解输入过程,首先介绍两个重要线程的相关工作:

InputReader线程负责通过EventHub(监听目录/dev/input)读取输入事件,一旦监听到输入事件则放入到InputDispatcher的mInBoundQueue队列,并通知其处理该事件; InputDispatcher线程负责将接收到的输入事件分发给目标应用窗口,分发过程使用到3个事件队列: mInBoundQueue用于记录InputReader发送过来的输入事件; outBoundQueue用于记录即将分发给目标应用窗口的输入事件; waitQueue用于记录已分发给目标应用,且应用尚未处理完成的输入事件; mInBoundQueue用于记录InputReader发送过来的输入事件; outBoundQueue用于记录即将分发给目标应用窗口的输入事件; waitQueue用于记录已分发给目标应用,且应用尚未处理完成的输入事件;

输入的超时机制不一定在时间到时爆炸,但处理后续报告事件的过程将检测爆炸是否发生,因此更被认为是扫雷过程,如下图所示。

图4:

对于按键类型的输入事件,则outboundQueue或者waitQueue不为空, 对于非按键的输入事件,则waitQueue不为空,且等待队头时间超时500ms

为什么输入超时机制排雷而不是定时爆炸?这是因为对于输入,即使事件的执行时间超过超时周期,只要用户稍后不生成输入事件,ANR也不会被触发。这里的去挖掘是指在当前输入系统中正在处理耗时事件的前提下,每个后续输入事件将检测正在处理的前一个事件是否超时(进入去挖掘状态),以及当前时间是否超过从最后一个输入事件分发时间点开始的超时周期。如果是前一个输入事件,ANR的超时将被重置,这样它就不会爆炸。

有关更多详细信息,请参见输入系统-ANR原理分析,http://gityuan.com/2017/01/01/input-anr

ANR超时阈值

不同组件的超时阈值不同。服务、广播、内容提供商和输入的超时阈值如下:

前台服务和后台服务的区别

系统启动前台服务的超时时间是200秒,而后台服务的超时时间是200秒,那么系统如何区分前台服务和后台服务呢?让我们看看动态服务的核心逻辑:

在startService进程中,发起进程调用程序所隶属的进程调度组决定已启动的服务是属于前台还是后台。当发起进程不等于process list . schemgroundbackground(后台进程组)时,它被认为是前台服务,否则是后台服务,并在ServiceRecord中用成员变量createdFromFg标记。

SCHEDGROUPBACKGROUND调度组有哪些流程?过程调度组可以大致分为顶层、前台和后台。过程优先级(Adj)和过程调度组(SCHED_GROUP)算法相对复杂。对应关系可以大致理解为Adj等于0的进程属于顶级进程组,Adj等于100或200的进程属于前台进程组,Adj大于200的进程属于后台进程组。Adj的意思见下表,Adj是一个简单的形容词。用户基本察觉不到200进程,主要做一些后台工作,所以后台服务有一个较长的超时阈值,后台服务属于后台进程调度组(background process scheduling group),它分配的CPU时间片少于前台服务属于前台进程调度组。

有关详细信息,请参考http://gityuan.com/2018/05/19/android-process-adj.解释安卓进程优先级的ADJ算法

前台服务具体而言,它是指由前台流程调度组中的流程发起的服务。这不同于通常的fg服务,后者指的是带有前台通知的服务。

前台和后台广播超时

前台广播超时10秒,后台广播超时60秒,如何区分前台广播和后台广播?让我们看看AMS的核心逻辑:

根据发送广播(Intent intent)中的意图标志是否包含FlagGreceIverforeGround,决定将广播放在前台广播队列或后台广播队列中。前台广播队列的超时是10s,后台广播队列的超时是60s。默认情况下,广播被放在后台广播队列中,除非添加了FlagGreceIverforeGround标志。

背景广播比前景广播具有更长的超时阈值。同时,当后台服务在广播分发过程(mDelayBehindServices)中启动时,广播分发将被延迟,等待服务完成。等待服务引起的ANR广播将被忽略。后台广播属于后台进程调度组,而前台广播属于前台进程调度组。简而言之,背景广播不太容易出现在ANR,执行速度也较慢。

此外,只有串行广播有超时机制,因为接收器是串行的,而前一个接收器速度较慢,这将影响后一个接收器。并行广播通过环路一次将广播事件分发给所有接收器,因此不存在相互影响的问题,也不存在广播超时。

前台广播,确切地说,是指前台广播队列中的广播。

ANR正面和背面

除了前台服务、前台广播和前台ANR之外,你可能还在雾中。看看核心逻辑:

决定是前景还是背景ANR取决于当ANR出现时用户是否可感知该应用,例如活动对前景可见的过程或前景通知fg服务的过程。这些是用户可感知的场景。ANR的出现对用户体验有很大影响,所以有必要弹出一个框让用户决定是退出还是等待。如果这样的应用程序被直接杀死,它将导致用户莫名其妙地闪回。

与前景ANR相比,背景ANR只抓取无响应进程发生的轨迹,不收集中央处理器信息,将直接在背景中杀死无响应进程,而不会用弹出框提示用户。

ANR前线办公室正是用户能够感知的流程中出现的ANR。

ANR爆炸现场

ANR发生服务、广播、提供商和输入后,中央控制系统将立即获取现场信息进行调试和分析。收集的信息包括以下内容:

将amanr信息输出到EventLog,也就是说ANR触发的时间点最接近的就是EventLog中输出的amanr信息 收集以下重要进程的各个线程调用栈trace信息,保存在data/anr/traces.txt文件 当前发生ANR的进程,system_server进程以及所有persistent进程 audioserver, cameraserver, mediaserver, surfaceflinger等重要的native进程 CPU使用率排名前5的进程 将发生ANR的reason以及CPU使用情况信息输出到main log 将traces文件和CPU使用情况信息保存到dropbox,即data/system/dropbox目录 对用户可感知的进程则弹出ANR对话框告知用户,对用户不可感知的进程发生ANR则直接杀掉 当前发生ANR的进程,system_server进程以及所有persistent进程 audioserver, cameraserver, mediaserver, surfaceflinger等重要的native进程 CPU使用率排名前5的进程

整个ANR信息收集过程相对耗时,每次捕获捕获过程的跟踪信息等待200毫秒,这表明等待时间越持久。关于捕获跟踪命令,Java进程可以通过在adb外壳环境中执行kill -3 [pid]来捕获相应pid的调用堆栈。对于在adb shell环境中执行调试器的本机进程,可以获取相应pid的调用堆栈。Txt和dropbox目录,用于ANR问题后的跟踪。有关更多详细信息,请参见了解安卓ANR的信息收集流程,http://gityuan.com/2016/12/02/app-not-response.

使用现场信息,您可以调试和分析,首先定位ANR发生的时间点,然后检查跟踪信息,然后分析是否存在耗时的消息和绑定调用、锁竞争、CPU资源抢占,并结合特定场景的上下文进行分析。调试意味着需要从系统的角度为前面提到的消息、绑定器、锁和其他资源提炼更多的调试信息,这里不再赘述,然后用ANR案例进行解释。

作为应用程序开发人员,主线程应该尽可能只做与用户界面相关的操作,以避免耗时的操作,如过于复杂的用户界面绘制、网络操作和文件输入输出操作。为了避免主线程和工作线程之间的锁竞争,减少系统耗时的绑定调用,小心使用共享首选项(sharePreference),并注意主线程执行提供者查询操作。简而言之,尽可能减少主线程的负载,让它空闲并等待,以便随时响应用户的操作。

回答

最后,要回答文章开头的问题,哪条路会触发ANR?承诺是从定时炸弹种植到拆除炸弹的任何一条或多条路径的缓慢执行将导致ANR的缓慢执行(以服务为例)。可能是服务生命周期的回调方法(如onStartCommand)比较慢,或者是主线程的消息队列中有其他耗时的消息延迟了服务回调方法的执行,或者是SP操作比较慢,或者是system_server进程的绑定线程比较忙,没有及时收到拆弹指令。此外,活动管理器线程也可能被阻塞。这种现象是前台服务的执行时间可能超过10秒,但ANR不会发生。

当ANR发生时,主线程空闲或停留在非耗时代码中的原因是什么?可能是捕获跟踪太耗时,错过了场景,也可能是主线程的消息队列积累了大量消息,捕获快照的最后时刻只是一个瞬态,也可能是广播“队列-工作-循环程序”一直在处理服务点操作。

本文的知识来源于对安卓系统源代码的研究和工作实践。安卓达摩学院独家武术秘密与您分享,希望能增进您对ANR的了解。

感觉良好

请观看

请回搜狐看看更多

负责任的编辑:

发布时间:2019-11-24

发表评论:

◎欢迎参与讨论,请在这里发表您的看法、交流您的观点。