文章

ANR基础

ANR基础

ANR 基础

ANR 本质

ANR(Application Not Responding),应用程序无响应,简单一个定义,却涵盖了很多 Android 系统的设计思想

首先,ANR 属于应用程序的范畴。这不同于 SNR(System Not Respoding),SNR 反映的问题是系统进程 (system_server) 失去了响应能力,而 ANR 明确将问题圈定在应用程序。SNR 由 Watchdog 机制保证,具体可以查阅 Watchdog 机制以及问题分析; ANR 由消息处理机制保证,Android 在系统层实现了一套精密的机制来发现 ANR,核心原理是消息调度和超时处理

其次,ANR 机制主体实现在系统层。所有与 ANR 相关的消息,都会经过系统进程 (system_server) 调度,然后派发到应用进程完成对消息的实际处理,同时,系统进程设计了不同的超时限制来跟踪消息的处理。 一旦应用程序处理消息不当,超时限制就起作用了,它收集一些系统状态,譬如 CPU/IO 使用情况、进程函数调用栈,并且报告用户有进程无响应了 (ANR 对话框,部分 Rom 不显示 ANR 对话框,而是直接闪退到主界面)

然后,ANR 问题本质是一个性能问题。ANR 机制实际上对应用程序主线程的限制,要求主线程在限定的时间内处理完一些最常见的操作 (启动服务、处理广播、处理输入), 如果处理超时,则认为主线程已经失去了响应其他操作的能力。主线程中的耗时操作,譬如密集 CPU 运算、大量 IO、复杂界面布局等,都会降低应用程序的响应能力

最后,部分 ANR 问题是很难分析的。有时候由于系统底层的一些影响,导致消息调度失败,出现问题的场景又难以复现。 这类 ANR 问题往往需要花费大量的时间去了解系统的一些行为,超出了 ANR 机制本身的范畴。有一些 ANR 问题很难调查清楚,因为整个系统不稳定的因素很多,例如 Linux Kernel 本身的 Bug 引起的内存碎片过多、硬件损坏等。这类比较底层的原因引起的 ANR 问题往往无从查起,并且这根本不是应用程序的问题,浪费了应用开发人员很多时间,如果你从事过整个系统的开发和维护工作的话会深有体会。所以我不能保证了解了本章的所有内容后能够解决一切 ANR 问题,如果出现了很疑难的 ANR 问题,我建议最好去和做 Framework、驱动和内核的朋友聊聊,或者,如果问题只是个十万分之一的偶然现象,不影响程序的正常运行,我倒是建议不去理它

– From duanqz

ANR 常见类型

  1. **InputDispatching Timeout **5 秒内无法响应屏幕触摸事件或键盘输入事件
  2. BroadcastQueue Timeout 在执行前台广播(BroadcastReceiver)的 onReceive() 函数时 10 秒没有处理完成,后台为 60 秒。
  3. Service Timeout 前台 20s,后台 200s;startForeground 超时 10s
  4. **ContentProvider Timeout **内容提供者,在 publish 过超时 10s(少见)

ANR 原理?

系统 ANR 完整流程可以分为如下三个部分:

  • 超时检测
  • ANR 信息收集
  • ANR 信息输出

超时检测

触发 ANR 的过程可分为三个步骤:埋炸弹,、拆炸弹,、引爆炸弹

ANR(Application Not Responding)的监测原理本质上是消息机制,设定一个 delay 消息,超时未被移除则触发 ANR。具体逻辑处理都在 system server 端,包括发送超时消息,移除超时消息,处理超时消息以及 ANR 弹框展示等;对于 app 而言,触发 ANR 的条件是主线程阻塞。

1、Service Timeout

Service Timeout 是位于 ActivityManager 线程中的 AMS.MainHandler 收到 SERVICE_TIMEOUT_MSG 消息时触发。对于 Service 有两类:

  1. 对于前台服务,则超时为 SERVICE_TIMEOUT = 20s
  2. 对于后台服务,则超时为 SERVICE_BACKGROUND_TIMEOUT = 200s
  • **埋炸弹 **在 system_server 进程 ActiveServices.realStartServiceLocked() 调用的过程会埋下一颗炸弹, 超时没有启动完成则会爆炸
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
// How long we wait for a service to finish executing.
static final int SERVICE_TIMEOUT = 20 * 1000 * Build.HW_TIMEOUT_MULTIPLIER;

// How long we wait for a service to finish executing.
static final int SERVICE_BACKGROUND_TIMEOUT = SERVICE_TIMEOUT * 10;

private void realStartServiceLocked(ServiceRecord r, ProcessRecord app,
            IApplicationThread thread, int pid, UidRecord uidRecord, boolean execInFg,
            boolean enqueueOomAdj) throws RemoteException {
    bumpServiceExecutingLocked(r, execInFg, "create", null /* oomAdjReason */);
    // ...
}
private boolean bumpServiceExecutingLocked(ServiceRecord r, boolean fg, String why,
            @Nullable String oomAdjReason) {
    scheduleServiceTimeoutLocked(r.app);
    // ...
}
void scheduleServiceTimeoutLocked(ProcessRecord proc) {
    if (proc.mServices.numberOfExecutingServices() == 0 || proc.getThread() == null) {
        return;
    }
    Message msg = mAm.mHandler.obtainMessage(
        ActivityManagerService.SERVICE_TIMEOUT_MSG);
    msg.obj = proc;
    mAm.mHandler.sendMessageDelayed(msg, proc.mServices.shouldExecServicesFg()
                                    ? SERVICE_TIMEOUT : SERVICE_BACKGROUND_TIMEOUT);
}
  • 拆炸弹 主线程 handleCreateService() 拆除炸弹
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
// ActivityThread.java
private void handleCreateService(CreateServiceData data) {
    // ... 
    ActivityManager.getService().serviceDoneExecuting(data.token, SERVICE_DONE_EXECUTING_ANON, 0, 0);
}
// ActivityManagerService.java
public void serviceDoneExecuting(IBinder token, int type, int startId, int res) {
    synchronized(this) {
        // ...
        mServices.serviceDoneExecutingLocked((ServiceRecord) token, type, startId, res, false);
    }
}
// ActiveServices.java
private void serviceDoneExecutingLocked(ServiceRecord r, boolean inDestroying, boolean finishing) {
    ...
    if (r.executeNesting <= 0) {
        if (r.app != null) {
            r.app.execServicesFg = false;
            r.app.executingServices.remove(r);
            if (r.app.executingServices.size() == 0) {
                // 当前服务所在进程中没有正在执行的service
                mAm.mHandler.removeMessages(ActivityManagerService.SERVICE_TIMEOUT_MSG, r.app);
        ...
    }
    ...
}
  • 引爆炸弹
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
// MainHandler.java
final class MainHandler extends Handler {
    public void handleMessage(Message msg) {
        switch (msg.what) {
            case SERVICE_TIMEOUT_MSG: {
                mServices.serviceTimeout((ProcessRecord)msg.obj);
            } break;
            // ...
        }
        // ...
    }
}

// ActiveServices.java
void serviceTimeout(ProcessRecord proc) {
    if (anrMessage != null) {
        // 当存在timeout的service,则执行appNotResponding
        mAm.mAnrHelper.appNotResponding(proc, anrMessage);
    }
}

ANR 监控工具

LooperPrinter

监控主线程消息开始执行和结束执行时机的 hook 方案

钉钉 ANRCanary

image.png

ANR 分析?

ANR 分析办法一:Log

产生 ANR 后,看下 Log:
image.png
可以看到 logcat 清晰地记录了 ANR 发生的时间,以及线程的 tid 和一句话概括原因:WaitingInMainSignalCatcherLoop,大概意思为主线程等待异常。
最后一句 The application may be doing too much work on its main thread.告知可能在主线程做了太多的工作。

ANR 分析办法二:traces.txt

拉取 trace 文件

  1. 旧版本系统 /data/anr/traces.txt
  2. 新版本系统 /data/anr/anr_xxx 多个 anr 文件,也可以使用 adb bugreport xxx 拉取

ANR 面试题

什么是 ANR?ANR 发生的原因是什么?

ANR 即 Application Not Responding,顾名思义就是应用程序无响应。
在 Android 中,一般情况下,四大组件均是工作在主线程中的,Android 中的 Activity Manager 和 Window Manager 会随时监控应用程序的响应情况,如果因为一些耗时操作(网络请求或者 IO 操作)造成主线程阻塞一定时间(例如造成 5s 内不能响应用户事件或者 BroadcastReceiver 的 onReceive 方法执行时间超过 10s),那么系统就会显示 ANR 对话框提示用户对应的应用处于无响应状态。

Android 中为什么主线程不会因为 Looper.loop() 里的死循环卡死?

见 Handler 的 Android中为什么主线程不会因为Looper.loop()里的死循环卡死?

epoll 机制

ANR 了解过吗?有没有实际的 ANR 定位问题的经历

有,协程 runBlocking 使用不当导致的

线上 ANR 怎么搜集?高版本?

nativepollonce ANR 怎么解决?

Ref

《钉钉 ANR 治理最佳实践》

  • 1、《[钉钉 ANR 治理最佳实践定位 ANR 不再雾里看花](http://mp.weixin.qq.com/s?__biz=Mzg4MjE5OTI4Mw==&mid=2247498818&idx=1&sn=9e94b32bbb1a1b8e7fbac471cacf3cda&chksm=cf58e3def82f6ac8936d20b28bc3fdd579eddd5c93c7ea54cf952e35b802d70ea58b9449c790&scene=21#wechat_redirect)》
  • 2、《[让 nativePollOnce 不再排名第一钉钉 ANR 治理最佳实践](http://mp.weixin.qq.com/s?__biz=Mzg4MjE5OTI4Mw==&mid=2247499065&idx=1&sn=2dd9332fe0e49f947e38d5852527d245&chksm=cf58e2a5f82f6bb3b954bec4121c22f7223f79cdcdf750ac81e4426c7a137cf3b4ce852ba2ae&scene=21#wechat_redirect)》
  • 3、 钉钉 ANR 实战踩坑与经验总结 | 钉钉 ANR 治理最佳实践-阿里云开发者社区

Android ANR 系列 – 高爷

  1. Android App ANR 系列 1 :理解 Android ANR 设计思想
  2. Android App ANR 系列 2 :ANR 分析套路和关键 Log 介绍
  3. Android App ANR 系列 3 :ANR 案例分享
本文由作者按照 CC BY 4.0 进行授权