分析Android中的ANR

什么是ANR

Application Not Responding,意思就是程序未响应。

如果一个应用无法响应用户的输入,系统就会弹出一个ANR对话框,如下图所示:

这里写图片描述

用户可以自行选择继续等待,或者是停止当前程序。

ANR产生的条件

只有主线程才会产生ANR

在Activity中5s无响应,在BroadcastReceiver中10s无响应,在Service中20s无响应。

ANR产生的根本原因

1.CPU被占用导致主线程得不到CPU的资源,比如其他线程在进行频繁的读写操作。

2.在主线程中执行了耗时操作,比如在主线程中进行数据库操作或者访问网络。

分析ANR

导出ANR文件

ANR产生时, 系统会生成一个traces.txt的文件放在data/anr/下。可以通过adb命令将其导出到本地,例如:

adb pull /data/anr/traces.txt D:/tingshuonitiao

D:/tingshuonitiao是接收导出文件的本地文件夹。

分析ANR文件

1.耗时操作导致的ANR

----- pid 7710 at 2017-10-25 01:24:53 -----  //发生ANR的时间
Cmd line: com.tsnt.relax  //最新ANR发生的进程
Build fingerprint: 'Android/sdk_google_phone_x86/generic_x86:6.0/MASTER/3079352:userdebug/test-keys'
ABI: 'x86'
……
DALVIK THREADS (16):
……
"main" prio=5 tid=1 Sleeping
  | group="main" sCount=1 dsCount=0 obj=0x73d99f68 self=0xb4134500
  | sysTid=7710 nice=0 cgrp=default sched=0/0 handle=0xb77f7c00
  | state=S schedstat=( 0 0 0 ) utm=9 stm=18 core=1 HZ=100
  | stack=0xbf144000-0xbf146000 stackSize=8MB
  | held mutexes=
  at java.lang.Thread.sleep!(Native method)
  - sleeping on <0x07abb553> (a java.lang.Object)
  at java.lang.Thread.sleep(Thread.java:1031)
  - locked <0x07abb553> (a java.lang.Object)
  at java.lang.Thread.sleep(Thread.java:985)
  at android.os.SystemClock.sleep(SystemClock.java:120)
  at com.tsnt.relax.fragment.SceneFragment$3.onClick(SceneFragment.java:100)  //程序中出现ANR的地方
  at android.view.View.performClick(View.java:5198)
  at android.view.View$PerformClick.run(View.java:21147)
  at android.os.Handler.handleCallback(Handler.java:739)
  at android.os.Handler.dispatchMessage(Handler.java:95)
  at android.os.Looper.loop(Looper.java:148)
  at android.app.ActivityThread.main(ActivityThread.java:5417)
  at java.lang.reflect.Method.invoke!(Native method)
  at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:726)
  at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:616)

我们来看到程序中SceneFragment的部分代码:

        mTitle.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                SystemClock.sleep(6000);
                mPopupWindow.showAsDropDown(mToolbar);
            }
        });

原因很明显,在主线程中做了耗时操作

2.CPU被占用导致的ANR

这个时候你看到的trace信息可能会包含这样的信息:

Process:com.tsnt.relax
……
CPU usage from 3330ms to 814ms ago:
6% 178/system_server: 3.5% user + 1.4% kernel / faults: 86 minor 20 major
4.6% 2976/com.anly.githubapp: 0.7% user + 3.7% kernel /faults: 52 minor 19 major
0.9% 252/com.android.systemui: 0.9% user + 0% kernel
……
100%TOTAL: 5.9% user + 4.1% kernel + 89% iowait

其中最后一句表明:CPU占用达到100%,并且其中绝大数是被iowait即I/O操作占用了。

从各种CPU Usage信息中大概可以分析如下几种情况:

  • 如果某些其他进程的CPU占用较高,几乎占用了所有CPU资源,而发生ANR的进程CPU占用为0%或非常低,则认为CPU资源被占用,进程没有被分配足够的资源,从而发生了ANR。这种情况多数可以认为是系统状态的问题,并不是由本应用造成的。

  • 如果发生ANR的进程CPU占用较高,如到了80%或90%以上,则可以怀疑应用内一些代码不合理消耗掉了CPU资源,如出现了死循环或者后台有许多线程执行任务等等原因,这就要结合trace和ANR前后的log进一步分析了。

  • 如果CPU总用量不高,这有一定概率是由于主线程的操作耗时过长,或者是由于主进程被锁。

3.内存原因导致的ANR

其实内存原因也有可能会导致ANR,假设App可使用内存不多时我们加载一个大图片,就可能会产生ANR,这时trace信息可能是这样的:

Cmdline: android.process.acore
……
DALVIK THREADS:
"main"prio=5 tid=3 VMWAIT
|group="main" sCount=1 dsCount=0 s=N obj=0x40026240self=0xbda8
| sysTid=1815 nice=0 sched=0/0 cgrp=unknownhandle=-1344001376
atdalvik.system.VMRuntime.trackExternalAllocation(NativeMethod)
atandroid.graphics.Bitmap.nativeCreate(Native Method)
atandroid.graphics.Bitmap.createBitmap(Bitmap.java:468)
atandroid.view.View.buildDrawingCache(View.java:6324)
atandroid.view.View.getDrawingCache(View.java:6178)
……
MEMINFO in pid 1360 [android.process.acore] **
native dalvik other total
size: 17036 23111 N/A 40147
allocated: 16484 20675 N/A 37159
free: 296 2436 N/A 2732

可以看到free的内存已所剩无几,当然这种情况可能更多的是会产生OOM。

ANR机制的实现原理

请参考gityuan的博客

如何降低ANR的概率

1.主线程中不要进行耗时操作,把耗时操作放在子线程中。

2.尽量避免主线程的被锁的情况

3.避免应用内的I/O密集型操作

参考:
1.[转]Android ANR 分析解决方法
2.Android App优化之ANR详解
3.android ANR发生的原因总结和解决办法
4.Android ANR问题总结

猜你喜欢

转载自blog.csdn.net/sted_zxz/article/details/78337071