宁波北京网站建设,分类信息网站有哪些,平阳门户网站建设,江苏联运建设有限公司网站工作之余#xff0c;对之前学习到的和结合自己项目过程中的遇到的问题经验做一些总结#xff0c;下面讲一讲Android开发过程中遇到的ANR的问题#xff0c;做一下整理
一、概述
解决ANR一直是Android 开发者需要掌握的重要技巧#xff0c;一般从三个方面着手。 开发阶段对之前学习到的和结合自己项目过程中的遇到的问题经验做一些总结下面讲一讲Android开发过程中遇到的ANR的问题做一下整理
一、概述
解决ANR一直是Android 开发者需要掌握的重要技巧一般从三个方面着手。 开发阶段通过工具检查各个方法的耗时卡顿情况发现一处修改一处。 线上阶段这个阶段主要依靠监控工具发现ANR并上报比如matrix。 分析阶段如果线上用户发生ANR并且你获取了一份日志这就涉及了本文要分享的内容——ANR日志分析技巧 二、ANR产生机制 ANR——应用无响应Activity是5秒BroadCastReceiver是10秒Service是20秒。 这句话说的很笼统要想深入分析定位ANR需要知道更多知识点一般来说ANR按产生机制分为4类
①输入事件超时(5s)
②广播类型超时前台15s后台60s
③服务超时前台20s后台200s
④ContentProvider 类型
2.1 输入事件超时(5s)
InputEvent Timeout
a.InputDispatcher发送key事件给 对应的进程的 Focused Window 对应的window不存在、处于暂停态、或通道(input channel)占满、通道未注册、通道异常、或5s内没有处理完一个事件就会发生ANRb.InputDispatcher发送MotionEvent事件有个例外之处当对应Touched Window的 input waitQueue中有超过0.5s的事件inputDispatcher会暂停该事件并等待5s如果仍旧没有收到window的‘finish’事件则触发ANRc.下一个事件到达发现有一个超时事件才会触发ANR 2.2 广播类型超时前台15s后台60s
BroadcastReceiver Timeout
a.静态注册的广播和有序广播会ANR动态注册的非有序广播并不会ANRb.广播发送时会判断该进程是否存在不存在则创建创建进程的耗时也算在超时时间里c.只有当进程存在前台显示的Activity才会弹出ANR对话框否则会直接杀掉当前进程d.当onReceive执行超过阈值前台15s后台60s将产生ANRe.如何发送前台广播Intent.addFlags(Intent.FLAG_RECEIVER_FOREGROUND)
2.3 服务超时前台20s后台200s
Service Timeout
a.Service的以下方法都会触发ANRonCreate(),onStartCommand(), onStart(), onBind(), onRebind(), onTaskRemoved(), onUnbind(),onDestroy().b.前台Service超时时间为20s后台Service超时时间为200sc.如何区分前台、后台执行————当前APP处于用户态此时执行的Service则为前台执行。d.用户态有前台activity、有前台广播在执行、有foreground service执行
2.4 ContentProvider 类型
a.ContentProvider创建发布超时并不会ANRb.使用ContentProviderclient来访问ContentProverder可以自主选择触发ANR超时时间自己定client.setDetectNotResponding(PROVIDER_ANR_TIMEOUT);
三、导致ANR的原因
很多开发者认为那就是耗时操作导致ANR全部是app应用层的问题。实际上线上环境大部分ANR由系统原因导致。
①应用层导致ANR耗时操作
② 系统导致ANR
3.1 应用层导致ANR耗时操作
a. 函数阻塞如死循环、主线程IO、处理大数据b. 锁出错主线程等待子线程的锁c. 内存紧张系统分配给一个应用的内存是有上限的长期处于内存紧张会导致频繁内存交换进而导致应用的一些操作超时
3.2 系统导致ANR
a. CPU被抢占一般来说前台在玩游戏可能会导致你的后台广播被抢占CPUb. 系统服务无法及时响应比如获取系统联系人等系统的服务都是Binder机制服务能力也是有限的有可能系统服务长时间不响应导致ANRc. 其他应用占用的大量内存 四、分析日志
发生ANR的时候系统会产生一份anr日志文件手机的/data/anr 目录下文件名称可能各厂商不一样业内大多称呼为trace文件内含如下几项重要信息。
① CPU 负载
②内存信息
③ 堆栈消息
4.1 CPU 负载
Load: 2.62 / 2.55 / 2.25CPU usage from 0ms to 1987ms later (2020-03-10 08:31:55.169 to 2020-03-10 08:32:17.156): 41% 2080/system_server: 28% user 12% kernel / faults: 76445 minor 180 major 26% 9378/com.xiaomi.store: 20% user 6.8% kernel / faults: 68408 minor 68 major........省略N行.....66% TOTAL: 20% user 15% kernel 28% iowait 0.7% irq 0.7% softirq 如上所示
第一行1、5、15 分钟内正在使用和等待使用CPU 的活动进程的平均数第二行表明负载信息抓取在ANR发生之后的0~1987ms。同时也指明了ANR的时间点2020-03-10 08:31:55.169中间部分各个进程占用的CPU的详细情况最后一行各个进程合计占用的CPU信息。
名词解释
a. user:用户态,kernel:内核态b. faults:内存缺页minor——轻微的major——重度需要从磁盘拿数据c. iowait:IO使用等待占比d. irq:硬中断softirq:软中断
注意 iowait占比很高意味着有很大可能是io耗时导致ANR具体进一步查看有没有进程faults major比较多。 单进程CPU的负载并不是以100%为上限而是有几个核就有百分之几百如4核上限为400%。 4.2 内存信息
Total number of allocations 476778 进程创建到现在一共创建了多少对象Total bytes allocated 52MB 进程创建到现在一共申请了多少内存Total bytes freed 52MB 进程创建到现在一共释放了多少内存Free memory 777KB 不扩展堆的情况下可用的内存Free memory until GC 777KB GC前的可用内存Free memory until OOME 383MB OOM之前的可用内存Total memory 当前总内存已用可用Max memory 384MB 进程最多能申请的内存
从含义可以得出结论Free memory until OOME 的值很小的时候已经处于内存紧张状态。应用可能是占用了过多内存。
ps:如果ANR时间点前后日志里有打印onTrimMemory也可以作为内存紧张的一个参考判断
4.3 堆栈消息
堆栈信息是最重要的一个信息展示了ANR发生的进程当前所有线程的状态。
suspend all histogram: Sum: 2.834s 99% C.I. 5.738us-7145.919us Avg: 607.155us Max: 41543usDALVIK THREADS (248):main prio5 tid1 Native | groupmain sCount1 dsCount0 flags1 obj0x74b17080 self0x7bb7a14c00 | sysTid2080 nice-2 cgrpdefault sched0/0 handle0x7c3e82b548 | stateS schedstat( 757205342094 583547320723 2145008 ) utm52002 stm23718 core5 HZ100 | stack0x7fdc995000-0x7fdc997000 stackSize8MB | held mutexes kernel: __switch_to0xb0/0xbc kernel: SyS_epoll_wait0x288/0x364 kernel: SyS_epoll_pwait0xb0/0x124 kernel: cpu_switch_to0x38c/0x2258 native: #00 pc 000000000007cd8c /system/lib64/libc.so (__epoll_pwait8) native: #01 pc 0000000000014d48 /system/lib64/libutils.so (android::Looper::pollInner(int)148) native: #02 pc 0000000000014c18 /system/lib64/libutils.so (android::Looper::pollOnce(int, int*, int*, void**)60) native: #03 pc 0000000000127474 /system/lib64/libandroid_runtime.so (android::android_os_MessageQueue_nativePollOnce(_JNIEnv*, _jobject*, long, int)44) at android.os.MessageQueue.nativePollOnce(Native method) at android.os.MessageQueue.next(MessageQueue.java:330) at android.os.Looper.loop(Looper.java:169) at com.android.server.SystemServer.run(SystemServer.java:508) at com.android.server.SystemServer.main(SystemServer.java:340) at java.lang.reflect.Method.invoke(Native method) at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:536) at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:856 ........省略N行.... OkHttp ConnectionPool daemon prio5 tid251 TimedWaiting | groupmain sCount1 dsCount0 flags1 obj0x13daea90 self0x7bad32b400 | sysTid29998 nice0 cgrpdefault sched0/0 handle0x7b7d2614f0 | stateS schedstat( 951407 137448 11 ) utm0 stm0 core3 HZ100 | stack0x7b7d15e000-0x7b7d160000 stackSize1041KB | held mutexes at java.lang.Object.wait(Native method) - waiting on 0x05e5732e (a com.android.okhttp.ConnectionPool) at com.android.okhttp.ConnectionPool$1.run(ConnectionPool.java:103) - locked 0x05e5732e (a com.android.okhttp.ConnectionPool) at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1167) at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:641) at java.lang.Thread.run(Thread.java:764)
如上日志所示本文截图了两个线程信息一个是主线程main它的状态是native。
另一个是OkHttp ConnectionPool它的状态是TimeWaiting。众所周知教科书上说线程状态有5种新建、就绪、执行、阻塞、死亡。而Java中的线程状态有6种6种状态都定义在java.lang.Thread.State中
问题来了上述main线程的native是什么状态哪来的其实trace文件中的状态是是CPP代码中定义的状态
由此可知main函数的native状态是正在执行JNI函数。堆栈信息是我们分析ANR的第一个重要的信息一般来说 main线程处于 BLOCK、WAITING、TIMEWAITING状态那基本上是函数阻塞导致ANR 如果main线程无异常则应该排查CPU负载和内存环境。 五、典型案例分析 5.1 主线程执行耗时操作
main prio5 tid1 Runnable | groupmain sCount0 dsCount0 flags0 obj0x72deb848 self0x7748c10800 | sysTid8968 nice-10 cgrpdefault sched0/0 handle0x77cfa75ed0 | stateR schedstat( 24783612979 48520902 756 ) utm2473 stm5 core5 HZ100 | stack0x7fce68b000-0x7fce68d000 stackSize8192KB | held mutexes mutator lock(shared held) at com.example.test.MainActivity$onCreate$2.onClick(MainActivity.kt:20)——关键行 at android.view.View.performClick(View.java:7187) at android.view.View.performClickInternal(View.java:7164) at android.view.View.access$3500(View.java:813) at android.view.View$PerformClick.run(View.java:27640) at android.os.Handler.handleCallback(Handler.java:883) at android.os.Handler.dispatchMessage(Handler.java:100) at android.os.Looper.loop(Looper.java:230) at android.app.ActivityThread.main(ActivityThread.java:7725) at java.lang.reflect.Method.invoke(Native method) at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:526) at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1034)
上述日志表明主线程正处于执行状态看堆栈信息可知不是处于空闲状态发生ANR是因为一处click监听函数里执行了耗时操作。
5.2 主线程被锁阻塞
main prio5 tid1 Blocked | groupmain sCount1 dsCount0 flags1 obj0x72deb848 self0x7748c10800 | sysTid22838 nice-10 cgrpdefault sched0/0 handle0x77cfa75ed0 | stateS schedstat( 390366023 28399376 279 ) utm34 stm5 core1 HZ100 | stack0x7fce68b000-0x7fce68d000 stackSize8192KB | held mutexes at com.example.test.MainActivity$onCreate$1.onClick(MainActivity.kt:15) - waiting to lock 0x01aed1da (a java.lang.Object) held by thread 3 ——————关键行 at android.view.View.performClick(View.java:7187) at android.view.View.performClickInternal(View.java:7164) at android.view.View.access$3500(View.java:813) at android.view.View$PerformClick.run(View.java:27640) at android.os.Handler.handleCallback(Handler.java:883) at android.os.Handler.dispatchMessage(Handler.java:100) at android.os.Looper.loop(Looper.java:230) at android.app.ActivityThread.main(ActivityThread.java:7725) at java.lang.reflect.Method.invoke(Native method) at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:526) at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1034) ........省略N行..... WQW TEST prio5 tid3 TimeWating | groupmain sCount1 dsCount0 flags1 obj0x12c44230 self0x772f0ec000 | sysTid22938 nice0 cgrpdefault sched0/0 handle0x77391fbd50 | stateS schedstat( 274896 0 1 ) utm0 stm0 core1 HZ100 | stack0x77390f9000-0x77390fb000 stackSize1039KB | held mutexes at java.lang.Thread.sleep(Native method) - sleeping on 0x043831a6 (a java.lang.Object) at java.lang.Thread.sleep(Thread.java:440) - locked 0x043831a6 (a java.lang.Object) at java.lang.Thread.sleep(Thread.java:356) at com.example.test.MainActivity$onCreate$2$thread$1.run(MainActivity.kt:22) - locked 0x01aed1da (a java.lang.Object)————————————————————关键行 at java.lang.Thread.run(Thread.java:919) 这是一个典型的主线程被锁阻塞的例子
waiting to lock 0x01aed1da (a java.lang.Object) held by thread 3 其中等待的锁是0x01aed1da这个锁的持有者是线程 3。进一步搜索 “tid3” 找到线程3 发现它正在TimeWating。
那么ANR的原因找到了线程3持有了一把锁并且自身长时间不释放主线程等待这把锁发生超时。在线上环境中常见因锁而ANR的场景是SharePreference写入
5.3 CPU被抢占
CPU usage from 0ms to 10625ms later (2020-03-09 14:38:31.633 to 2020-03-09 14:38:42.257): 543% 2045/com.alibaba.android.rimet: 54% user 89% kernel / faults: 4608 minor 1 major ————关键行 99% 674/android.hardware.camera.provider2.4-service: 81% user 18% kernel / faults: 403 minor 24% 32589/com.wang.test: 22% user 1.4% kernel / faults: 7432 minor 1 major ........省略N行.....
如上日志第二行是钉钉的进程占据CPU高达543%抢占了大部分CPU资源因而导致发生ANR。
5.4 内存紧张导致ANR
如果有一份日志CPU和堆栈都很正常不贴出来了仍旧发生ANR考虑是内存紧张。 系统日志里搜索am_meminfo 这个没有搜索到。再次搜索onTrimMemory果然发现了很多条记录
10-31 22:37:19.749 20733 20733 E Runtime : onTrimMemory level:80,pid:com.xxx.xxx:Launcher010-31 22:37:33.458 20733 20733 E Runtime : onTrimMemory level:80,pid:com.xxx.xxx:Launcher010-31 22:38:00.153 20733 20733 E Runtime : onTrimMemory level:80,pid:com.xxx.xxx:Launcher010-31 22:38:58.731 20733 20733 E Runtime : onTrimMemory level:80,pid:com.xxx.xxx:Launcher010-31 22:39:02.816 20733 20733 E Runtime : onTrimMemory level:80,pid:com.xxx.xxx:Launcher0 可以看出在发生ANR的时间点前后内存都处于紧张状态level等级是80
可知80这个等级是很严重的应用马上就要被杀死被杀死的这个应用从名字可以看出来是桌面连桌面都快要被杀死那普通应用能好到哪里去呢
一般来说发生内存紧张会导致多个应用发生ANR所以在日志中如果发现有多个应用一起ANR了可以初步判定此ANR与你的应用无关。
5.5 系统服务超时导致ANR
系统服务超时一般会包含BinderProxy.transactNative关键字请看如下日志
main prio5 tid1 Native | groupmain sCount1 dsCount0 flags1 obj0x727851e8 self0x78d7060e00 | sysTid4894 nice0 cgrpdefault sched0/0 handle0x795cc1e9a8 | stateS schedstat( 8292806752 1621087524 7167 ) utm707 stm122 core5 HZ100 | stack0x7febb64000-0x7febb66000 stackSize8MB | held mutexes kernel: __switch_to0x90/0xc4 kernel: binder_thread_read0xbd8/0x144c kernel: binder_ioctl_write_read.constprop.580x20c/0x348 kernel: binder_ioctl0x5d4/0x88c kernel: do_vfs_ioctl0xb8/0xb1c kernel: SyS_ioctl0x84/0x98 kernel: cpu_switch_to0x34c/0x22c0 native: #00 pc 000000000007a2ac /system/lib64/libc.so (__ioctl4) native: #01 pc 00000000000276ec /system/lib64/libc.so (ioctl132) native: #02 pc 00000000000557d4 /system/lib64/libbinder.so (android::IPCThreadState::talkWithDriver(bool)252) native: #03 pc 0000000000056494 /system/lib64/libbinder.so (android::IPCThreadState::waitForResponse(android::Parcel*, int*)60) native: #04 pc 00000000000562d0 /system/lib64/libbinder.so (android::IPCThreadState::transact(int, unsigned int, android::Parcel const, android::Parcel*, unsigned int)216) native: #05 pc 000000000004ce1c /system/lib64/libbinder.so (android::BpBinder::transact(unsigned int, android::Parcel const, android::Parcel*, unsigned int)72) native: #06 pc 00000000001281c8 /system/lib64/libandroid_runtime.so (???) native: #07 pc 0000000000947ed4 /system/framework/arm64/boot-framework.oat (Java_android_os_BinderProxy_transactNative__ILandroid_os_Parcel_2Landroid_os_Parcel_2I196) at android.os.BinderProxy.transactNative(Native method) ————————————————关键行 at android.os.BinderProxy.transact(Binder.java:804) at android.net.IConnectivityManager$Stub$Proxy.getActiveNetworkInfo(IConnectivityManager.java:1204)—关键行 at android.net.ConnectivityManager.getActiveNetworkInfo(ConnectivityManager.java:800) at com.xiaomi.NetworkUtils.getNetworkInfo(NetworkUtils.java:2) at com.xiaomi.frameworkbase.utils.NetworkUtils.getNetWorkType(NetworkUtils.java:1)
从堆栈可以看出获取网络信息发生了ANRgetActiveNetworkInfo。
前文有讲过系统的服务都是Binder机制16个线程服务能力也是有限的有可能系统服务长时间不响应导致ANR。如果其他应用占用了所有Binder线程那么当前应用只能等待。
可进一步搜索blockUntilThreadAvailable关键字
at android.os.Binder.blockUntilThreadAvailable(Native method)
如果有发现某个线程的堆栈包含此字样可进一步看其堆栈确定是调用了什么系统服务。此类ANR也是属于系统环境的问题如果某类型机器上频繁发生此问题应用层可以考虑规避策略。 参考链接ANR日志分析全面解析_android anr 日志-CSDN博客
后面分享使用 Matrix工具去监控app卡顿