亲宝软件园·资讯

展开

解析Android ANR问题

huansky 人气:0

一、ANR介绍

ANR 由消息处理机制保证,Android 在系统层实现了一套精密的机制来发现 ANR,核心原理是消息调度和超时处理。ANR 机制主体实现在系统层,所有与 ANR 相关的消息,都会经过系统进程system_server调度,具体是ActivityManagerService服务,然后派发到应用进程完成对消息的实际处理,同时,系统进程设计了不同的超时限制来跟踪消息的处理。 一旦应用程序处理消息不当,超时限制就起作用了,它收集一些系统状态,譬如 CPU/IO 使用情况、进程函数调用栈 CallStack,(有些平台比如 MTK,还会打印相应的 Message 出来供调试分析),最后报告用户有进程无响应了( ANR 对话框)。

1.1、ANR类型

ANR 一般有三种类型:

1.1.1、KeyDispatchTimeout

这个主要是按键或触摸事件在特定时间内无响应,一般 Android 平台默认超时时间是 5s 会报 anr,不过有些平台会修改这个时间,比如 MTK 有些平台就是 8s 的超时时间。

这个超时时间可以在 ActivityManagerService.java 查看:

// M: ANR debug mechanism (Ori: 5*1000)
static final int KEY_DISPATCHING_TIMEOUT = 8*1000;

1.1.2、BroadcastTimeout

这类超时会有 ANR 提示框弹出,用户可以选择forceStop或者继续等待。

这个主要是 BroadcastRecevier 在规定时间无法处理完成。前台广播超时时间是 10s,后台广播超时是 60s,这类超时没有提示框弹出。

==> AMS.java

// How long we allow a receiver to run before giving up on it.
static final int BROADCAST_FG_TIMEOUT = 10*1000;
static final int BROADCAST_BG_TIMEOUT = 60*1000;

1.1.3、ServiceTimeout&nbsp

Service 在规定时间内无法处理完成操作,即会报出服务超时,这类 ANR 同样没有提示框出现。超时时间,前台 Service 是 20s,后台 Service 是 200s。

==> ActivityServices.java

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

三种 ANR 中只有第 1 种会显示系统提示对话框,因为用户正在做界面交互操作,如果长时间没有任何响应,会让用户怀疑设备死机了,大多数人此时会开始乱按,甚至拔出电池重启,给用户的体验肯定是非常糟糕的。

三种 ANR 发生时都会在 log 中输出错误信息,你会发现各个应用进程和系统进程的函数堆栈信息都输出到了一个 /data/anr/traces.txt 的文件中,这个文件是分析ANR原因的关键文件,同时在日志中还会看到当时的 CPU 使用率,这也是重要信息,在后面的章节会详细介绍如何利用它们分析ANR问题。

这三种ANR不是孤立的,有可能会相互影响。例如一个应用程序进程中同时有一个正在显示的 Activity 和一个正在处理消息的 BroadcastReceiver,它们都运行在这个进程的主线程中。如果 BR 的 onReceive 函数没有返回,此时用户点击屏幕,而 onReceive 超过 5 秒仍然没有返回,主线程无法处理用户输入事件,就会引起第 1 种 ANR。如果继续超过 10 秒没有返回,又会引起第 2 种ANR。

1.2、ANR发生的原理

对于ANR 发生基本原理如下:

二、ANR 分析示例

ANR 本质上是一个 Performance 问题,发生 ANR 的时候,如果问题可能出在 APK 自己身上,那么主要排查的方向是 apk 本身,分析看看是不是在 UI 线程做了耗时的操作?

个人认为,有一些 ANR 问题是很难分析的,比如 app 运行的时候由于当前系统底层的一些影响,导致当前 message 处理失败。这类问题往往没有规律,又难以重现。对于这类 ANR 问题,一般需要花费大量时间去了解系统的一些其他行为,而超出了 ANR 本身的范畴,这类问题就相当于一个警示信号,告诉你系统哪个地方出问题了。

2.1、查看 log 日志文件

搜索关键字 ANR :

04-17 17:15:23.817 E/ActivityManager(  187): ANR in com.da.android 

(com.da.android/com.text.my.app.ui.activities.ContentActivity)

上面这段文字的含义是 4月17日,17点15分23秒817ms时候发生了 ANR .其实一般还会带上 ANR 的原因,以及 CPU 使用信息,

这里我们写了一个 demo,比如在广播接收器的 onReceive 方法(广播接收器的 onReceive 方法默认在主线程执行)里面睡眠一段时间,如果这个时候主线程没有任何操作了,那么广播接收器可以正常执行完毕。不会造成 ANR 。如果我在发送广播之后然后点击多次返回按键,这个时候因为主线程睡眠了,无法响应,就会出现 ANR。

其对应的完整 log 信息如下 :

ANR in com.brotherd.broadcastdemo (com.brotherd.broadcastdemo/.MainActivity)

    PID: 6317

    Reason: Input dispatching timed out (Waiting to send key event because the focused window has not finished processing all of the input events that were previously delivered to it.  Outbound queue length: 0.  Wait queue length: 2.)

    //    Load: 38.14 / 37.82 / 37.45  // ANR 发生前 cpu 使用信息

    CPU usage from 45678ms to 0ms ago (2020-10-27 08:39:06.256 to 2020-10-27 08:39:51.933):

      15% 1265/system_server: 12% user + 3.5% kernel / faults: 6531 minor 16 major

      13% 23112/com.android.systemui: 9.7% user + 3.5% kernel / faults: 6033 minor 103 major // minor major 表示页的错误

      4.7% 578/surfaceflinger: 2.6% user + 2% kernel / faults: 469 minor 2 major

      4.4% 673/hwpged: 0% user + 4.3% kernel / faults: 29 minor

      2.3% 549/android.hardware.graphics.composer@2.2-service: 1.1% user + 1.1% kernel / faults: 149 minor

      0.1% 2246/com.huawei.android.pushagent: 0% user + 0% kernel / faults: 2583 minor 22 major

      1.5% 461/logd: 0.8% user + 0.6% kernel / faults: 52 minor

      1.5% 2077/com.huawei.powergenie: 0.7% user + 0.8% kernel / faults: 843 minor 2 major

      0.5% 17384/com.android.settings: 0.4% user + 0.1% kernel / faults: 5419 minor 73 major

      1.3% 259/mmc-cmdqd/0: 0% user + 1.3% kernel

      1% 3888/kworker/u16:0: 0% user + 1% kernel / faults: 1 minor

      0.9% 18949/android.process.media: 0.5% user + 0.3% kernel / faults: 1408 minor 8 major

      0.8% 148/kswapd0: 0% user + 0.8% kernel

      0.5% 657/dubaid: 0.3% user + 0.2% kernel / faults: 139 minor

      0.5% 2105/com.huawei.android.launcher: 0.4% user + 0.1% kernel / faults: 159 minor 1 major

      0.5% 2366/com.huawei.systemmanager:service: 0.3% user + 0.1% kernel / faults: 374 minor

      0.5% 922/hisi_frw/0: 0% user + 0.5% kernel

      0.4% 3951/kworker/u16:8: 0% user + 0.4% kernel

      0.4% 27040/com.huawei.appmarket: 0.3% user + 0.1% kernel / faults: 350 minor 2 major

      0.4% 238/kworker/0:1H: 0% user + 0.4% kernel

      0.4% 2028/com.huawei.hiview: 0.1% user + 0.2% kernel / faults: 88 minor

      0.3% 577/lmkd: 0% user + 0.3% kernel

      0.3% 677/vendor.huawei.hardware.sensors@1.0-service: 0% user + 0.3% kernel / faults: 19 minor

      0.3% 3715/kworker/u16:1: 0% user + 0.3% kernel

      0.3% 29718/com.huawei.hidisk: 0.2% user + 0.1% kernel / faults: 404 minor 1 major

      0.1% 527/zygote64: 0% user + 0.1% kernel / faults: 541 minor

      0.3% 3954/kworker/u16:10: 0% user + 0.3% kernel

      0.3% 581/powerlogd: 0.2% user + 0.1% kernel / faults: 25 minor

      0.3% 3952/kworker/u16:9: 0% user + 0.3% kernel

      0% 23351/com.huawei.hwid: 0% user + 0% kernel / faults: 854 minor 10 major

      0.1% 3945/kworker/u16:6: 0% user + 0.1% kernel

      0.2% 24447/kworker/u17:0: 0% user + 0.2% kernel

      0.2% 548/android.hardware.graphics.allocator@2.0-service: 0% user + 0.2% kernel / faults: 47 minor

      0.2% 2042/com.huawei.systemserver: 0.1% user + 0.1% kernel / faults: 95 minor

      0.2% 426/oeminfo_nvm_server: 0% user + 0.1% kernel / faults: 79 minor 205 major

      0.2% 4187/kworker/u17:1: 0% user + 0.2% kernel

      0.2% 462/servicemanager: 0% user + 0.1% kernel / faults: 20 minor

      0.2% 3633/kworker/u16:2: 0% user + 0.2% kernel

      0.2% 5290/com.huawei.health:DaemonService: 0.1% user + 0% kernel / faults: 77 minor

      0.1% 7/rcu_preempt: 0% user + 0.1% kernel

      0.1% 93/sys_heap: 0% user + 0.1% kernel

      0.1% 949/hisi_hcc: 0% user + 0.1% kernel

      0.1% 637/hisecd: 0% user + 0.1% kernel / faults: 66 minor

      0.1% 2097/com.android.phone: 0% user + 0.1% kernel / faults: 97 minor

      0.1% 3441/kworker/u16:5: 0% user + 0.1% kernel

      0.1% 526/netd: 0% user + 0.1% kernel / faults: 142 minor 2 major

      0.1% 2063/com.huawei.HwOPServer: 0% user + 0% kernel / faults: 168 minor

      0.1% 1//init: 0% user + 0% kernel / faults: 263 minor 5 major

      0% 465/vendor.huawei.hardware.hwfactoryinterface@1.1-service: 0% user + 0% kernel / faults: 113 minor 5 major

      0.1% 3944/kworker/u16:4: 0% user + 0.1% kernel

      0.1% 655/displayengineserver: 0% user + 0% kernel / faults: 21 minor

      0.1% 918/oal_gpio_rx_dat: 0% user + 0.1% kernel

      0.1% 3955/com.eg.android.AlipayGphone:push: 0% user + 0% kernel / faults: 132 minor

      0.1% 26941/com.huawe// anr 发生后 cpu 使用情况

  2020-10-27 08:39:53.753 1265-1285/? E/ActivityManager: CPU usage from 51ms to 493ms later (2020-10-27 08:39:51.984 to 2020-10-27 08:39:52.426) with 99% awake:

      48% 1265/system_server: 10% user + 37% kernel

      45% 1285/ActivityManager: 8% user + 37% kernel

      4.9% 578/surfaceflinger: 4.9% user + 0% kernel / faults: 5 minor

      2.4% 578/surfaceflinger: 2.4% user + 0% kernel

      3.1% 3952/kworker/u16:9: 0% user + 3.1% kernel

      3.7% 23112/com.android.systemui: 3.7% user + 0% kernel

      9.7% TOTAL: 5.2% user + 4.4% kernel  // 可以从 total 里面看到整个 cpu 的情况

首先看前面几行:

ANR in com.brotherd.broadcastdemo (com.brotherd.broadcastdemo/.MainActivity)

PID: 6317

Reason: Input dispatching timed out (Waiting to send key event because the focused window has not finished processing all of the input events that were previously delivered to it.  Outbound queue length: 0.  Wait queue length: 2.)

这几行表明了 ANR 发生所处的 activity,进程 ID,以及 ANR 原因(input 事件分发超时);

ANRManager会打印出anr 前后的 cpu 使用情况,这个可以反映出当时系统的Performance状态:

那么这个时候,我们就要看看 anr 发生的时候,主线程在做什么了。

2.2、Trace 文件(data/anr/traces.txt)

log 文件只是告诉你 ANR 发生时间,但是并具体详细的信息,这时候就得查看 trace 文件(App 的进程发生 ANR 时,系统让活跃的 Top 进程都进行了一下 dump,进程中的各种Thread 就都 dump 到这个 trace 文件里了,所以 trace 文件中包含了每一条线程的运行时状态)。

对于当前这个例子的堆栈信息如下:

在上面的信息中,有这样一行

at com.brotherd.broadcastdemo.MyReceiver.onReceive(MyReceiver.java:23)

这就是造成 ANR 的根本原因。在 MyReceiver 类的 onReceive 方法中调用了 Thread 的 sleep 方法导致主线程阻塞,造成 ANR。

然后我们看一些字段的信息。

"main" prio=5 tid=1 Sleeping

  | group="main" sCount=1 dsCount=0 flags=1 obj=0x7682ab30 self=0x7bd3815c00

  | sysTid=6317 nice=-10 cgrp=default sched=0/0 handle=0x7c59fc8548

  | state=S schedstat=( 1009468742 32888019 224 ) utm=91 stm=9 core=4 HZ=100

  | stack=0x7ff27e1000-0x7ff27e3000 stackSize=8MB

  | held mutexes=

这些字段含义如下线程名:

线程的上下文信息:

线程的堆栈信息:

此外,线程的状态很重要,了解这些状态的意义对分析ANR的原因是有帮助的,总结如下:

Thread.java 中的状态和 Thread.cpp 中的状态是有对应关系的。可以看到前者更加概括,也比较容易理解,面向 Java 的使用者;而后者更详细,面向虚拟机内部的环境。traces.txt 中显示的线程状态都是 Thread.cpp 中定义的。另外,所有的线程都是遵循 POSIX 标准的本地线程。

线程状态示例:

2.3、死锁demo

我们来看一个死锁的例子:

"PowerManagerService" prio=5 tid=24 MONITOR

| group="main" sCount=1 dsCount=0 obj=0x41dd0eb0 self=0x5241b218

| sysTid=567 nice=0 sched=0/0 cgrp=apps handle=1380038664

| state=S schedstat=( 6682116007 11324451214 33313 ) utm=450 stm=219 core=1

at com.android.server.am.ActivityManagerService.broadcastIntent(ActivityManagerService.java:~13045)

- waiting to lock <0x41a874a0> (a com.android.server.am.ActivityManagerService) held by tid=12 (android.server.ServerThread)

at android.app.ContextImpl.sendBroadcast(ContextImpl.java:1144)

at com.android.server.power.PowerManagerService$DisplayBlankerImpl.unblankAllDisplays(PowerManagerService.java:3442)

at com.android.server.power.DisplayPowerState$PhotonicModulator$1.run(DisplayPowerState.java:456)

at android.os.Handler.handleCallback(Handler.java:800)

at android.os.Handler.dispatchMessage(Handler.java:100)

at android.os.Looper.loop(Looper.java:194)

at android.os.HandlerThread.run(HandlerThread.java:60)

tid=24 线程在等待一个 <0x41a874a0> 的锁,而这个锁被 tid=12 的线程占用了,我们来看看 tid=12:

"android.server.ServerThread" prio=5 tid=12 MONITOR| group="main" sCount=1 dsCount=0 obj=0x41a76178 self=0x507837a8

| sysTid=545 nice=-2 sched=0/0 cgrp=apps handle=1349936616

| state=S schedstat=( 15368096286 21707846934 69485 ) utm=1226 stm=310 core=0

at com.android.server.power.PowerManagerService.isScreenOnInternal(PowerManagerService.java:~2529)

- waiting to lock <0x41a7e2e8> (a java.lang.Object) held by tid=85 (Binder_B)

at com.android.server.power.PowerManagerService.isScreenOn(PowerManagerService.java:2522)

at com.android.server.wm.WindowManagerService.sendScreenStatusToClientsLocked(WindowManagerService.java:7749)

at com.android.server.wm.WindowManagerService.setEventDispatching(WindowManagerService.java:7628)

at com.android.server.am.ActivityManagerService.updateEventDispatchingLocked(ActivityManagerService.java:8083)

at com.android.server.am.ActivityManagerService.wakingUp(ActivityManagerService.java:8077)

tid=12 线程在等待 <0x41a7e2e8> 的锁,而这个锁被 tid=85 的线程占用了,我们来看看 tid=85:

"Binder_B" prio=5 tid=85 MONITOR | group="main" sCount=1 dsCount=0 obj=0x42744770 self=0x58329e88

| sysTid=3700 nice=-20 sched=0/0 cgrp=apps handle=1471424616

| state=S schedstat=( 1663727513 2044643318 6806 ) utm=132 stm=34 core=1

at com.android.server.power.PowerManagerService$DisplayBlankerImpl.toString(PowerManagerService.java:~3449)

- waiting to lock <0x41a7e420> (a com.android.server.power.PowerManagerService$DisplayBlankerImpl) held by tid=24 (PowerManagerService)

值得注意的是,trace里面一般会包含时间,尽量分析跟anr时间相近的trace,避免受到其他干扰。tid=85线程在等待<0x41a7e420>的锁释放,而这个锁被tid=24占用了,所以就发送了死锁。那么这种情况下我们就需要找到发生死锁的source code,进行分析并修改。

三、其他分析思路

有时候我们 log 也分析了,traces 也分析了,还是很难分析出 ANR 的原因,那么我们可能就需要尝试从其他方面分析了:

从main log 里面找 anr 发生时间前 8s,看看 app 的 main thread 在做什么操作?是否有异常的地方,一般 process 的 pid 会等于主线程 id。

大范围搜寻 log,看是否 anr 发生之前,哪里有发生 crash?是否有可能引起 anr。

看情况抓取平台的一些辅助信息,包括但不局限于:

四、减少发生 ANR 概率

加载全部内容

相关教程
猜你喜欢
用户评论