ANR技術詳解

? ? ? 在我們平時開發(fā)的過程中,常常測試的同事會告訴我們在運行的時候偶現(xiàn)ANR錯誤或者monkey測試時,出現(xiàn)了ANR的錯誤,今天就從原理到真實的案例一步一步的教大家如何去分析ANR相關的問題,希望大家在下次遇到ANR問題時,可以更加從容不迫。


知識點匯總:

一:ANR出現(xiàn)的簡述與原理

二:ANR的類型與日志輸出

三:ANR錯誤類別匯總

四:ANR實例講解(相冊)

五:ANR分析總結

六:擴展閱讀


一:ANR出現(xiàn)的簡述與原理

描述:ANR(Application Not responding),是指應用程序未響應,Android系統(tǒng)對于一些事件需要在一定的時間范圍內完成,如果超過預定時間能未能得到有效響應或者響應時間過長,都會造成ANR。一般地,這時往往會彈出一個提示框,告知用戶當前xxx未響應,用戶可選擇繼續(xù)等待或者Force Close。

? ? ? ?在ANR觸發(fā)的詳細原理,可以最后參考網(wǎng)址仔細了解,在參考網(wǎng)址中,四大組件的觸發(fā)原理都略有不同,其中Activity的觸發(fā)原理與其他三者區(qū)別比較大,下面大概說一下ANR觸發(fā)的原理。

? ? ? ?在說ANR的原理前,普及一下系統(tǒng)相關的一些知識,在我們程序運行的過程中,有一個系統(tǒng)服務進程(systemService)一直默默的為我們服務,而有些系統(tǒng)服務我們是可以使用的,例如:調用接口getSystemService,可以使用一些系統(tǒng)提供給我們的服務,但有些系統(tǒng)服務我們是無法直接使用的,但是卻在我們程序運行的時候為我們服務,并起到了非常重要的效果,例如幾個核心系統(tǒng)服務:AMS(activityManagerService),PMS(packageManagerService),WMS(windowManagerService),而觸發(fā)ANR的函數(shù)就在AMS系統(tǒng)服務里面,叫appnotresponding()函數(shù)。


ANR的觸發(fā)原理:

???ANR是一套監(jiān)控Android應用響應是否及時的機制,可以把發(fā)生ANR比作是引爆炸彈,那么整個流程包含三部分組成:

???埋定時炸彈:中控系統(tǒng)(system_server進程)啟動倒計時,在規(guī)定時間內如果目標(應用進程)沒有干完所有的活,則中控系統(tǒng)會定向炸毀(殺進程)目標。

???拆炸彈:在規(guī)定的時間內干完工地的所有活,并及時向中控系統(tǒng)報告完成,請求解除定時炸彈,則幸免于難。

???引爆炸彈:中控系統(tǒng)立即封裝現(xiàn)場,抓取快照,搜集目標執(zhí)行慢的罪證(traces),便于后續(xù)的案件偵破(調試分析),最后是炸毀目標。

下面以Service為例子來簡單介紹一下ANR的原理:

service超時機制:

? ? ? ?從上面的圖我們可以看到,綠色的進程即為系統(tǒng)服務進程,而ANR對話框的觸發(fā)條件就是在系統(tǒng)進程AMS的appNotResponsing中觸發(fā)的,而藍色的進程運行的程序則為我們自己的進程,這里我用比較通俗的話來給大家解釋一下這個過程。

? ? ? ? 把綠色進程比做:老板,藍色進程比做:工人

? ? ? ? 老板在早上起來,先在自己身上放一個20秒(service 超時是20s)的定時炸彈,讓后發(fā)送消息告訴工人要干活了,并且要在規(guī)定的時間內干完活,并發(fā)送回老板干完活了,告訴老板可以拆炸彈了,這時老板就可以拆炸彈了,所以如果從老板埋炸彈開始,到收到拆炸彈過程,如果有任何一個環(huán)節(jié)出了問題,都有可能導致炸彈爆炸,即發(fā)送ANR(工人未能按時完成工作只是其中一種場景)?


? ?下面我列出有可能會導致ANR的原因:

一:發(fā)送消息到Service進程時,消息阻塞了,導致ANR。

二:Service進程任務處理時間超時,導致ANR。(最常見)

三:Service進程訪問內存對象時,對象被其他線程阻塞,導致ANR。

四:Service進程執(zhí)行任務時,主線程的時間片被其他線程搶占(GC線程),導致處理時間超時,導致ANR。

五:Service進程發(fā)送拆炸彈消息阻塞,導致系統(tǒng)進程延遲收到消息,導致ANR。


二:ANR的類型與日志輸出

? ?ANR分為四種類型,分別如下:

一:Service Timeout:比如前臺服務在20s內未執(zhí)行完成。

二:BroadcastQueue Timeout:比如前臺廣播在10s內未執(zhí)行完成。

三:ContentProvider Timeout:內容提供者,在publish過超時10s。

四:InputDispatching Timeout:? 輸入事件分發(fā)超時5s,包括按鍵和觸摸事件。(KeyDispatchTimeout)。

? ANR超時閾值:

? ? ? ?不同組件的超時閾值各有不同,關于service、broadcast、contentprovider以及input的超時閾值如下表:

? ? ? ? 這時候大家可能會覺得有點奇怪,為什么ANR會有后臺ANR,其實如果后臺進程一直被阻塞或者主線程一直做耗時操作,也有可能會導致后臺ANR的,那下面我們就區(qū)分一下,什么是后臺service和后臺broadcase吧。

? ? ? ? 進程優(yōu)先級(Adj):(前后臺服務ANR區(qū)分)

描述:進程調度組大體可分為TOP、前臺、后臺,其對應關系可粗略理解為Adj等于0的進程屬于Top進程組,Adj等于100或者200的進程屬于前臺進程組,Adj大于200的進程屬于后臺進程組,前臺服務準確來說,是指由處于前臺進程調度組的進程發(fā)起的服務。

后臺廣播ANR區(qū)分:根據(jù)發(fā)送廣播sendBroadcast(Intent intent)中的intent的flags是否包含F(xiàn)LAG_RECEIVER_FOREGROUND來決定把該廣播是放入前臺廣播隊列或者后臺廣播隊列,前臺廣播隊列的超時為10s,后臺廣播隊列的超時為60s,默認情況下廣播是放入后臺廣播隊列,除非指明加上FLAG_RECEIVER_FOREGROUND標識。


三:ANR的日志輸出

? ? ? ?對于service、broadcast、provider、input發(fā)生ANR后,中控系統(tǒng)會馬上去抓取現(xiàn)場的信息,用于調試分析。收集的信息包括如下:

1、將am_anr信息輸出到EventLog,也就是說ANR觸發(fā)的時間點最接近的就是EventLog中輸出的am_anr信息。

2、收集以下重要進程的各個線程調用棧trace信息,保存在data/anr/traces.txt文件 。

2.1、當前發(fā)生ANR的進程,system_server進程以及所有persistent進程

2.2、audioserver, cameraserver, mediaserver, surfaceflinger等重要的native進程

2.3、CPU使用率排名前5的進程

3、將發(fā)生ANR的reason以及CPU使用情況信息輸出到main log。

4、將traces文件和CPU使用情況信息保存到dropbox,即data/system/dropbox目錄。

5、對用戶可感知的進程則彈出ANR對話框告知用戶,對用戶不可感知的進程發(fā)生ANR則直接殺掉。

注意事項:

? ? ? ?獲取日志有一點需要注意,發(fā)生ANR后,不要選擇結束進程,因為這樣AMS會kill掉該進程,有些信息會打印不出來(比如MTK平臺上會生成db.XX.ANR,寫入到aee_exp文件夾下需要時間),最好是ANR發(fā)生后等兩三分鐘左右,再獲取日志。

? ? ? ?一般需要data/anr下生成的trace文件以及手機系統(tǒng)日志。


四:ANR錯誤類別匯總

類別一:主線程Binder調用等待超時

解析:很明顯當時在做Binder通信,并沒有waiting to lock等代表死鎖的字樣,那么說明這個案例即有可能是在等Binder對端響應,我們知道Binder通信對于發(fā)起方來說是默認是阻塞等待響應,只有有了返回結果后才會繼續(xù)執(zhí)行下去。

? ? ? ?這個進程當時在做什么,這時候就需要找到anr文件夾下另外一個文件binderinfo,這里需要找到與我們發(fā)起方進程1461通信的是哪個進程。

? ? 可以看到是1666號這個進程,再回到trace中看下,這個進程當時在做什么

解析:可以看到當時對端在做消息的讀取,也就是說這里出了問題,很明顯這里我們無法修改,我們這個問題在于主線程執(zhí)行了Binder請求,對端遲遲未返回便很容易出現(xiàn)這個問題,當前做法異步中執(zhí)行。


類別二:主線程等待鎖

解析:這個案例中gallery的main thread在執(zhí)行UploaderChimeraService的onDestroy方法時,需要lock 0x23f65d8b,但這個lock有被upload_periodic GCM Task 拿住,這個thread當前是在做連接網(wǎng)絡的動作。從這段信息來看,很有可能與測試時手機連接的網(wǎng)絡有關,當時連接的事google的網(wǎng)絡,由于墻的原因,無法連接gms的相關server有關

? ? ? ? 還有一種情況就是死鎖,即形成了頭尾相連,互相等待的情況,對于這種問題以及上面案例的解決,一般會嘗試將鎖改為超時鎖,比如lock的trylock,超時會自動釋放鎖,從而避免一直持有鎖的情況發(fā)生。


類別三:卡在IO上

? ? ? ?這種情況一般是和文件操作相關,判斷是否是這種情況,可以看main log中搜索關鍵字“ANR in”,看這段信息的最下邊,比如下面的信息:

??ANRManager: 100% TOTAL: 2% user + 2.1% kernel + 95% iowait + 0.1% softirq

很明顯,IO占比很高,這個時候就需要查看trace日志看當時的callstack,或者在這段ANR點往前看0~4s,看看當時做的什么文件操作,這種場景有遇到過,常見解決方法是對耗時文件操作采取異步操作。


類別四:主線程有耗時的動作

? ? ? ?這種情況是ANR類型問題里遇到最多的,比如網(wǎng)絡訪問,訪問數(shù)據(jù)庫之類的,都很容易造成主線程堵塞,

這里以訪問數(shù)據(jù)庫來說,這類型引起的ANR,一般來講看當時的CPU使用情況會發(fā)現(xiàn)user占比較高,看trace中主線程當時的信息會發(fā)現(xiàn)會有一些比如query像ContentProvider這種數(shù)據(jù)庫的動作。這種情況下,還可以去看eventlog或者mainlog,在ANR發(fā)生前后打印出來的信息,比如訪問數(shù)據(jù)庫這種,在eventlog中搜索"am_anr",然后看前后片段,會發(fā)現(xiàn)發(fā)生ANR的這個進程有很多數(shù)據(jù)庫相關的信息,說明在發(fā)生ANR前后主線程一直在忙于訪問數(shù)據(jù)庫,這類型的問題常見于圖庫,聯(lián)系人,彩短信應用。

? ? ? ?所以這種問題的解決,一般考慮的是異步解決,異步解決并不是簡單的new一個線程,要根據(jù)業(yè)務場景以及頻率來決定,Android常見的異步AsyncTask, IntentService, 線程池(官方四種或自定義), new thread等,一般來說不建議直接new thread。

??特殊案例:Sharepreference操作導致的ANR。


類別五:binder線程池被占滿

? ? ? ? 系統(tǒng)對每個process最多分配15個binder線程,這個是谷歌的設計(/frameworks/native/libs/binder/ProcessState.cpp)

? ? ? ?如果另一個process發(fā)送太多重復binder請求,那么就會導致接收端binder線程被占滿,從而處理不了其它的binder請求,這時候請求端發(fā)起的請求就會阻塞等待了(未設置異步請求的前提下),這本身就是系統(tǒng)的一個限制,如果應用未按照系統(tǒng)的要求來實現(xiàn)對應邏輯,那么就會造成問題。

? ? ? ?而系統(tǒng)端是不會(也不建議)通過修改系統(tǒng)行為來兼容應用邏輯,否則更容易造成其它根據(jù)系統(tǒng)需求正常編寫的應用反而出現(xiàn)不可預料的問題。

? ? ? ?判斷Binder是否用完,可以在trace中搜索關鍵字“binder_f”,如果搜索到則表示已經用完,然后就要找log其他地方看是誰一直在消耗binder或者是有死鎖發(fā)生,之前有遇到過壓力測試手電筒應用,出現(xiàn)Binder線程池被占滿情況,解決的思路就是降低極短時間內大量Binder請求的發(fā)生,修復的手法是發(fā)送BInder請求的函數(shù)中做時間差過濾,限定在500ms內最多執(zhí)行一次。


類別六:只存在于Monkey測試下

? ? ? ?有些問題是只有在Monkey環(huán)境下才能跑出來,平時的user版本用戶使用是不會出現(xiàn)的,這種問題的話就沒有改動的意義。

比如下面這個例子:

ActivityManager: Not finishing activity because controller resumed

03-18 07:25:50.901 810 870 I am_anr : [0,25443,android.process.media,1086897733,Input dispatching timed out (Waiting because no window has focus but there is a focused application that may eventually add a window when it finishes starting up.)]

? ? ? ? 發(fā)生這個ANR的原因是Contoller將resume的操作給攔截了, 導致Focus不過去, 從而導致ANR,User版本不會有Contoller, 所以不會出現(xiàn)這個 ANR. 所以這個 ANR 可以忽略。

? ? ? ? 下面我會有兩個實例,是我在項目開發(fā)的過程中解決的兩個ANR。


四:ANR實例講解(相冊)

實例一:#9531:相冊】【Dev6】【偶現(xiàn)】照片tab存在大量照片(如3000張),折疊狀態(tài)快速滑動后展開至大屏,出現(xiàn)“相冊沒有響應”(詳情見附件)

一:logcat-event-log( EventLog )文件搜索關鍵字am_anr:(非必需)

ANR錯誤日志:

09-17 14:12:00.406? 1187? 1352 I am_anr? : [0,5608,org.codeaurora.gallery,953728069,Input dispatching timed out (Waiting to send non-key event because the touched window has not finished processing certain input events that were delivered to it over 500.0ms ago.? Wait queue length: 17.? Wait queue head age: 5521.1ms.)]

二:logcat-log( main log )文件搜索關鍵字anr in:(非必需)

09-17 14:12:03.779? 1187? 1352 E ActivityManager: ANR in org.codeaurora.gallery (org.codeaurora.gallery/com.android.gallery3d.app.GalleryActivity)

09-17 14:12:03.779? 1187? 1352 E ActivityManager: PID: 5608

09-17 14:12:03.779? 1187? 1352 E ActivityManager: Reason: Input dispatching timed out (Waiting to send non-key event because the touched window has not finished processing certain input events that were delivered to it over 500.0ms ago.? Wait queue length: 17.? Wait queue head age: 5521.1ms.)

09-17 14:12:03.779? 1187? 1352 E ActivityManager: Load: 9.33 / 5.85 / 3.35

09-17 14:12:03.779? 1187? 1352 E ActivityManager: CPU usage from 21279ms to 0ms ago (2019-09-17 14:11:39.112 to 2019-09-17 14:12:00.391):

09-17 14:12:03.779? 1187? 1352 E ActivityManager:?? 31% 1187/system_server: 11% user + 19% kernel / faults: 4240 minor

09-17 14:12:03.779? 1187? 1352 E ActivityManager:?? 20% 5608/org.codeaurora.gallery: 9.4% user + 10% kernel / faults: 17590 minor

三:Traces.txt日志分析,搜索關鍵字:tid=1

"main" prio=5 tid=1 Native

? | group="main" sCount=1 dsCount=0 flags=1 obj=0x75bbb9f0 self=0x7977a14c00

? | sysTid=4581 nice=-10 cgrp=default sched=0/0 handle=0x79fda63548

? | state=S schedstat=( 8102631431 3226315969 19028 ) utm=598 stm=211 core=5 HZ=100

? | stack=0x7fdd543000-0x7fdd545000 stackSize=8MB

? | held mutexes=kernel: (couldn't read /proc/self/task/4581/stack)

? native: #00 pc 000000000001f22c? /system/lib64/libc.so (syscall+28)

? native: #01 pc 00000000000d7278? /system/lib64/libart.so (art::ConditionVariable::WaitHoldingLocks(art::Thread*)+148)

? native: #02 pc 0000000000513e70? /system/lib64/libart.so

? at android.os.BinderProxy.transactNative(Native method)

? at android.os.BinderProxy.transact(Binder.java:1127)

? at android.hardware.display.IDisplayManager$Stub$Proxy.getDisplayInfo(IDisplayManager.java:404)

? at android.hardware.display.DisplayManagerGlobal.getDisplayInfo(DisplayManagerGlobal.java:124)

? - locked <0x08e21a90> (a java.lang.Object)

? at android.view.Display.updateDisplayInfoLocked(Display.java:1056)

? at android.view.Display.getSize(Display.java:613)

? - locked <0x07bd5f89> (a android.view.Display)

? at com.bumptech.glide.request.target.ViewTarget$SizeDeterminer.getDisplayDimens(ViewTarget.java:267)

? at com.bumptech.glide.request.target.ViewTarget$SizeDeterminer.getSizeForParam(ViewTarget.java:250)

? at com.bumptech.glide.request.target.ViewTarget$SizeDeterminer.getViewHeightOrParam(ViewTarget.java:231)

? at com.bumptech.glide.request.target.ViewTarget$SizeDeterminer.getSize(ViewTarget.java:209)

? at com.bumptech.glide.request.target.ViewTarget.getSize(ViewTarget.java:100)

? at com.bumptech.glide.request.GenericRequest.begin(GenericRequest.java:272)

? at com.bumptech.glide.manager.RequestTracker.runRequest(RequestTracker.java:37)

? at com.bumptech.glide.GenericRequestBuilder.into(GenericRequestBuilder.java:661)

? at com.bumptech.glide.GenericRequestBuilder.into(GenericRequestBuilder.java:697)

? at com.bumptech.glide.BitmapRequestBuilder.into(BitmapRequestBuilder.java:498)

? at com.android.gallery.ui.home.timelinepage.TimelinePageAdapter$MediaItemAdapter.setPhotoItem

(TimelinePageAdapter.java:215)

? at com.android.gallery.ui.home.timelinepage.TimelinePageAdapter$MediaItemAdapter.convert(TimelinePageAdapter.java:193)

? at com.android.gallery.ui.home.timelinepage.TimelinePageAdapter$MediaItemAdapter.convert(TimelinePageAdapter.java:178)

? ? ? ?通過查看日志,我們可以看到是開源框架里面的代碼發(fā)生ANR了,再結合測試同事的操作,我發(fā)現(xiàn)是在快速滑動的時候,如果轉屏,glide框架大量重復執(zhí)行不同圖片的裁剪動作,因為旋轉屏幕后imageview的大小發(fā)生變化,導致裁剪的圖片發(fā)生變化。

? ?解決思路一:在轉屏的時候停止列表的滑動(stopScrolling)

? ?解決思路二:不要調用裁剪圖片的函數(shù),改用其他實現(xiàn)方式。

實例二:#9587【相冊】【DEV6】照片tab存在1000張照片,在照片tab全選刪除照片,提示“相冊沒有響應”(非必須)

一:logcat-event-log( EventLog )文件搜索關鍵字am_anr:(非必需)

ANR錯誤日志:

09-19 10:27:20.760? 1213? 1383 I am_anr? : [0,10929,org.codeaurora.gallery,953728069,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: 4.)]

? ? ? ?上面這行表示ANR類型為Input dispatching timed out, 這種anr的原因的是在viewrootimpl分發(fā)事件時,并沒有找到focuswindow導致的,這一步確認時間點在:10:27:20 , 進程號:10929。

二:logcat-log( main log )文件搜索關鍵字anr in:(非必需)

09-19 10:27:26.147? 1213? 1383 E ActivityManager: ANR in org.codeaurora.gallery (org.codeaurora.gallery/com.android.gallery3d.app.GalleryActivity)

09-19 10:27:26.147? 1213? 1383 E ActivityManager: PID: 10929

09-19 10:27:26.147? 1213? 1383 E ActivityManager: 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: 4.)

09-19 10:27:26.147? 1213? 1383 E ActivityManager: Load: 3.52 / 3.93 / 3.29

09-19 10:27:26.147? 1213? 1383 E ActivityManager: CPU usage from 0ms to 5386ms later (2019-09-19 10:27:20.726 to 2019-09-19 10:27:26.112) with 99% awake:

09-19 10:27:26.147? 1213? 1383 E ActivityManager:?? 38% 1213/system_server: 14% user + 24% kernel / faults: 12458 minor 13 major

09-19 10:27:26.147? 1213? 1383 E ActivityManager:?? 0.7% 950/media.codec: 0.4% user + 0.3% kernel / faults: 15822 minor 4 major

09-19 10:27:26.147? 1213? 1383 E ActivityManager:?? 18% 743/android.hardware.sensors@1.0-service: 5% user + 12% kernel / faults: 744 minor

09-19 10:27:26.147? 1213? 1383 E ActivityManager:?? 14% 2109/com.android.systemui: 8.7% user + 5.9% kernel / faults: 3454 minor

09-19 10:27:26.147? 1213? 1383 E ActivityManager:?? 8.7% 4403/com.sohu.inputmethod.sogou.oem: 5.1% user + 3.5% kernel / faults: 2317 minor 8 major

查看ANR時的CPU以及IO率(可選)

? ? ? ?這一步一般來說能基本定位是什么造成了ANR,是IO高還是CPU高,如兩者都不是,需進入第三步trace日志分析環(huán)節(jié),查看mobilelog文件夾下的main_log,搜索關鍵字"ANR in",可以看到當時的CPU以及IO率,這一個環(huán)節(jié)一般來講主要是看發(fā)生ANR時的CPU使用情況,CPU是否吃緊,

? ? ? ?還有需要注意iowait的占有率,如果占比比較高,則排查的方向要傾向與讀取文件操作有關的信息,可以看trace日志中有沒有一些讀取文件或者操作SD卡的動作。

anr_2019-09-19-10-27-21-129(traces.txt)文件搜索關鍵字tid=1:

"main" prio=5 tid=1 Blocked

? | group="main" sCount=1 dsCount=0 flags=1 obj=0x759d2fa8 self=0x7192c14c00

? | sysTid=10929 nice=-10 cgrp=default sched=0/0 handle=0x7218c2f548

? | state=S schedstat=( 12920555277 3219927306 32391 ) utm=981 stm=310 core=4 HZ=100

? | stack=0x7fe00ff000-0x7fe0101000 stackSize=8MB

? | held mutexes=

? at com.android.gallery.data.reposity.AlbumSetAllDataLoader$ReloadTask.notifyDirty(AlbumSetAllDataLoader.java:-1)

? - waiting to lock <0x0aefa2a7> (a com.android.gallery.data.reposity.AlbumSetAllDataLoader$ReloadTask) held by thread 58

? at com.android.gallery.data.reposity.AlbumSetAllDataLoader$MySourceListener.onContentDirty(AlbumSetAllDataLoader.java:202)

? at com.android.gallery3d.data.MediaSet.notifyContentChanged(MediaSet.java:254)

? - locked <0x0fc15354> (a java.lang.Object)

? at com.android.gallery3d.data.ClusterAlbumSet.onContentDirty(ClusterAlbumSet.java:107)

? at com.android.gallery3d.data.MediaSet.notifyContentChanged(MediaSet.java:254)

? - locked <0x03ac42fd> (a java.lang.Object)

? atcom.android.gallery3d.data.ComboAlbumSet.onContentDirty(ComboAlbumSet.java:103)

? at com.android.gallery3d.data.MediaSet.notifyContentChanged(MediaSet.java:254)

? - locked <0x096fcbf2> (a java.lang.Object)

at com.android.gallery3d.data.ChangeNotifier.onChange(ChangeNotifier.java:54)

? at com.android.gallery3d.data.DataManager$NotifyBroker.onChange(DataManager.java:450)

? - locked <0x03e9c243> (a com.android.gallery3d.data.DataManager$NotifyBroker)

? at android.database.ContentObserver.onChange(ContentObserver.java:130)

? at android.database.ContentObserver.onChange(ContentObserver.java:145)

? at android.database.ContentObserver$NotificationRunnable.run(ContentObserver.java:216)

? at android.os.Handler.handleCallback(Handler.java:873)

? at android.os.Handler.dispatchMessage(Handler.java:99)

? at android.os.Looper.loop(Looper.java:193)

? at android.app.ActivityThread.main(ActivityThread.java:6766)

? at java.lang.reflect.Method.invoke(Native method)

? at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:493)

? at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:858)

"Thread-112" prio=5 tid=58 Blocked

? | group="main" sCount=1 dsCount=0 flags=1 obj=0x138409f0 self=0x71758a3000

? | sysTid=11785 nice=10 cgrp=default sched=0/0 handle=0x716e7ff4f0

? | state=S schedstat=( 131607643 242516728 1013 ) utm=9 stm=3 core=0 HZ=100

? | stack=0x716e6fc000-0x716e6fe000 stackSize=1041KB

? | held mutexes=

? at com.android.gallery3d.data.ClusterAlbumSet.isLoading(ClusterAlbumSet.java:-1)

? - waiting to lock <0x02ba1cd8> (a com.android.gallery3d.data.ClusterAlbumSet) held by thread 56

? at com.android.gallery.data.reposity.AlbumSetAllDataLoader$ReloadTask.run(AlbumSetAllDataLoader.java:369)

? - locked <0x0aefa2a7> (a com.android.gallery.data.reposity.AlbumSetAllDataLoader$ReloadTask)

解決思路一:由于主線程頻繁的執(zhí)行工作線程的函數(shù)notifyDirty(),而當工作線程也被阻塞時,主線程執(zhí)行的函數(shù)無法在規(guī)定時間內完成工作,解決思路:減少主線程調用notifyDirty()的屏幕,解決此問題。

解決思路二:解決56線程的阻塞問題也可能可以解決此問題。

Log查看總結:

一:、logcat-event-log( EventLog )文件搜索關鍵字am_anr:(非必需)

解析:04-29 10:00:57.240 1267 1341 I am_anr : [0,6073,com.android.dialer,952745541,Input dispatching timed out。

二:logcat-log( main log )文件搜索關鍵字anr in:(非必需)

解析:可以看到當時的CPU以及IO率,這一個環(huán)節(jié)一般來講主要是看發(fā)生ANR時的CPU使用情況,CPU是否吃緊,

還有需要注意iowait的占有率,如果占比比較高,則排查的方向要傾向與讀取文件操作有關的信息,可以看trace日志中有沒有一些讀取文件或者操作SD卡的動作。

三:anr_2019-09-19-10-27-21-129(traces.txt)文件搜索關鍵字tid=1:

解析:一般來講直接先看tid=1的堆棧即對應主線程,因為ANR都是主線程執(zhí)行超時導致。


五:ANR分析總結

? ? ? ?先定位發(fā)生ANR時間點,然后查看trace信息,接著分析是否有耗時的message、binder調用,鎖的競爭,CPU資源的搶占,以及結合具體場景的上下文來分析,調試手段就需要針對前面說到的message、binder、鎖等資源從系統(tǒng)角度細化更多debug信息,這里不再展開,后續(xù)再以ANR案例來講解。

? ? ? ?作為應用開發(fā)者應讓主線程盡量只做UI相關的操作,避免耗時操作,比如過度復雜的UI繪制,網(wǎng)絡操作,文件IO操作;避免主線程跟工作線程發(fā)生鎖的競爭,減少系統(tǒng)耗時binder的調用,謹慎使用SharePreference ,注意主線程執(zhí)行provider query操作。簡而言之,盡可能減少主線程的負載,讓其空閑待命,以期可隨時響應用戶的操作。

??附加:當SharePreference操作發(fā)生ANR的場景:

1、 SharePreference主線程 getXX 方法會 ANR。

2、 SharePreference apply 方法會 ANR。

3、 SharePreference主線程調用 commit 方法 ANR。

解決思路:在主線程調用 commit 方法會出現(xiàn) ANR,可以將所有的 commit 任務放到單線程池的線程里去執(zhí)行。

( SingleThreadPool )


六:擴展閱讀

1、http://gityuan.com/2019/04/06/android-anr/(徹底理解安卓應用無響應機制)

2、http://gityuan.com/2016/07/02/android-anr/(理解Android ANR的觸發(fā)原理)

3、https://www.itdecent.cn/p/653aef57ae51(SharedPreferences ANR 總結)

4、https://www.itdecent.cn/p/3959a601cea6(ANR問題一般解決思路 )

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
【社區(qū)內容提示】社區(qū)部分內容疑似由AI輔助生成,瀏覽時請結合常識與多方信息審慎甄別。
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發(fā)布,文章內容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

友情鏈接更多精彩內容