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

本文深入探讨Android中的ANR问题,包括设计原理、影响因素、工具建设和优化策略等内容,旨在帮助开发者更好地理解ANR并有效应对。

写在前面

ANR 问题,对于从事 Android 开发的同学来说并不陌生,日常开发中,经常会遇到应用乃至系统层面引起的各种问题,很多时候因为不了解其运行原理,在面对该类问题时可能会一头雾水。与此同时,因为现有监控能力不足或获取信息有限,使得这类问题如同镜中花水中月,让我们在追求真理的道路上举步维艰。如下图:

工作中在帮助大家分析问题时,发现有不少同学问到,在哪里可以更加系统的学习?于是本人抱着“授人以鱼,不如授人以渔”的态度,结合个人理解和工作实践,接下来将从设计原理、影响要素、工具建设、分析思路,案例实战、优化探索等几个篇章,对 ANR 方向进行一次全面的总结,希望帮助大家在今后的工作中更好地理解和应对以下问题:

  1. 什么是 ANR?

  2. 系统是如何设计 ANR 的?

  3. 发生 ANR 时系统都会获取哪些信息以及工作流程?

  4. 导致 ANR 的原因有哪些?

  5. 遇到这类问题该如何分析?

  6. 如何能更加快速准确的定位问题?

  7. 面对这类问题我们能主动做些什么?

简述

在正式分析 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 响应超时代码逻辑如下:

final void broadcastTimeoutLocked(boolean fromMsg) {
        ......
        long now = SystemClock.uptimeMillis();
        BroadcastRecord r = mOrderedBroadcasts.get(0);
        if (fromMsg) {
            //我们刚才提到的时间对齐方式,避免频繁取消和设置消息超时
            long timeoutTime = r.receiverTime + mTimeoutPeriod;
            if (timeoutTime > now) {
                setBroadcastTimeoutLocked(timeoutTime);
                return;
            }
        }
        ......
        ......
        Object curReceiver;
        if (r.nextReceiver > 0) {
            //获取当前超时广播接收者
  &nb
评论 3
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值