Android性能優(yōu)化之ANR問(wèn)題定位分析
前言
ANR(Application Not Response)應(yīng)用程序未響應(yīng),當(dāng)主線程被阻塞時(shí),就會(huì)彈出如下彈窗
要么關(guān)閉當(dāng)前app,要么就等待,其實(shí)這個(gè)時(shí)候沒(méi)有挽救的措施,選擇等待最終的結(jié)果也是ANR,最終都需要?dú)⒌魬?yīng)用進(jìn)程,我們看下日志,原因是Input dispatching timed out,點(diǎn)擊事件處理超時(shí)導(dǎo)致ANR。
2022-08-27 16:11:53.168 2057-2080/system_process E/ActivityManager: ANR in com.lay.image_process (com.lay.image_process/.MainActivity) PID: 31848 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: 6. Wait queue head age: 5525.3ms.)
其實(shí)相對(duì)于其他的錯(cuò)誤,ANR比較棘手在于,沒(méi)有崩潰日志,定位問(wèn)題比較困難,而且ANR是必須要解決的問(wèn)題,這個(gè)用戶(hù)體驗(yàn)極差,因此本章的核心在于攻堅(jiān)ANR問(wèn)題。
1 ANR原因總結(jié)
從上面的日志中,我們看到造成ANR的原因是Input dispatching timed out,那么除此之外,還有什么其他的錯(cuò)誤。
1.1 KeyDispatchTimeout
input事件在5s之內(nèi)沒(méi)有處理,產(chǎn)生ANR;這種異常是比較常見(jiàn)的問(wèn)題,常發(fā)生在點(diǎn)擊事件處理中,logcat的關(guān)鍵字就是Input dispatching timed out
這里需要說(shuō)明一點(diǎn),Input事件導(dǎo)致ANR跟下面幾種不同的是,看下面的代碼,點(diǎn)擊按鈕5s后,才彈出toast,這種情況下會(huì)發(fā)生ANR嗎?
btnSend.setOnClickListener { Thread.sleep(5 * 1000) ToastUtils.setText(this) }
我們可以在私下測(cè)試一下,其實(shí)是不會(huì)的,如果用戶(hù)后續(xù)沒(méi)有繼續(xù)輸入事件,那么就不會(huì)產(chǎn)生ANR
1.2 BroadCastTimeout
class MyBroadCast : BroadcastReceiver(){ override fun onReceive(context: Context?, intent: Intent?) { //TODO 接收廣播 } }
我們?cè)谑褂脧V播接收器接收廣播時(shí),需要重寫(xiě)B(tài)roadcastReceiver的onReceive方法,當(dāng)前方法是在主線程中,如果在10s內(nèi)沒(méi)有處理彎沉,就會(huì)ANR。
因此,在onReceive方法中不能做耗時(shí)操作,如果需要?jiǎng)t需要?jiǎng)?chuàng)建新的線程。logcat關(guān)鍵字是 Timeout of broadcast BroadcastRecord
1.3 ServiceTimeout
class MyService : Service(){ override fun onCreate() { super.onCreate() } override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int { return super.onStartCommand(intent, flags, startId) } override fun onBind(intent: Intent?): IBinder? { TODO("Not yet implemented") } }
同樣,在Service中依然不能做耗時(shí)操作,如onCreate、onStartCommand、onBind方法中如果超過(guò)20s沒(méi)有處理完成,就會(huì)ANR。
所以如果需要在服務(wù)中執(zhí)行耗時(shí)操作,建議使用IntentService,logcat關(guān)鍵字是 Timeout executing service
1.4 ContentProviderTimeout
四大組件最后一個(gè)組件,如果ContentProvider在10內(nèi)沒(méi)有處理就會(huì)導(dǎo)致ANR,這個(gè)組件使用很少,暫時(shí)先不分析
綜上所述,如果出現(xiàn)ANR,主要原因就是在主線程執(zhí)行了耗時(shí)操作,導(dǎo)致UI線程被阻塞發(fā)生ANR;那么在我們的實(shí)際項(xiàng)目中,有哪些操作,可能會(huì)導(dǎo)致ANR呢?
1. 主線程進(jìn)行頻繁的IO操作
不知道我們還有多少在使用SP存儲(chǔ)的,其實(shí)它底層就是通過(guò)IO讀寫(xiě)操作文件,如果頻繁地在主線程進(jìn)行SP讀寫(xiě)可能會(huì)造成卡頓或者ANR,之前就有過(guò)線上的ANR事故,建議大家都是用MMKV,讀寫(xiě)速度秒殺SP;
除此之外,主線程進(jìn)行網(wǎng)絡(luò)操作也會(huì)導(dǎo)致ANR
2. 多線程爭(zhēng)奪資源導(dǎo)致死鎖3. CPU資源耗盡
等等......
2 ANR問(wèn)題解決
2.1 線下問(wèn)題解決
如果在我們實(shí)際的開(kāi)發(fā)過(guò)程中,如果出現(xiàn)ANR,那么很簡(jiǎn)單,打開(kāi)logcat窗口就可以查看,還有一種方式,就是查看trace日志,路徑為 /data/anr/xxx
打開(kāi)trace日志,通過(guò)這一部分就能猜到具體原因了,就是因?yàn)樵谥骶€程中,響應(yīng)點(diǎn)擊事件時(shí),線程進(jìn)入休眠阻塞
線下出問(wèn)題對(duì)于我們來(lái)說(shuō)永遠(yuǎn)都是最簡(jiǎn)單的,難的就是在線上出了問(wèn)題,用戶(hù)隔你十萬(wàn)八千里,該如何處理?
2.2 線上問(wèn)題解決
2.2.1 Bugly
可能很多小伙伴的項(xiàng)目中都集成了bugly,確實(shí)bugly是很不錯(cuò)的線上監(jiān)控組件,像Crash、ANR都能夠檢測(cè)到,但是很多時(shí)候,日志是不全的,堆棧信息不全就沒(méi)法定位問(wèn)題,bugly可以作為兜底方案,具體的監(jiān)控方案,我們可以自己實(shí)現(xiàn)。
2.2.2 FileObserver
對(duì)于線上監(jiān)控,往往有兩種方式,我這邊先講解第一種,通過(guò)FileObserver監(jiān)聽(tīng)某個(gè)目錄下文件是否發(fā)生變化,這里不言而喻了,就是/data/anr/xxx,如果當(dāng)前文件夾中的文件發(fā)生變化,那么意味著ANR發(fā)生了,首先我們先了解一個(gè)這個(gè)類(lèi)。
@Deprecated public FileObserver(String path) { this(new File(path)); } /** * Equivalent to FileObserver(file, FileObserver.ALL_EVENTS). */ public FileObserver(@NonNull File file) { this(Arrays.asList(file)); } /** * Equivalent to FileObserver(paths, FileObserver.ALL_EVENTS). * * @param files The files or directories to monitor */ public FileObserver(@NonNull List<File> files) { this(files, ALL_EVENTS); }
我們先看一下其中比較核心的構(gòu)造方法,F(xiàn)ileObserver能夠監(jiān)聽(tīng)某個(gè)路徑下的文件、某個(gè)文件或者文件集合的變化,F(xiàn)ileObserver是一個(gè)抽象類(lèi),那么我們可以實(shí)現(xiàn)它來(lái)監(jiān)聽(tīng)anr目錄文件的變化
@IntDef(flag = true, value = { ACCESS, MODIFY, ATTRIB, CLOSE_WRITE, CLOSE_NOWRITE, OPEN, MOVED_FROM, MOVED_TO, CREATE, DELETE, DELETE_SELF, MOVE_SELF })
具體的文件狀態(tài)有以上這些,包括ACCESS(當(dāng)前文件被訪問(wèn)了)、MODIFY(當(dāng)前文件被修改了)、CREATE(當(dāng)前文件被創(chuàng)建了)、DELETE(當(dāng)前文件被刪除了)等等
class ANRFileObserver( val anrPath: String ) : FileObserver(anrPath) { override fun onEvent(event: Int, path: String?) { when(event){ ACCESS->{ } MODIFY->{ } DELETE->{ } CREATE->{ } } } }
這里主要是檢測(cè)這4種狀態(tài),當(dāng)前文件夾下內(nèi)容有修改時(shí),就將全部的trace文件上傳到服務(wù)端進(jìn)行日志查看。
val observer = ANRFileObserver("/data/anr/") observer.startWatching()
但是這里需要注意的就是,很多高版本的ROM已經(jīng)不支持當(dāng)前文件夾的查看,甚至需要Root,因此此策略暫時(shí)不能應(yīng)用,那么除此之外,還可以通過(guò)WatchDog來(lái)監(jiān)控線程狀態(tài),從而判斷是否發(fā)生ANR。
2.2.3 WatchDog
從字面意思上看,就是看門(mén)狗,其實(shí)這個(gè)是Android系統(tǒng)中的一種監(jiān)控機(jī)制,當(dāng)SystemServer進(jìn)程啟動(dòng),調(diào)用start方法之后,WatchDog也就啟動(dòng)了run方法
從上面這張圖可以理解WatchDog的原理:首先WatchDog是一個(gè)線程,每隔5s發(fā)送一個(gè)Message消息到主線程的MessageQueue中,主線程Looper從消息隊(duì)列中取出Message,如果沒(méi)有阻塞,那么在5s內(nèi)會(huì)執(zhí)行這個(gè)Message任務(wù),就沒(méi)有ANR;如果超過(guò)5s沒(méi)有執(zhí)行,那么就有可能出現(xiàn)ANR。
到此這篇關(guān)于Android性能優(yōu)化之ANR問(wèn)題定位分析的文章就介紹到這了,更多相關(guān)Android ANR 內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
android實(shí)現(xiàn)Uri獲取真實(shí)路徑轉(zhuǎn)換成File的方法
這篇文章主要介紹了android實(shí)現(xiàn)Uri獲取真實(shí)路徑轉(zhuǎn)換成File的方法,涉及Android操作路徑的相關(guān)技巧,需要的朋友可以參考下2015-05-05Android 擴(kuò)大 View 的點(diǎn)擊區(qū)域的方法
這篇文章主要介紹了Android 擴(kuò)大 View 的點(diǎn)擊區(qū)域的方法,小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧2018-04-04Android TextView中文字通過(guò)SpannableString設(shè)置屬性用法示例
這篇文章主要介紹了Android TextView中文字通過(guò)SpannableString設(shè)置屬性用法,結(jié)合實(shí)例形式分析了TextView控件中SpannableString類(lèi)相關(guān)屬性的使用技巧,需要的朋友可以參考下2016-08-08Android onTouchEvent事件中onTouch方法返回值(介紹)
下面小編就為大家?guī)?lái)一篇Android onTouchEvent事件中onTouch方法返回值(介紹)。小編覺(jué)得挺不錯(cuò)的,現(xiàn)在就分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧2017-04-04Android下載進(jìn)度監(jiān)聽(tīng)和通知的處理詳解
這篇文章主要為大家詳細(xì)介紹了Android下載進(jìn)度監(jiān)聽(tīng)和通知的處理,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2017-08-08深入理解Android熱修復(fù)技術(shù)原理之so庫(kù)熱修復(fù)技術(shù)
通常情況下,大多數(shù)人希望android下熱補(bǔ)丁方案能夠做到補(bǔ)丁的全方位修復(fù),包括類(lèi)修復(fù)/資源修復(fù)/so庫(kù)的修復(fù)。 這里主要介紹熱補(bǔ)丁之so庫(kù)修復(fù)思路2021-06-06Jetpack?Compose慣性衰減動(dòng)畫(huà)AnimateDecay詳解
這篇文章主要為大家介紹了Jetpack?Compose慣性衰減動(dòng)畫(huà)AnimateDecay詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2022-11-11Android Studio 3.1.3升級(jí)至3.6.1后舊項(xiàng)目的兼容操作方法
這篇文章主要介紹了Android Studio 3.1.3升級(jí)至3.6.1后舊項(xiàng)目的兼容操作方法,本文給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2020-03-03