全面解析Android之ANR日志
解決ANR一直是Android 開(kāi)發(fā)者需要掌握的重要技巧,一般從三個(gè)方面著手。
開(kāi)發(fā)階段:通過(guò)工具檢查各個(gè)方法的耗時(shí),卡頓情況,發(fā)現(xiàn)一處修改一處。 線上階段:這個(gè)階段主要依靠監(jiān)控工具發(fā)現(xiàn)ANR并上報(bào),比如matrix。 分析階段:如果線上用戶發(fā)生ANR,并且你獲取了一份日志,這就涉及了本文要分享的內(nèi)容——ANR日志分析技巧。二、ANR產(chǎn)生機(jī)制網(wǎng)上通俗的一段面試答題
ANR——應(yīng)用無(wú)響應(yīng),Activity是5秒,BroadCastReceiver是10秒,Service是20秒。
這句話說(shuō)的很籠統(tǒng),要想深入分析定位ANR,需要知道更多知識(shí)點(diǎn),一般來(lái)說(shuō),ANR按產(chǎn)生機(jī)制,分為4類:
2.1 輸入事件超時(shí)(5s)InputEvent Timeout
a.InputDispatcher發(fā)送key事件給 對(duì)應(yīng)的進(jìn)程的 Focused Window ,對(duì)應(yīng)的window不存在、處于暫停態(tài)、或通道(input channel)占滿、通道未注冊(cè)、通道異常、或5s內(nèi)沒(méi)有處理完一個(gè)事件,就會(huì)發(fā)生ANR
b.InputDispatcher發(fā)送MotionEvent事件有個(gè)例外之處:當(dāng)對(duì)應(yīng)Touched Window的 input waitQueue中有超過(guò)0.5s的事件,inputDispatcher會(huì)暫停該事件,并等待5s,如果仍舊沒(méi)有收到window的‘finish’事件,則觸發(fā)ANR
c.下一個(gè)事件到達(dá),發(fā)現(xiàn)有一個(gè)超時(shí)事件才會(huì)觸發(fā)ANR
2.2 廣播類型超時(shí)(前臺(tái)15s,后臺(tái)60s)BroadcastReceiver Timeout
a.靜態(tài)注冊(cè)的廣播和有序廣播會(huì)ANR,動(dòng)態(tài)注冊(cè)的非有序廣播并不會(huì)ANR
b.廣播發(fā)送時(shí),會(huì)判斷該進(jìn)程是否存在,不存在則創(chuàng)建,創(chuàng)建進(jìn)程的耗時(shí)也算在超時(shí)時(shí)間里
c.只有當(dāng)進(jìn)程存在前臺(tái)顯示的Activity才會(huì)彈出ANR對(duì)話框,否則會(huì)直接殺掉當(dāng)前進(jìn)程
d.當(dāng)onReceive執(zhí)行超過(guò)閾值(前臺(tái)15s,后臺(tái)60s),將產(chǎn)生ANR
e.如何發(fā)送前臺(tái)廣播:Intent.addFlags(Intent.FLAG_RECEIVER_FOREGROUND)
2.3 服務(wù)超時(shí)(前臺(tái)20s,后臺(tái)200s)Service Timeout
a.Service的以下方法都會(huì)觸發(fā)ANR:onCreate(),onStartCommand(), onStart(), onBind(), onRebind(), onTaskRemoved(), onUnbind(),
onDestroy().
b.前臺(tái)Service超時(shí)時(shí)間為20s,后臺(tái)Service超時(shí)時(shí)間為200s
c.如何區(qū)分前臺(tái)、后臺(tái)執(zhí)行————當(dāng)前APP處于用戶態(tài),此時(shí)執(zhí)行的Service則為前臺(tái)執(zhí)行。
d.用戶態(tài):有前臺(tái)activity、有前臺(tái)廣播在執(zhí)行、有foreground service執(zhí)行
2.4 ContentProvider 類型a.ContentProvider創(chuàng)建發(fā)布超時(shí)并不會(huì)ANR
b.使用ContentProviderclient來(lái)訪問(wèn)ContentProverder可以自主選擇觸發(fā)ANR,超時(shí)時(shí)間自己定
client.setDetectNotResponding(PROVIDER_ANR_TIMEOUT);
ps:Activity生命周期超時(shí)會(huì)不會(huì)ANR?——經(jīng)測(cè)試并不會(huì)。
override fun onCreate(savedInstanceState: Bundle?) { Thread.sleep(60000) super.onCreate(savedInstanceState) setContentView(R.layout.activity_main)}三、導(dǎo)致ANR的原因
很多開(kāi)發(fā)者認(rèn)為,那就是耗時(shí)操作導(dǎo)致ANR,全部是app應(yīng)用層的問(wèn)題。實(shí)際上,線上環(huán)境大部分ANR由系統(tǒng)原因?qū)е隆?/p>3.1 應(yīng)用層導(dǎo)致ANR(耗時(shí)操作)
a. 函數(shù)阻塞:如死循環(huán)、主線程IO、處理大數(shù)據(jù)
b. 鎖出錯(cuò):主線程等待子線程的鎖
c. 內(nèi)存緊張:系統(tǒng)分配給一個(gè)應(yīng)用的內(nèi)存是有上限的,長(zhǎng)期處于內(nèi)存緊張,會(huì)導(dǎo)致頻繁內(nèi)存交換,進(jìn)而導(dǎo)致應(yīng)用的一些操作超時(shí)
3.2 系統(tǒng)導(dǎo)致ANRa. CPU被搶占:一般來(lái)說(shuō),前臺(tái)在玩游戲,可能會(huì)導(dǎo)致你的后臺(tái)廣播被搶占CPU
b. 系統(tǒng)服務(wù)無(wú)法及時(shí)響應(yīng):比如獲取系統(tǒng)聯(lián)系人等,系統(tǒng)的服務(wù)都是Binder機(jī)制,服務(wù)能力也是有限的,有可能系統(tǒng)服務(wù)長(zhǎng)時(shí)間不響應(yīng)導(dǎo)致ANR
c. 其他應(yīng)用占用的大量?jī)?nèi)存
四、分析日志發(fā)生ANR的時(shí)候,系統(tǒng)會(huì)產(chǎn)生一份anr日志文件(手機(jī)的/data/anr 目錄下,文件名稱可能各廠商不一樣,業(yè)內(nèi)大多稱呼為trace文件),內(nèi)含如下幾項(xiàng)重要信息。
4.1 CPU 負(fù)載Load: 2.62 / 2.55 / 2.25
CPU 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 分鐘內(nèi)正在使用和等待使用CPU 的活動(dòng)進(jìn)程的平均數(shù) 第二行:表明負(fù)載信息抓取在ANR發(fā)生之后的0~1987ms。同時(shí)也指明了ANR的時(shí)間點(diǎn):2020-03-10 08:31:55.169 中間部分:各個(gè)進(jìn)程占用的CPU的詳細(xì)情況 最后一行:各個(gè)進(jìn)程合計(jì)占用的CPU信息。名詞解釋:
a. user:用戶態(tài),kernel:內(nèi)核態(tài)
b. faults:內(nèi)存缺頁(yè),minor——輕微的,major——重度,需要從磁盤(pán)拿數(shù)據(jù)
c. iowait:IO使用(等待)占比
d. irq:硬中斷,softirq:軟中斷
注意:
iowait占比很高,意味著有很大可能,是io耗時(shí)導(dǎo)致ANR,具體進(jìn)一步查看有沒(méi)有進(jìn)程faults major比較多。 單進(jìn)程CPU的負(fù)載并不是以100%為上限,而是有幾個(gè)核,就有百分之幾百,如4核上限為400%。4.2 內(nèi)存信息Total number of allocations 476778進(jìn)程創(chuàng)建到現(xiàn)在一共創(chuàng)建了多少對(duì)象
Total bytes allocated 52MB 進(jìn)程創(chuàng)建到現(xiàn)在一共申請(qǐng)了多少內(nèi)存
Total bytes freed 52MB 進(jìn)程創(chuàng)建到現(xiàn)在一共釋放了多少內(nèi)存
Free memory 777KB 不擴(kuò)展堆的情況下可用的內(nèi)存
Free memory until GC 777KBGC前的可用內(nèi)存
Free memory until OOME 383MBOOM之前的可用內(nèi)存
Total memory 當(dāng)前總內(nèi)存(已用+可用)
Max memory 384MB 進(jìn)程最多能申請(qǐng)的內(nèi)存
從含義可以得出結(jié)論:**Free memory until OOME**的值很小的時(shí)候,已經(jīng)處于內(nèi)存緊張狀態(tài)。應(yīng)用可能是占用了過(guò)多內(nèi)存。
另外,除了trace文件中有內(nèi)存信息,普通的eventlog日志中,也有內(nèi)存信息(不一定打印)
04-02 22:00:08.195 1531 1544 I am_meminfo: [350937088,41086976,492830720,427937792,291887104]
以上四個(gè)值分別指的是:
Cached Free, Zram, Kernel,NativeCached+Free的內(nèi)存代表著當(dāng)前整個(gè)手機(jī)的可用內(nèi)存,如果值很小,意味著處于內(nèi)存緊張狀態(tài)。一般低內(nèi)存的判定閾值為:4G 內(nèi)存手機(jī)以下閥值:350MB,以上閥值則為:450MB
ps:如果ANR時(shí)間點(diǎn)前后,日志里有打印onTrimMemory,也可以作為內(nèi)存緊張的一個(gè)參考判斷
4.3 堆棧消息堆棧信息是最重要的一個(gè)信息,展示了ANR發(fā)生的進(jìn)程當(dāng)前所有線程的狀態(tài)。
suspend all histogram: Sum: 2.834s 99% C.I. 5.738us-7145.919us Avg: 607.155us Max: 41543us
DALVIK THREADS (248):
'main' prio=5 tid=1 Native
| group='main' sCount=1 dsCount=0 flags=1 obj=0x74b17080 self=0x7bb7a14c00
| sysTid=2080 nice=-2 cgrp=default sched=0/0 handle=0x7c3e82b548
| state=S schedstat=( 757205342094 583547320723 2145008 ) utm=52002 stm=23718 core=5 HZ=100
| stack=0x7fdc995000-0x7fdc997000 stackSize=8MB
| held mutexes=
kernel: __switch_to+0xb0/0xbc
kernel: SyS_epoll_wait+0x288/0x364
kernel: SyS_epoll_pwait+0xb0/0x124
kernel: cpu_switch_to+0x38c/0x2258
native: #00 pc 000000000007cd8c /system/lib64/libc.so (__epoll_pwait+8)
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 prio=5 tid=251 TimedWaiting
| group='main' sCount=1 dsCount=0 flags=1 obj=0x13daea90 self=0x7bad32b400
| sysTid=29998 nice=0 cgrp=default sched=0/0 handle=0x7b7d2614f0
| state=S schedstat=( 951407 137448 11 ) utm=0 stm=0 core=3 HZ=100
| stack=0x7b7d15e000-0x7b7d160000 stackSize=1041KB
| 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)
如上日志所示,本文截圖了兩個(gè)線程信息,一個(gè)是主線程main,它的狀態(tài)是native。
另一個(gè)是OkHttp ConnectionPool,它的狀態(tài)是TimeWaiting。眾所周知,教科書(shū)上說(shuō)線程狀態(tài)有5種:新建、就緒、執(zhí)行、阻塞、死亡。而Java中的線程狀態(tài)有6種,6種狀態(tài)都定義在:java.lang.Thread.State中
問(wèn)題來(lái)了,上述main線程的native是什么狀態(tài),哪來(lái)的?其實(shí)trace文件中的狀態(tài)是是CPP代碼中定義的狀態(tài),下面是一張對(duì)應(yīng)關(guān)系表。
由此可知,main函數(shù)的native狀態(tài)是正在執(zhí)行JNI函數(shù)。堆棧信息是我們分析ANR的第一個(gè)重要的信息,一般來(lái)說(shuō):
main線程處于 BLOCK、WAITING、TIMEWAITING狀態(tài),那基本上是函數(shù)阻塞導(dǎo)致ANR;
如果main線程無(wú)異常,則應(yīng)該排查CPU負(fù)載和內(nèi)存環(huán)境。
五、典型案例分析5.1 主線程無(wú)卡頓,處于正常狀態(tài)堆棧'main' prio=5 tid=1 Native
| group='main' sCount=1 dsCount=0 flags=1 obj=0x74b38080 self=0x7ad9014c00
| sysTid=23081 nice=0 cgrp=default sched=0/0 handle=0x7b5fdc5548
| state=S schedstat=( 284838633 166738594 505 ) utm=21 stm=7 core=1 HZ=100
| stack=0x7fc95da000-0x7fc95dc000 stackSize=8MB
| held mutexes=
kernel: __switch_to+0xb0/0xbc
kernel: SyS_epoll_wait+0x288/0x364
kernel: SyS_epoll_pwait+0xb0/0x124
kernel: cpu_switch_to+0x38c/0x2258
native: #00 pc 000000000007cd8c /system/lib64/libc.so (__epoll_pwait+8)
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 00000000001275f4 /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 android.app.ActivityThread.main(ActivityThread.java:7073)
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:876)
上述主線程堆棧就是一個(gè)很正常的空閑堆棧,表明主線程正在等待新的消息。
如果ANR日志里主線程是這樣一個(gè)狀態(tài),那可能有兩個(gè)原因:
該ANR是CPU搶占或內(nèi)存緊張等其他因素引起 這份ANR日志抓取的時(shí)候,主線程已經(jīng)恢復(fù)正常遇到這種空閑堆棧,可以按照第3節(jié)的方法去分析CPU、內(nèi)存的情況。其次可以關(guān)注抓取日志的時(shí)間和ANR發(fā)生的時(shí)間是否相隔過(guò)久,時(shí)間過(guò)久這個(gè)堆棧就沒(méi)有分析意義了。
5.2 主線程執(zhí)行耗時(shí)操作'main' prio=5 tid=1 Runnable
| group='main' sCount=0 dsCount=0 flags=0 obj=0x72deb848 self=0x7748c10800
| sysTid=8968 nice=-10 cgrp=default sched=0/0 handle=0x77cfa75ed0
| state=R schedstat=( 24783612979 48520902 756 ) utm=2473 stm=5 core=5 HZ=100
| stack=0x7fce68b000-0x7fce68d000 stackSize=8192KB
| held mutexes= 'mutator lock'(shared held)
at com.example.test.MainActivity$onCreate$2.onClick(MainActivity.kt:20)——關(guān)鍵行!!!
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)
上述日志表明,主線程正處于執(zhí)行狀態(tài),看堆棧信息可知不是處于空閑狀態(tài),發(fā)生ANR是因?yàn)橐惶巆lick監(jiān)聽(tīng)函數(shù)里執(zhí)行了耗時(shí)操作。
5.3 主線程被鎖阻塞'main' prio=5 tid=1 Blocked
| group='main' sCount=1 dsCount=0 flags=1 obj=0x72deb848 self=0x7748c10800
| sysTid=22838 nice=-10 cgrp=default sched=0/0 handle=0x77cfa75ed0
| state=S schedstat=( 390366023 28399376 279 ) utm=34 stm=5 core=1 HZ=100
| stack=0x7fce68b000-0x7fce68d000 stackSize=8192KB
| 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 ——————關(guān)鍵行!!!
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' prio=5 tid=3 TimeWating
| group='main' sCount=1 dsCount=0 flags=1 obj=0x12c44230 self=0x772f0ec000
| sysTid=22938 nice=0 cgrp=default sched=0/0 handle=0x77391fbd50
| state=S schedstat=( 274896 0 1 ) utm=0 stm=0 core=1 HZ=100
| stack=0x77390f9000-0x77390fb000 stackSize=1039KB
| 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)————————————————————關(guān)鍵行!!!
at java.lang.Thread.run(Thread.java:919)
這是一個(gè)典型的主線程被鎖阻塞的例子;
waiting to lock <0x01aed1da> (a java.lang.Object) held by thread 3
其中等待的鎖是<0x01aed1da>,這個(gè)鎖的持有者是線程 3。進(jìn)一步搜索 “tid=3” 找到線程3, 發(fā)現(xiàn)它正在TimeWating。
那么ANR的原因找到了:線程3持有了一把鎖,并且自身長(zhǎng)時(shí)間不釋放,主線程等待這把鎖發(fā)生超時(shí)。在線上環(huán)境中,常見(jiàn)因鎖而ANR的場(chǎng)景是SharePreference寫(xiě)入。
5.4 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 ————關(guān)鍵行!!!
99% 674/[email protected]: 81% user + 18% kernel / faults: 403 minor
24% 32589/com.wang.test: 22% user + 1.4% kernel / faults: 7432 minor 1 major
........省略N行.....
如上日志,第二行是釘釘?shù)倪M(jìn)程,占據(jù)CPU高達(dá)543%,搶占了大部分CPU資源,因而導(dǎo)致發(fā)生ANR。
5.5內(nèi)存緊張導(dǎo)致ANR如果有一份日志,CPU和堆棧都很正常(不貼出來(lái)了),仍舊發(fā)生ANR,考慮是內(nèi)存緊張。
從CPU第一行信息可以發(fā)現(xiàn),ANR的時(shí)間點(diǎn)是2020-10-31 22:38:58.468—CPU usage from 0ms to 21752ms later (2020-10-31 22:38:58.468 to 2020-10-31 22:39:20.220)
接著去系統(tǒng)日志里搜索am_meminfo, 這個(gè)沒(méi)有搜索到。再次搜索onTrimMemory,果然發(fā)現(xiàn)了很多條記錄;
10-31 22:37:19.749 20733 20733 E Runtime : onTrimMemory level:80,pid:com.xxx.xxx:Launcher0
10-31 22:37:33.458 20733 20733 E Runtime : onTrimMemory level:80,pid:com.xxx.xxx:Launcher0
10-31 22:38:00.153 20733 20733 E Runtime : onTrimMemory level:80,pid:com.xxx.xxx:Launcher0
10-31 22:38:58.731 20733 20733 E Runtime : onTrimMemory level:80,pid:com.xxx.xxx:Launcher0
10-31 22:39:02.816 20733 20733 E Runtime : onTrimMemory level:80,pid:com.xxx.xxx:Launcher0
可以看出,在發(fā)生ANR的時(shí)間點(diǎn)前后,內(nèi)存都處于緊張狀態(tài),level等級(jí)是80,查看Android API 文檔;
/** * Level for {@link #onTrimMemory(int)}: the process is nearing the end * of the background LRU list, and if more memory isn’t found soon it will * be killed. */ static final int TRIM_MEMORY_COMPLETE = 80;
可知80這個(gè)等級(jí)是很嚴(yán)重的,應(yīng)用馬上就要被殺死,被殺死的這個(gè)應(yīng)用從名字可以看出來(lái)是桌面,連桌面都快要被殺死,那普通應(yīng)用能好到哪里去呢?
一般來(lái)說(shuō),發(fā)生內(nèi)存緊張,會(huì)導(dǎo)致多個(gè)應(yīng)用發(fā)生ANR,所以在日志中如果發(fā)現(xiàn)有多個(gè)應(yīng)用一起ANR了,可以初步判定,此ANR與你的應(yīng)用無(wú)關(guān)。
5.6 系統(tǒng)服務(wù)超時(shí)導(dǎo)致ANR系統(tǒng)服務(wù)超時(shí)一般會(huì)包含BinderProxy.transactNative關(guān)鍵字,請(qǐng)看如下日志:
'main' prio=5 tid=1 Native
| group='main' sCount=1 dsCount=0 flags=1 obj=0x727851e8 self=0x78d7060e00
| sysTid=4894 nice=0 cgrp=default sched=0/0 handle=0x795cc1e9a8
| state=S schedstat=( 8292806752 1621087524 7167 ) utm=707 stm=122 core=5 HZ=100
| stack=0x7febb64000-0x7febb66000 stackSize=8MB
| held mutexes=
kernel: __switch_to+0x90/0xc4
kernel: binder_thread_read+0xbd8/0x144c
kernel: binder_ioctl_write_read.constprop.58+0x20c/0x348
kernel: binder_ioctl+0x5d4/0x88c
kernel: do_vfs_ioctl+0xb8/0xb1c
kernel: SyS_ioctl+0x84/0x98
kernel: cpu_switch_to+0x34c/0x22c0
native: #00 pc 000000000007a2ac /system/lib64/libc.so (__ioctl+4)
native: #01 pc 00000000000276ec /system/lib64/libc.so (ioctl+132)
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_2I+196)
at android.os.BinderProxy.transactNative(Native method) ————————————————關(guān)鍵行!!!
at android.os.BinderProxy.transact(Binder.java:804)
at android.net.IConnectivityManager$Stub$Proxy.getActiveNetworkInfo(IConnectivityManager.java:1204)—關(guān)鍵行!
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)
at com.xiaomi.frameworkbase.utils.NetworkUtils.isWifiConnected(NetworkUtils.java:1)
從堆棧可以看出獲取網(wǎng)絡(luò)信息發(fā)生了ANR:getActiveNetworkInfo。
系統(tǒng)的服務(wù)都是Binder機(jī)制(16個(gè)線程),服務(wù)能力也是有限的,有可能系統(tǒng)服務(wù)長(zhǎng)時(shí)間不響應(yīng)導(dǎo)致ANR。如果其他應(yīng)用占用了所有Binder線程,那么當(dāng)前應(yīng)用只能等待。
可進(jìn)一步搜索:blockUntilThreadAvailable關(guān)鍵字:
at android.os.Binder.blockUntilThreadAvailable(Native method)
如果有發(fā)現(xiàn)某個(gè)線程的堆棧,包含此字樣,可進(jìn)一步看其堆棧,確定是調(diào)用了什么系統(tǒng)服務(wù)。此類ANR也是屬于系統(tǒng)環(huán)境的問(wèn)題,如果某類型機(jī)器上頻繁發(fā)生此問(wèn)題,應(yīng)用層可以考慮規(guī)避策略。
以上就是全面解析Android之ANR日志的詳細(xì)內(nèi)容,更多關(guān)于Android ANR日志的資料請(qǐng)關(guān)注好吧啦網(wǎng)其它相關(guān)文章!
相關(guān)文章:
1. ASP基礎(chǔ)知識(shí)VBScript基本元素講解2. jsp中sitemesh修改tagRule技術(shù)分享3. JSP servlet實(shí)現(xiàn)文件上傳下載和刪除4. React優(yōu)雅的封裝SvgIcon組件示例5. ASP刪除img標(biāo)簽的style屬性只保留src的正則函數(shù)6. JavaWeb Servlet中url-pattern的使用7. php網(wǎng)絡(luò)安全中命令執(zhí)行漏洞的產(chǎn)生及本質(zhì)探究8. 輕松學(xué)習(xí)XML教程9. asp(vbscript)中自定義函數(shù)的默認(rèn)參數(shù)實(shí)現(xiàn)代碼10. 詳解瀏覽器的緩存機(jī)制
