一、ANR介绍

ANR-application not response,使用无呼应,使用开发者一般是重视自己的APP进程有没有呈现,体系开发者会重视当时体系运转起来后全体上一切的APP进程有没有呈现ANR,从这句话可以知道,只要使用进程的主线程(UI线程)超时或许导致ANR,咱们从体系全体角度来论述问题,趁便也能包括使用呈现的ANR bug。

从现象来看,ANR呈现时,会弹出一个使用无呼应的弹框,当然也或许不弹,一是因为现在的设备厂商觉得这弹框难看,给去掉了,二是有些ANR本身就只要log记载,不会呈现弹框。一向听到的便是不能在主线程做耗时操作,否则或许造成ANR,这儿首先要清晰:主线程做了许多耗时操作后,还得有事情发生,按键也好、点击触摸的touch事情也好,这样才会呈现,全体上可以归成几类:

  • 1、使用进程主线程导致,这儿首要表现的便是activity-5s超时(会弹框),broadcast-10s超过,service-20s超时,三类超时后,再发生事情,这个事情无法传递给焦点窗口就会上报ANR
  • 2、CPU耗尽
  • 3、内存耗尽

简单来说,ANR便是发生的事情长期没时机履行,Google就设计了这样一种提示用户的交互流程,避免用户认为设备手机坏成板砖了,假如没有这样的提醒,用户或许会说,这什么破手机、破体系,进而扣电池砸手机,有个弹框就能把问题聚焦到详细的某个APP上。因而CPU或许内存耗尽,它显然也会导致ANR的呈现,刚好此刻的APP上某个控件点击事情他便是无法履行(CPU功耗高,UI线程一向抢不到履行的时刻片)而超时。内存耗尽也是相同的逻辑。可以看到,呈现ANR的进程中,有时分是本身进程里导致的,也有是受了池鱼之灾,其他进程占用CPU太多,导致自己彻底无法履行而呈现。

二、ANR怎样下手剖析

上面说了ANR是怎样一回事,以及ANR为什么会发生,接着咱们来剖析,呈现ANR应该怎样检查,怎样追踪问题是哪里发生。呈现ANR时(怎样让设备呈现ANR日志,最简单的方法是shell指令:adb shell kill 3 pid),” data/anr/ “目录下会发生相似”anr_2022-03-29-15-45-53-142″这样命名的文件(老版别时ANR发生的日志叫” data/anr/ traces.txt”,老版别只会记载最近一次ANR发生时的体系仓库,高版别改了,默认可以存储64个文件,每发生一次ANR,就会生成一个文件),因而在发生ANR时取出日志,咱们可以人为的制作一次ANR,很简单-activity的oncreate()周期里来个死循环,然后跳转这个activity后,点击屏幕,OK-anr_2022-03-29-15-45-53-142文件发生,下面来看怎样剖析ANR日志:

2.1、Traces文件详细剖析

—– pid 30675 at 2022-03-29 15:45:53 —– Cmd line: com.android.test

最初便是发生ANR的进程pid、时刻、以及进程名,从最初的信息,体系开发人员就能把这个bug分给详细某个使用担任人了,当然有些时分发生ANR的进程或许没问题,是被他人给耽误了,所以接着往下看

Build fingerprint: ‘//*:10/QKQ1.210910.001/75:userdebug/release-keys’
ABI: ‘arm64Build type: optimized
Zygote loaded classes=9126 post zygote classes=318
Dumping registered class loaders
#0 dalvik.system.PathClassLoader: [], parent #1
#1 java.lang.BootClassLoader: [], no parent
#2 dalvik.system.PathClassLoader: [/system/framework/tcmclient.jar], parent #0
#3 dalvik.system.PathClassLoader: [], parent #0
#4 dalvik.system.PathClassLoader: [/data/app/com.android.test-o0YNE5m6e2qP5cEIcS4y8A==/base.apk:/data/app/com.android.test-o0YNE5m6e2qP5cEIcS4y8A==/base.apk!classes2.dex], parent #1
Done dumping class loaders
Intern table: 33006 strong; 438 weak
JNI: CheckJNI is on; globals=618 (plus 36 weak)
Libraries: /data/app/com.android.test-o0YNE5m6e2qP5cEIcS4y8A==/lib/arm64/libSRS.so libandroid.so libcompiler_rt.so libjavacore.so libjavacrypto.so libjnigraphics.so libmedia_jni.so libopenjdk.so libqti_performance.so libsoundpool.so libwebviewchromium_loader.so (11)
Heap: 24% free, 1468KB/1948KB; 40045 objects

这是发生ANR的使用进程一些信息,假如是自己的使用看着会觉得很熟悉,接着来

Dumping cumulative Gc timings
Start Dumping histograms for 1 iterations for concurrent copying
ProcessMarkStack: Sum: 12.241ms 99% C.I. 12.241ms-12.241ms Avg: 12.241ms Max: 12.241ms
ScanImmuneSpaces: Sum: 2.466ms 99% C.I. 2.466ms-2.466ms Avg: 2.466ms Max: 2.466ms
VisitConcurrentRoots: Sum: 1.220ms 99% C.I. 1.220ms-1.220ms Avg: 1.220ms Max: 1.220ms
ClearFromSpace: Sum: 850us 99% C.I. 850us-850us Avg: 850us Max: 850us
ThreadListFlip: Sum: 700us 99% C.I. 700us-700us Avg: 700us Max: 700us
FlipThreadRoots: Sum: 389us 99% C.I. 389us-389us Avg: 389us Max: 389us
ScanCardsForSpace: Sum: 358us 99% C.I. 358us-358us Avg: 358us Max: 358us
InitializePhase: Sum: 293us 99% C.I. 293us-293us Avg: 293us Max: 293us
GrayAllDirtyImmuneObjects: Sum: 174us 99% C.I. 174us-174us Avg: 174us Max: 174us
FlipOtherThreads: Sum: 142us 99% C.I. 142us-142us Avg: 142us Max: 142us
SweepSystemWeaks: Sum: 131us 99% C.I. 131us-131us Avg: 131us Max: 131us
EnqueueFinalizerReferences: Sum: 75us 99% C.I. 75us-75us Avg: 75us Max: 75us
VisitNonThreadRoots: Sum: 65us 99% C.I. 65us-65us Avg: 65us Max: 65us
ResumeRunnableThreads: Sum: 46us 99% C.I. 46us-46us Avg: 46us Max: 46us
(Paused)GrayAllNewlyDirtyImmuneObjects: Sum: 41us 99% C.I. 41us-41us Avg: 41us Max: 41us
MarkZygoteLargeObjects: Sum: 28us 99% C.I. 28us-28us Avg: 28us Max: 28us
CopyingPhase: Sum: 24us 99% C.I. 24us-24us Avg: 24us Max: 24us
RecordFree: Sum: 19us 99% C.I. 19us-19us Avg: 19us Max: 19us
ForwardSoftReferences: Sum: 17us 99% C.I. 17us-17us Avg: 17us Max: 17us
ProcessReferences: Sum: 16us 99% C.I. 3us-13us Avg: 8us Max: 13us
ReclaimPhase: Sum: 13us 99% C.I. 13us-13us Avg: 13us Max: 13us
EmptyRBMarkBitStack: Sum: 10us 99% C.I. 10us-10us Avg: 10us Max: 10us
(Paused)FlipCallback: Sum: 7us 99% C.I. 7us-7us Avg: 7us Max: 7us
(Paused)SetFromSpace: Sum: 6us 99% C.I. 6us-6us Avg: 6us Max: 6us
ResumeOtherThreads: Sum: 4us 99% C.I. 4us-4us Avg: 4us Max: 4us
(Paused)ClearCards: Sum: 2us 99% C.I. 250ns-2000ns Avg: 95ns Max: 2000ns
UnBindBitmaps: Sum: 1us 99% C.I. 1us-1us Avg: 1us Max: 1us
Done Dumping histograms
concurrent copying paused: Sum: 764us 99% C.I. 764us-764us Avg: 764us Max: 764us
concurrent copying freed-bytes: Avg: 49KB Max: 49KB Min: 49KB
Freed-bytes histogram: 0:1
concurrent copying total time: 19.399ms mean time: 19.399ms
concurrent copying freed: 262 objects with total size 49KB
concurrent copying throughput: 13789.5/s / 2586KB/s per cpu-time: 5032000/s / 4914KB/s
Average major GC reclaim bytes ratio 0.679411 over 1 GC cycles
Average major GC copied live bytes ratio 0.765451 over 4 major GCs
Cumulative bytes moved 5264536
Cumulative objects moved 117659
Peak regions allocated 226 (56MB) / 384 (96MB)
Start Dumping histograms for 1 iterations for young concurrent copying
ScanImmuneSpaces: Sum: 7.517ms 99% C.I. 7.517ms-7.517ms Avg: 7.517ms Max: 7.517ms
ProcessMarkStack: Sum: 7.348ms 99% C.I. 7.348ms-7.348ms Avg: 7.348ms Max: 7.348ms
VisitConcurrentRoots: Sum: 3.267ms 99% C.I. 3.267ms-3.267ms Avg: 3.267ms Max: 3.267ms
ClearFromSpace: Sum: 3.048ms 99% C.I. 3.048ms-3.048ms Avg: 3.048ms Max: 3.048ms
InitializePhase: Sum: 2.244ms 99% C.I. 2.244ms-2.244ms Avg: 2.244ms Max: 2.244ms
ScanCardsForSpace: Sum: 1.260ms 99% C.I. 1.260ms-1.260ms Avg: 1.260ms Max: 1.260ms
SweepSystemWeaks: Sum: 925us 99% C.I. 925us-925us Avg: 925us Max: 925us
GrayAllDirtyImmuneObjects: Sum: 713us 99% C.I. 713us-713us Avg: 713us Max: 713us
FlipOtherThreads: Sum: 324us 99% C.I. 324us-324us Avg: 324us Max: 324us
VisitNonThreadRoots: Sum: 254us 99% C.I. 254us-254us Avg: 254us Max: 254us
EnqueueFinalizerReferences: Sum: 204us 99% C.I. 204us-204us Avg: 204us Max: 204us
RecordFree: Sum: 126us 99% C.I. 1us-125us Avg: 63us Max: 125us
SweepArray: Sum: 110us 99% C.I. 110us-110us Avg: 110us Max: 110us
ThreadListFlip: Sum: 108us 99% C.I. 108us-108us Avg: 108us Max: 108us
(Paused)SetFromSpace: Sum: 77us 99% C.I. 77us-77us Avg: 77us Max: 77us
(Paused)ClearCards: Sum: 73us 99% C.I. 1us-26us Avg: 3.476us Max: 26us
CopyingPhase: Sum: 69us 99% C.I. 69us-69us Avg: 69us Max: 69us
(Paused)GrayAllNewlyDirtyImmuneObjects: Sum: 62us 99% C.I. 62us-62us Avg: 62us Max: 62us
ResumeRunnableThreads: Sum: 40us 99% C.I. 40us-40us Avg: 40us Max: 40us
ReclaimPhase: Sum: 33us 99% C.I. 33us-33us Avg: 33us Max: 33us
MarkZygoteLargeObjects: Sum: 31us 99% C.I. 31us-31us Avg: 31us Max: 31us
ProcessReferences: Sum: 28us 99% C.I. 5us-23us Avg: 14us Max: 23us
EmptyRBMarkBitStack: Sum: 26us 99% C.I. 26us-26us Avg: 26us Max: 26us
FreeList: Sum: 21us 99% C.I. 21us-21us Avg: 21us Max: 21us
ForwardSoftReferences: Sum: 20us 99% C.I. 20us-20us Avg: 20us Max: 20us
(Paused)FlipCallback: Sum: 8us 99% C.I. 8us-8us Avg: 8us Max: 8us
SwapBitmaps: Sum: 6us 99% C.I. 6us-6us Avg: 6us Max: 6us
FlipThreadRoots: Sum: 5us 99% C.I. 5us-5us Avg: 5us Max: 5us
ResumeOtherThreads: Sum: 3us 99% C.I. 3us-3us Avg: 3us Max: 3us
Done Dumping histograms
young concurrent copying paused: Sum: 314us 99% C.I. 314us-314us Avg: 314us Max: 314us
young concurrent copying freed-bytes: Avg: 3068KB Max: 3068KB Min: 3068KB
Freed-bytes histogram: 2880:1
young concurrent copying total time: 27.979ms mean time: 27.979ms
young concurrent copying freed: 6163 objects with total size 3068KB
young concurrent copying throughput: 228259/s / 110MB/s per cpu-time: 116376888/s / 110MB/s
Average minor GC reclaim bytes ratio 1.45298 over 1 GC cycles
Average minor GC copied live bytes ratio 0.0708724 over 2 minor GCs
Cumulative bytes moved 498176
Cumulative objects moved 4041
Peak regions allocated 226 (56MB) / 384 (96MB)
Total time spent in GC: 47.378ms
Mean GC size throughput: 63MB/s
Mean GC object throughput: 135590 objects/s
Total number of allocations 46469
Total bytes allocated 4565KB
Total bytes freed 3097KB
Free memory 480KB
Free memory until GC 480KB
Free memory until OOME 94MB
Total memory 1948KB
Max memory 96MB
Zygote space size 1508KB
Total mutator paused time: 1.078ms
Total time waiting for GC to complete: 16.563us
Total GC count: 2
Total GC time: 47.378ms
Total blocking GC count: 0
Total blocking GC time: 0
Native bytes total: 6988048 registered: 86288
Total native bytes at last GC: 6758872
/system/framework/oat/arm64/android.hidl.base-V1.0-java.odex: quicken
/system/framework/oat/arm64/android.hidl.manager-V1.0-java.odex: quicken
Current JIT code cache size: 3320B
Current JIT data cache size: 2264B
Current JIT mini-debug-info size: 8024B
Current JIT capacity: 64KB
Current number of JIT JNI stub entries: 0
Current number of JIT code cache entries: 9
Total number of JIT compilations: 5
Total number of JIT compilations for on stack replacement: 1
Total number of JIT code cache collections: 0
Memory used for stack maps: Avg: 243B Max: 480B Min: 40B
Memory used for compiled code: Avg: 953B Max: 2332B Min: 92B
Memory used for profiling info: Avg: 751B Max: 7776B Min: 24B
Start Dumping histograms for 18 iterations for JIT timings
Compiling: Sum: 25.341ms 99% C.I. 0.870ms-10.530ms Avg: 2.815ms Max: 10.701ms
TrimMaps: Sum: 2.896ms 99% C.I. 66us-887us Avg: 321.777us Max: 887us
Generate JIT debug info: Sum: 960us 99% C.I. 52us-247.750us Avg: 106.666us Max: 248us
Done Dumping histograms
Memory used for compilation: Avg: 110KB Max: 319KB Min: 17KB
ProfileSaver total_bytes_written=0
ProfileSaver total_number_of_writes=0
ProfileSaver total_number_of_code_cache_queries=0
ProfileSaver total_number_of_skipped_writes=0
ProfileSaver total_number_of_failed_writes=0
ProfileSaver total_ms_of_sleep=5000
ProfileSaver total_ms_of_work=0
ProfileSaver max_number_profile_entries_cached=0
ProfileSaver total_number_of_hot_spikes=0
ProfileSaver total_number_of_wake_ups=0

大家应该了解过Android虚拟机的逻辑内存以及GC收回机制,能看懂GC young、old等,接下来便是要点,关键字 “main”

DALVIK THREADS (13):
“main” prio=5 tid=1 Runnable
| group=“main” sCount=0 dsCount=0 flags=0 obj=0x72b31ec8 self=0x7dacf71c00
| sysTid=30675 nice=0 cgrp=default sched=0/0 handle=0x7dae4d9ed0
| state=R schedstat=( 63858980563 752954346 3029 ) utm=6370 stm=15 core=2 HZ=100
| stack=0x7ff146a000-0x7ff146c000 stackSize=8192KB
| held mutexes= “mutator lock”(shared held)
TraceBegin:
at com.android.test.MainActivity.onCreate(MainActivity.java:97)
at android.app.Activity.performCreate(Activity.java:7845)
at android.app.Activity.performCreate(Activity.java:7834)
at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1307)
at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:3246)
at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:3410)
at android.app.servertransaction.LaunchActivityItem.execute(LaunchActivityItem.java:83)
at android.app.servertransaction.TransactionExecutor.executeCallbacks(TransactionExecutor.java:135)
at android.app.servertransaction.TransactionExecutor.execute(TransactionExecutor.java:95)
at android.app.ActivityThreadH . h a n d l e M e s s a g e ( A c t i v i t y T h r e a d . j a v a : 2017 ) a t a n d r o i d . o s . H a n d l e r . d i s p a t c h M e s s a g e ( H a n d l e r . j a v a : 107 ) a t a n d r o i d . o s . L o o p e r . l o o p ( L o o p e r . j a v a : 214 ) a t a n d r o i d . a p p . A c t i v i t y T h r e a d . m a i n ( A c t i v i t y T h r e a d . j a v a : 7478 ) a t j a v a . l a n g . r e f l e c t . M e t h o d . i n v o k e ( N a t i v e m e t h o d ) a t c o m . a n d r o i d . i n t e r n a l . o s . R u n t i m e I n i t H.handleMessage(ActivityThread.java:2017) at android.os.Handler.dispatchMessage(Handler.java:107) at android.os.Looper.loop(Looper.java:214) at android.app.ActivityThread.main(ActivityThread.java:7478) at java.lang.reflect.Method.invoke(Native method) at com.android.internal.os.RuntimeInitH.handleMessage(ActivityThread.java:2017)atandroid.os.Handler.dispatchMessage(Handler.java:107)atandroid.os.Looper.loop(Looper.java:214)atandroid.app.ActivityThread.main(ActivityThread.java:7478)atjava.lang.reflect.Method.invoke(Nativemethod)atcom.android.internal.os.RuntimeInitMethodAndArgsCaller.run(RuntimeInit.java:492)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:935)
TraceEnd:

用一个表格来解说这些参数意义

android 开发——疑难杂症ANR简单介绍与解析

至于TraceBegin-end这儿的运转时异常,就没必要说了,最初为了触发ANR写的死循环就在异常这儿,很简单就能看出来。接下来看Binder线程

“Binder:30675_1” prio=5 tid=13 Native
| group=“main” sCount=1 dsCount=0 flags=1 obj=0x12c802d8 self=0x7d1bf43800
| sysTid=30694 nice=0 cgrp=default sched=0/0 handle=0x7cc3972d50
| state=S schedstat=( 2909010 9923125 18 ) utm=0 stm=0 core=4 HZ=100
| stack=0x7cc387c000-0x7cc387e000 stackSize=991KB
| held mutexes=
kernel: (couldn’t read /proc/self/task/30694/stack)
native: #00 pc 00000000000d10b4 /apex/com.android.runtime/lib64/bionic/libc.so (__ioctl+4)
native: #01 pc 000000000008b6b0 /apex/com.android.runtime/lib64/bionic/libc.so (ioctl+132)
native: #02 pc 0000000000058d24 /system/lib64/libbinder.so (android::IPCThreadState::talkWithDriver(bool)+244)
native: #03 pc 0000000000058f00 /system/lib64/libbinder.so (android::IPCThreadState::getAndExecuteCommand()+24)
native: #04 pc 00000000000596d8 /system/lib64/libbinder.so (android::IPCThreadState::joinThreadPool(bool)+64)
native: #05 pc 000000000007f978 /system/lib64/libbinder.so (android::PoolThread::threadLoop()+24)
native: #06 pc 00000000000135f0 /system/lib64/libutils.so (android::Thread::_threadLoop(void*)+328)
native: #07 pc 00000000000c5bd8 /system/lib64/libandroid_runtime.so (android::AndroidRuntime::javaThreadShell(void*)+140)
native: #08 pc 00000000000e68c0 /apex/com.android.runtime/lib64/bionic/libc.so (__pthread_start(void*)+36)
native: #09 pc 0000000000084b6c /apex/com.android.runtime/lib64/bionic/libc.so (__start_thread+64)
TraceBegin:
(no managed stack frames)
TraceEnd:

Binder线程是进程中线程池用来处理binder请求的线程,后面还有许多,看日志输出的格式没有差异,咱们先把他们列举出来:

  • Signal Catcher –signal信号,担任接纳处理kernel发送的各种信号,Signal_quit(3)、6是不合法地址越界、11是空指针如同,记不太清了可以百度signal信号能查到,traces文件便是由这个线程输出的,能看到线程状况是runnable
  • Jit thread pool worker thread 0 –这是Java虚拟机的代码即时编译器?
  • ADB-JDWP Connection Control Thread –支持虚拟机调试的线程
  • HeapTaskDaemon –仓库
  • ReferenceQueueDaemon –引证行列
  • FinalizerDaemon –释放目标前调用finalize
  • FinalizerWatchdogDaemon –电子狗?
  • Binder线程 –默认的话有三个binder线程
  • Profile Saver –AS里的Profile
  • RenderThread –敞开硬件加速后,就会呈现操作GPU的烘托线程 然后就完了,最后的目录我猜想它的意思是:打印traces日志是在哪个目录下代码履行的(显着它指的便是AMS在源码的途径),暂时先这么了解,哪天发现有过错,回头来改正

—– end 30675 —– frameworks/base/services/core/java/com/android/server

作为一个资深的程序猿,应该要对线程的各种状况都了解,因为Android的代码分为Java端和native端(也便是c++),因而,线程也有这两头的差异,实质意义差不多,可是名称叫法有点出入(也便是线程状况的标志),记一遍就好,常量状况一般是用大写来命名,大写我就不认识单词了,全换成小写,了解意思就好:

android 开发——疑难杂症ANR简单介绍与解析

能看到c++侧,线程状况更多更细化一些,Traces日志中的线程状况是以c++端来赋值的,通过这个文件很简单看出ANR出问题的当地在:com.android.test.MainActivity.onCreate(MainActivity.java:97),翻代码就能找到97行来修改,当然实践开发中或许会遇到非常复杂,难以分辨出来的ANR。

2.2、cpu、内存使用率剖析

上面咱们说,ANR也或许是因为CPU占比太高,主线程一向抢不到时刻片导致的,其实平常在做单元测验或许功能测验的时分,随时重视进程的CPU占比也是一个开发的好习惯,有三个shell指令要记住:

1、adb shell top –检查当时体系中排名前20的进程信息,以CPU占比排序(后边加 -p 进程号可以检查某个进程的详细信息),top指令可以检查体系中一切正在运转进程的CPU信息和meminfo,-p 进程号可以看到这一个进程宏观上的详细信息,可以把这些信息抓下来(adb shell top -b >D:***.txt) 2、adb shell dumpsys cpuinfo,dumpsys指令可以供给非常多的服务,一共如同有200+, 3、adb shell dumpsys meminfo

top指令可以从全体上来看体系当时的CPU和内存信息,也可以看某个进程的CPU与内存,可以看出来这个进程是否正常,可是详细哪一行代码导致的是无法定位的,解说下使用率,CPU使用率大略的认为:一段时刻T减去CPU闲暇时刻t后与这段时刻的份额值,CPU使用率= (T – t)/T,在使用adb shell top 后 能看到有这样一行信息:

User 13%, System 5%, IOW 0%, IRQ 0% User 85 + Nice 0 + Sys 37 + Idle 509 + IOW 0 + IRQ 0 + SIRQ 0 = 631

User 处于用户态的运转时刻,不包括优先值为负进程 Nice 优先值为负的进程所占用的CPU时刻 Sys 处于核心态的运转时刻 Idle 除IO等待时刻以外的其它等待时刻 IOW IO等待时刻 IRQ 硬中止时刻 SIRQ 软中止时刻

PID PR CPU% S #THR VSS RSS PCY UID Name
0 13% S 56 423416K 88160K fg u0_a92 com.tmall.wireless
1 2% R 1 1232K 536K root top
0 1% S 46 341712K 40872K fg u0_a90 com.wandoujia.phoenix2.usbproxy
1 1% S 31 319976K 33284K fg u0_a74 com.android.Chinpower
0 1% S 32 67320K 20552K fg system /system/bin/surfaceflinger
0 1% S 112 445876K 80304K fg system system_server0 0% S 1 0K 0K root watchdog/0
1 0% S 1 0K 0K root khelper
1 0% S 1 0K 0K root suspend_sys_syn
1 0% S 1 0K 0K root suspend

PID 进程id PR 优先级 CPU% 当时瞬时CPU占用率 S 进程状况:D=不可中止的睡觉状况, R=运转, S=睡觉, T=盯梢/停止, Z=僵尸进程 #THR 程序当时所用的线程数 VSS Virtual Set Size 虚拟耗用内存(包括同享库占用的内存) RSS Resident Set Size 实践使用物理内存(包括同享库占用的内存) PCY 调度战略优先级,SP_BACKGROUND/SP_FOREGROUND UID 进程一切者的用户id Name 进程的名称

CPU使用率的核算是在ProcessStats类中完成的。假如在某两个时刻T1和T2(T1 < T2)进行采样记载,CPU使用率的整个算法可以归纳为以下几个公式:

userTime = (user2 + nice2) – (user1 + nice1)
systemTime = system2 - system1
idleTime = idle2 - idle1
iowaitTime = iowait2 - iowait1
irqTime = irq2 - irq1
softirqTime = softirq2 - softirq1
TotalTime = userTime + systemTime + idleTime + iowaitTime + irqTime + softirqTime

有了以上数据就可以核算详细的使用率了,例如用户态CPU使用率为: userCpuUsage = userTime / TotalTime 依此类推可以核算其他类型的使用率。而整个时刻段内CPU使用率为: CpuUsage = (TotalTime – idleTime) / TotalTime 以上核算的是整个体系的CPU使用率,关于指定进程的使用率是通过读取该进程的“/proc/进程号/stat”文件核算的,而关于指定进程的指定线程的使用率是通过读取该线程的“/proc/进程号/task/线程号/stat”文件核算的。进程和线程的CPU使用率只包括该进程或线程的总使用率、用户态使用率和内核态使用率。 AMS在履行appNotResponding函数过程中,共输出了两个时刻段的CPU使用率,通常情况下在ANR发生时刻点之前和之后各有一段。两段使用率都是两次调用ProcessStats目标的update函数,每次调用update函数时会保存上一时刻点的采样数据,并记载当时时刻点的采样数据。然后再调用ProcessStats目标的printCurrentState函数,按照上述公式归纳的算法核算CPU使用率,并输出终究的成果。 而关于内存相较于ANR的影响,由top指令观察到的进程内存变化已然够用。

三、Traces文件是怎样发生的

当咱们拿到一个ANR的Traces文件后,现已知道怎样来剖析ANR,不过假使可以知道ANR文件的来龙去脉怎样发生怎样保存,必然更好:例如我现在就知道高版别上Traces文件被改名了、假如作为体系开发者,可以优化日志体系、当然最大的好处便是可以对ANR了解深化等等。 不得不再次提起这句话:ANR的实质是在当时焦点窗口主线程在做耗时操作,此刻又发生了一个事情(按键或许Touch事情),此刻发现上一次事情履行超时,开始走ANR逻辑。很显然,要从事情这儿下手,Android体系的事情由硬件发生,驱动节点”dev/input”,处理事情的体系服务是InputManagerService,IMS服务运转在SystemServer进程,由startOtherServices函数担任启动,IMS的start函数中会调用jni函数nativeStart,在native层里首要逻辑都是由InputManager类完成,这个类中会启动两个线程:

  • InputReaderThread–担任打开input事情驱动节点,接纳硬件事情;
  • InputDispatchThread–担任将InputReaderThread线程接纳到的事情分发给详细的窗口

IMS会和WMS通讯以确认哪个window是当时焦点窗口便于传递事情,是用LocalSocket通讯 ,InputDispatchThread分发事情的过程中,会记载事情的分发时刻,假如发现超时,就开起ANR的流程,因而说IMS中是ANR的起点,InputDispatcher调用notifyANR(InputMonitor是IMS和WMS通讯的中间件),当InputReader从EventHub拿到事情后,从dispatchMotionLocked开始分发

/*frameworks\native\services\inputflinger\InputDispatcher.cpp*/
int32_t InputDispatcher::findTouchedWindowTargetsLocked(nsecs_t currentTime,
        const MotionEntry* entry, std::vector<InputTarget>& inputTargets, nsecs_t* nextWakeupTime,
        bool* outConflictingPointerActions) {
    ...
    // Step1、判别触摸过的窗口是否都可以接纳新的事情,这儿便是判别这些窗口环境是否搭建好
    for (const TouchedWindow& touchedWindow : mTempTouchState.windows) {
        if (touchedWindow.targetFlags & InputTarget::FLAG_FOREGROUND) {
            // Check whether the window is ready for more input.
            std::string reason = checkWindowReadyForMoreInputLocked(currentTime,
                    touchedWindow.windowHandle, entry, "touched");
            if (!reason.empty()) {
                //Step2、呈现模块的环境未配置好,
                injectionResult = handleTargetsNotReadyLocked(currentTime, entry,
                        nullptr, touchedWindow.windowHandle, nextWakeupTime, reason.c_str());
                goto Unresponsive;
            }
        }
    }
    for (const TouchedWindow& touchedWindow : mTempTouchState.windows) {
        //Step3、成功为当时的事情找到契合的窗口,将其添加到inputTarget中
        addWindowTargetLocked(touchedWindow.windowHandle, touchedWindow.targetFlags,
                touchedWindow.pointerIds, inputTargets);
    }
​
    for (const TouchedMonitor& touchedMonitor : mTempTouchState.gestureMonitors) {
        addMonitoringTargetLocked(touchedMonitor.monitor, touchedMonitor.xOffset,
                touchedMonitor.yOffset, inputTargets);
    }
    ...
}
/* 进这个函数的原因有许多,
   1、system还未准备好,waiting
   2、application未准备好,waiting
   3、当时时刻戳现已大于规则的target窗口超时时刻--呈现ANR*/
int32_t InputDispatcher::handleTargetsNotReadyLocked(nsecs_t currentTime,
        const EventEntry* entry,
        const sp<InputApplicationHandle>& applicationHandle,
        const sp<InputWindowHandle>& windowHandle,
        nsecs_t* nextWakeupTime, const char* reason) {
    ...
    if (currentTime >= mInputTargetWaitTimeoutTime) {
        onANRLocked(currentTime, applicationHandle, windowHandle,
                entry->eventTime, mInputTargetWaitStartTime, reason);//很显着,ANR// Force poll loop to wake up immediately on next iteration once we get the
        // ANR response back from the policy.
        *nextWakeupTime = LONG_LONG_MIN;
        return INPUT_EVENT_INJECTION_PENDING;
    }
    ...
}
​
void InputDispatcher::onANRLocked(
        nsecs_t currentTime, const sp<InputApplicationHandle>& applicationHandle,
        const sp<InputWindowHandle>& windowHandle,
        nsecs_t eventTime, nsecs_t waitStartTime, const char* reason) {
    float dispatchLatency = (currentTime - eventTime) * 0.000001f;
    float waitDuration = (currentTime - waitStartTime) * 0.000001f;
    ALOGI("Application is not responding: %s.  "
            "It has been %0.1fms since event, %0.1fms since wait started.  Reason: %s",
            getApplicationWindowLabel(applicationHandle, windowHandle).c_str(),
            dispatchLatency, waitDuration, reason);
​
    // Capture a record of the InputDispatcher state at the time of the ANR.
    time_t t = time(nullptr);
    struct tm tm;
    localtime_r(&t, &tm);
    char timestr[64];
    strftime(timestr, sizeof(timestr), "%F %T", &tm);
    mLastANRState.clear();
    mLastANRState += INDENT "ANR:\n";
    mLastANRState += StringPrintf(INDENT2 "Time: %s\n", timestr);
    mLastANRState += StringPrintf(INDENT2 "Window: %s\n",
            getApplicationWindowLabel(applicationHandle, windowHandle).c_str());
    mLastANRState += StringPrintf(INDENT2 "DispatchLatency: %0.1fms\n", dispatchLatency);
    mLastANRState += StringPrintf(INDENT2 "WaitDuration: %0.1fms\n", waitDuration);
    mLastANRState += StringPrintf(INDENT2 "Reason: %s\n", reason);
    dumpDispatchStateLocked(mLastANRState);
    CommandEntry* commandEntry = postCommandLocked(
            & InputDispatcher::doNotifyANRLockedInterruptible/*告诉其他当地发生了ANR*/);
    commandEntry->inputApplicationHandle = applicationHandle;
    commandEntry->inputChannel = windowHandle != nullptr ?
            getInputChannelLocked(windowHandle->getToken()) : nullptr;
    commandEntry->reason = reason;
}
​
void InputDispatcher::doNotifyANRLockedInterruptible(
        CommandEntry* commandEntry) {
    mLock.unlock();
    /*mPolicy是什么?
      它是jni一路传递下来的PhoneWindoManager目标,在WMS创立时设置,接口是IMS的
      WindowManagerCallbacks供给,因而终究回调的是PhoneWindowManager的notifyANR
      PhoneWindowManager是windowManagerPolicy接口的完成类
    */
    nsecs_t newTimeout = mPolicy->notifyANR(
            commandEntry->inputApplicationHandle,
            commandEntry->inputChannel ? commandEntry->inputChannel->getToken() : nullptr,
            commandEntry->reason);
    mLock.lock();
    resumeAfterTargetsNotReadyTimeoutLocked(newTimeout,
            commandEntry->inputChannel);
}

注意: mPolicy->notifyANR后(jni机制调用),流程走到IMS的notifyANR,

//com_android_server_input_InputManagerService.cpp
nsecs_t NativeInputManager::notifyANR(const sp<InputApplicationHandle>& inputApplicationHandle,
        const sp<IBinder>& token, const std::string& reason) {
    jlong newTimeout = env->CallLongMethod(mServiceObj,
                gServiceClassInfo.notifyANR, tokenObj,
                reasonObj);
    if (checkAndClearExceptionFromCallback(env, "notifyANR")) {
        newTimeout = 0; // abort dispatch
    } else {
        assert(newTimeout >= 0);
    }
    return newTimeout;
}
​
//InputManagerService.java
private long notifyANR(IBinder token, String reason) {
    return mWindowManagerCallbacks.notifyANR(
            token, reason);
}
​
//InputManagerCallback.java
@Override
public long notifyANR(IBinder token, String reason) {
    //输入事情log打印,不同情况打印不一样。关键字Input event dispatching timed out
    synchronized (mService.mGlobalLock) {
            Slog.i(TAG_WM, "Input event dispatching timed out "
                    + ...);
    }
    ...
    } else if (windowState != null) {
        // Notify the activity manager about the timeout and let it decide whether
        // to abort dispatching or keep waiting.
        long timeout = mService.mAmInternal.inputDispatchingTimedOut(
                windowState.mSession.mPid, aboveSystem, reason);
        if (timeout >= 0) {
            // The activity manager declined to abort dispatching.
            // Wait a bit longer and timeout again later.
            return timeout * 1000000L; // nanoseconds
        }
    }
    return 0; // abort dispatching
}
​
//ActivityManagerService.java
@VisibleForTesting
public final class LocalService extends ActivityManagerInternal {
    @Override
    public long inputDispatchingTimedOut(int pid, boolean aboveSystem, String reason) {
        synchronized (ActivityManagerService.this) {
            return ActivityManagerService.this.inputDispatchingTimedOut(
                    pid, aboveSystem, reason);
        }
    }
}
​
long inputDispatchingTimedOut(int pid, final boolean aboveSystem, String reason) {
    synchronized (this) {
        timeout = proc != null ? proc.getInputDispatchingTimeout() : KEY_DISPATCHING_TIMEOUT_MS;
    }
    if (inputDispatchingTimedOut(proc, null, null, null, null, aboveSystem, reason)) {
        return -1;
    }
    return timeout;
}
​
boolean inputDispatchingTimedOut(ProcessRecord proc, String activityShortComponentName,
        ApplicationInfo aInfo, String parentShortComponentName,
        WindowProcessController parentProcess, boolean aboveSystem, String reason) {
    if (proc != null) {
        proc.appNotResponding(activityShortComponentName, aInfo,
                parentShortComponentName, parentProcess, aboveSystem, annotation);
    }
​
    return true;
}
​

流程很清晰,最后调用到appNotResponding函数(frameworks/base/services/core/java/com/android/server/am/ProcessRecord.java),

void appNotResponding(String activityShortComponentName, ApplicationInfo aInfo,
              String parentShortComponentName, WindowProcessController parentProcess,
              boolean aboveSystem, String annotation) {
    mWindowProcessController.appEarlyNotResponding(annotation, () -> kill("anr", true));
    long anrTime = SystemClock.uptimeMillis();
        if (isMonitorCpuUsage()) {
            mService.updateCpuStatsNow();
        }
    synchronized (mService) {
            // PowerManager.reboot() can block for a long time, so ignore ANRs while shutting down.
            if (mService.mAtmInternal.isShuttingDown()) {
                Slog.i(TAG, "During shutdown skipping ANR: " + this + " " + annotation);
                return;
            } else if (isNotResponding()) {
                Slog.i(TAG, "Skipping duplicate ANR: " + this + " " + annotation);
                return;
            } else if (isCrashing()) {
                Slog.i(TAG, "Crashing app skipping ANR: " + this + " " + annotation);
                return;
            } else if (killedByAm) {
                Slog.i(TAG, "App already killed by AM skipping ANR: " + this + " " + annotation);
                return;
            } else if (killed) {
                Slog.i(TAG, "Skipping died app ANR: " + this + " " + annotation);
                return;
            }
​
            // In case we come through here for the same app before completing
            // this one, mark as anring now so we will bail out.
            setNotResponding(true);
            // Log the ANR to the event log.
            EventLog.writeEvent(EventLogTags.AM_ANR, userId, pid, processName, info.flags,
                    annotation);
            // Dump thread traces as quickly as we can, starting with "interesting" processes.
            firstPids.add(pid);
            // Don't dump other PIDs if it's a background ANR
            if (!isSilentAnr()) {
                int parentPid = pid;
                if (parentProcess != null && parentProcess.getPid() > 0) {
                    parentPid = parentProcess.getPid();
                }
                if (parentPid != pid) firstPids.add(parentPid);
                if (MY_PID != pid && MY_PID != parentPid) firstPids.add(MY_PID);
                for (int i = getLruProcessList().size() - 1; i >= 0; i--) {
                    ProcessRecord r = getLruProcessList().get(i);
                    if (r != null && r.thread != null) {
                        int myPid = r.pid;
                        if (myPid > 0 && myPid != pid && myPid != parentPid && myPid != MY_PID) {
                            if (r.isPersistent()) {
                                firstPids.add(myPid);
                                if (DEBUG_ANR) Slog.i(TAG, "Adding persistent proc: " + r);
                            } else if (r.treatLikeActivity) {
                                firstPids.add(myPid);
                                if (DEBUG_ANR) Slog.i(TAG, "Adding likely IME: " + r);
                            } else {
                                lastPids.put(myPid, Boolean.TRUE);
                                if (DEBUG_ANR) Slog.i(TAG, "Adding ANR proc: " + r);
                            }
                        }
                    }
                }
            }
        }
    // Log the ANR to the main log.
        StringBuilder info = new StringBuilder();
        info.setLength(0);
        info.append("ANR in ").append(processName);
        if (activityShortComponentName != null) {
            info.append(" (").append(activityShortComponentName).append(")");
        }
        info.append("\n");
        info.append("PID: ").append(pid).append("\n");
        if (annotation != null) {
            info.append("Reason: ").append(annotation).append("\n");
        }
        if (parentShortComponentName != null
                && parentShortComponentName.equals(activityShortComponentName)) {
            info.append("Parent: ").append(parentShortComponentName).append("\n");
        }
        ProcessCpuTracker processCpuTracker = new ProcessCpuTracker(true);
        ArrayList<Integer> nativePids = null;
        // don't dump native PIDs for background ANRs unless it is the process of interest
        String[] nativeProc = null;
        if (isSilentAnr()) {
            for (int i = 0; i < NATIVE_STACKS_OF_INTEREST.length; i++) {
                if (NATIVE_STACKS_OF_INTEREST[i].equals(processName)) {
                    nativeProc = new String[] { processName };
                    break;
                }
            }
            int[] pid = nativeProc == null ? null : Process.getPidsForCommands(nativeProc);
            if(pid != null){
                nativePids = new ArrayList<>(pid.length);
                for (int i : pid) {
                    nativePids.add(i);
                }
            }
        } else {
            nativePids = Watchdog.getInstance().getInterestingNativePids();
        }   
        //Step1、生成Traces文件
       File tracesFile = ActivityManagerService.dumpStackTraces(firstPids,
                (isSilentAnr()) ? null : processCpuTracker, (isSilentAnr()) ? null : lastPids,
                nativePids);
        String cpuInfo = null;
        if (isMonitorCpuUsage()) {
            mService.updateCpuStatsNow();
            synchronized (mService.mProcessCpuTracker) {
                cpuInfo = mService.mProcessCpuTracker.printCurrentState(anrTime);
            }
            info.append(processCpuTracker.printCurrentLoad());
            info.append(cpuInfo);
        }
        info.append(processCpuTracker.printCurrentState(anrTime));
        Slog.e(TAG, info.toString());
        if (tracesFile == null) {
            // There is no trace file, so dump (only) the alleged culprit's threads to the log
            Process.sendSignal(pid, Process.SIGNAL_QUIT);
        }
        StatsLog.write(StatsLog.ANR_OCCURRED, uid, processName,
                activityShortComponentName == null ? "unknown": activityShortComponentName,
                annotation,
                (this.info != null) ? (this.info.isInstantApp()
                        ? StatsLog.ANROCCURRED__IS_INSTANT_APP__TRUE
                        : StatsLog.ANROCCURRED__IS_INSTANT_APP__FALSE)
                        : StatsLog.ANROCCURRED__IS_INSTANT_APP__UNAVAILABLE,
                isInterestingToUserLocked()
                        ? StatsLog.ANROCCURRED__FOREGROUND_STATE__FOREGROUND
                        : StatsLog.ANROCCURRED__FOREGROUND_STATE__BACKGROUND,
                getProcessClassEnum(),
                (this.info != null) ? this.info.packageName : "");
        final ProcessRecord parentPr = parentProcess != null
                ? (ProcessRecord) parentProcess.mOwner : null;
        mService.addErrorToDropBox("anr", this, processName, activityShortComponentName,
                parentShortComponentName, parentPr, annotation, cpuInfo, tracesFile, null);
        if (mWindowProcessController.appNotResponding(info.toString(), () -> kill("anr", true),
                () -> {
                    synchronized (mService) {
                        mService.mServices.scheduleServiceTimeoutLocked(this);
                    }
                })) {
            return;
        }
        synchronized (mService) {
            // mBatteryStatsService can be null if the AMS is constructed with injector only. This
            // will only happen in tests.
            if (mService.mBatteryStatsService != null) {
                mService.mBatteryStatsService.noteProcessAnr(processName, uid);
            }
            if (isSilentAnr() && !isDebugging()) {
                kill("bg anr", true);
                return;
            }
            // Set the app's notResponding state, and look up the errorReportReceiver
            makeAppNotRespondingLocked(activityShortComponentName,
                    annotation != null ? "ANR " + annotation : "ANR", info.toString());
            // mUiHandler can be null if the AMS is constructed with injector only. This will only
            // happen in tests.
            if (mService.mUiHandler != null) {
                // Bring up the infamous App Not Responding dialog
                Message msg = Message.obtain();
                msg.what = ActivityManagerService.SHOW_NOT_RESPONDING_UI_MSG;
                msg.obj = new AppNotRespondingDialog.Data(this, aInfo, aboveSystem);
                mService.mUiHandler.sendMessage(msg);
            }
        }  
}
​

ActivityManagerService.dumpStackTraces,再调回到AMS里,由dumpStackTraces函数来创立并往文件中写入内容(data/anr/traces_**.txt),至此ANR剖析完成。

以上便是Android开发中常会遇到的ANR介绍以及剖析;Android开发中需要疑难核心知识点。需要深化的掌握;更多Android技能参考>可以> 前往

传送直达↓↓↓ :www.6hu.cc/go//?target=htt…

文末

ANR 的全称是 Application No Responding,即使用程序无呼应,详细是一些特定的 Message (Key Dispatch、Broadcast、Service) 在使用的UI线程(主线程)没有在规则的时刻内处理完,进而触发 ANR 异常。

ANR 由音讯处理机制确保,Android 在体系层完成了一套精细的机制来发现 ANR,核心原理是音讯调度和超时处理。ANR 机制主体完成在体系层,一切与 ANR 相关的音讯,都会通过体系进程 system_server调度,详细是 ActivityManagerService服务,然后派发到使用进程完成对音讯的实践处理,一起,体系进程设计了不同的超时约束来盯梢音讯的处理。 一旦使用程序处理音讯不当,超时约束就起作用了,它收集一些体系状况,譬如 CPU/IO 使用情况、进程函数调用栈 CallStack,(有些渠道比方 MTK,还会打印相应的 Message 出来供调试剖析),最后陈述用户有进程无呼应了( ANR 对话框)。

ANR问题,信任是每位开发日常都会遇到的问题。怎样更多问题可评论区或私信。