前言
本文對Android中常發生的ANR現象的成因原理及主要發生場景進行了詳細介紹,舉例了幾種典型的ANR場景實體,總結提供了優化改善的若干解決思路,
值得Android開發人員收藏
簡介
ANR全稱:Application Not Responding,也就是應用程式無回應
針對Android中常發生的ANR現象,要解決此問題,當真要從其原理開始分析,如下圖所示

原因
Android系統中,ActivityManagerService(簡稱AMS)和WindowManagerService(簡稱WMS)會檢測App的回應時間,如果App在特定時間無法相應螢屏觸摸或鍵盤輸入時間,或者特定事件沒有處理完畢,就會出現ANR,其原因無非是以下四點:
- InputDispatching Timeout:5秒內無法回應螢屏觸摸事件或鍵盤輸入事件
- BroadcastQueue Timeout :在執行前臺廣播(BroadcastReceiver)的
onReceive()函式時10秒沒有處理完成,后臺為60秒, - Service Timeout :前臺服務20秒內,后臺服務在200秒內沒有執行完畢,
- ContentProvider Timeout :ContentProvider的publish在10s內沒進行完,
ANR分析辦法
ANR重現
這里使用的是號稱Google親兒子的Google Pixel xl(Android 8.0系統)做的測驗,生成一個按鈕跳轉到ANRTestActivity,在后者的onCreate()中主執行緒休眠20秒:
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_anr_test);
// 這是Android提供執行緒休眠函式,與Thread.sleep()最大的區別是
// 該使用該函式不會拋出InterruptedException例外,
SystemClock.sleep(20 * 1000);
}
在進入ANRTestActivity后黑屏一段時間,大概有七八秒,終于彈出了ANR例外

ANR分析辦法一:Log
剛才產生ANR后,看下Log:

可以看到logcat清晰地記錄了ANR發生的時間,以及執行緒的tid和一句話概括原因:
WaitingInMainSignalCatcherLoop,大概意思為主執行緒等待例外,
最后一句The application may be doing too much work on its main thread.
告知可能在主執行緒做了太多的作業,
ANR分析辦法二:traces.txt
剛才的log有第二句Wrote stack traces to '/data/anr/traces.txt',說明ANR例外已經輸出到traces.txt檔案,使用adb命令把這個檔案從手機里匯出來:
1、cd到adb.exe所在的目錄,也就是Android SDK的platform-tools目錄,例如:
cd D:\Android\AndroidSdk\platform-tools
此外,除了Windows的cmd以外,還可以使用AndroidStudio的Terminal來輸入adb命令,
2、到指定目錄后執行以下adb命令匯出traces.txt檔案:
adb pull /data/anr/traces.txt
traces.txt默認會被匯出到Android SDK的\platform-tools目錄,一般來說traces.txt檔案記錄的東西會比較多,分析的時候需要有針對性地去找相關記錄,
----- pid 23346 at 2017-11-07 11:33:57 ----- ----> 行程id和ANR產生時間
Cmd line: com.sky.myjavatest
Build fingerprint: 'google/marlin/marlin:8.0.0/OPR3.170623.007/4286350:user/release-keys'
ABI: 'arm64'
Build type: optimized
Zygote loaded classes=4681 post zygote classes=106
Intern table: 42675 strong; 137 weak
JNI: CheckJNI is on; globals=526 (plus 22 weak)
Libraries: /system/lib64/libandroid.so /system/lib64/libcompiler_rt.so
/system/lib64/libjavacrypto.so
/system/lib64/libjnigraphics.so /system/lib64/libmedia_jni.so /system/lib64/libsoundpool.so
/system/lib64/libwebviewchromium_loader.so libjavacore.so libopenjdk.so (9)
Heap: 22% free, 1478KB/1896KB; 21881 objects ----> 記憶體使用情況
...
"main" prio=5 tid=1 Sleeping ----> 原因為Sleeping
| group="main" sCount=1 dsCount=0 flags=1 obj=0x733d0670 self=0x74a4abea00
| sysTid=23346 nice=-10 cgrp=default sched=0/0 handle=0x74a91ab9b0
| state=S schedstat=( 391462128 82838177 354 ) utm=33 stm=4 core=3 HZ=100
| stack=0x7fe6fac000-0x7fe6fae000 stackSize=8MB
| held mutexes=
at java.lang.Thread.sleep(Native method)
- sleeping on <0x053fd2c2> (a java.lang.Object)
at java.lang.Thread.sleep(Thread.java:373)
- locked <0x053fd2c2> (a java.lang.Object)
at java.lang.Thread.sleep(Thread.java:314)
at android.os.SystemClock.sleep(SystemClock.java:122)
at com.sky.myjavatest.ANRTestActivity.onCreate(ANRTestActivity.java:20) ----> 產生ANR的包名以及具體行數
at android.app.Activity.performCreate(Activity.java:6975)
at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1213)
at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2770)
at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2892)
at android.app.ActivityThread.-wrap11(ActivityThread.java:-1)
at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1593)
at android.os.Handler.dispatchMessage(Handler.java:105)
at android.os.Looper.loop(Looper.java:164)
at android.app.ActivityThread.main(ActivityThread.java:6541)
at java.lang.reflect.Method.invoke(Native method)
at com.android.internal.os.Zygote$MethodAndArgsCaller.run(Zygote.java:240)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:767)
在檔案中使用 ctrl + F 查找包名可以快速定位相關代碼,
通過上方log可以看出相關問題:
- 行程id和包名:
pid 23346com.sky.myjavatest - 造成ANR的原因:
Sleeping - 造成ANR的具體行數:
ANRTestActivity.java:20類的第20行
特別注意:產生新的ANR,原來的 traces.txt 檔案會被覆寫,
ANR分析辦法三:Java執行緒呼叫分析
通過JDK提供的命令可以幫助分析和除錯Java應用,命令為:
jstack {pid}
其中pid可以通過jps命令獲得,jps命令會列出當前系統中運行的所有Java虛擬機行程,比如
7266 Test
7267 Jps
ANR分析辦法四:DDMS分析ANR問題
- 使用DDMS——Update Threads工具
- 閱讀Update Threads的輸出
造成ANR的原因及解決辦法
上面例子只是由于簡單的主執行緒耗時操作造成的ANR,造成ANR的原因還有很多:
- 主執行緒阻塞或主執行緒資料讀取
解決辦法:避免死鎖的出現,使用子執行緒來處理耗時操作或阻塞任務,盡量避免在主執行緒query provider、不要濫用SharePreferenceS
- CPU滿負荷,I/O阻塞
解決辦法:檔案讀寫或資料庫操作放在子執行緒異步操作,
- 記憶體不足
解決辦法:AndroidManifest.xml檔案<applicatiion>中可以設定 android:largeHeap="true",以此增大App使用記憶體,不過不建議使用此法,從根本上防止記憶體泄漏,優化記憶體使用才是正道,
- 各大組件ANR
各大組件生命周期中也應避免耗時操作,注意BroadcastReciever的onRecieve()、后臺Service和ContentProvider也不要執行太長時間的任務
本章節暫且結束,后續預告請往下看,如有需要后續文章資料,文末有領取方式
ANR原始碼分析

crash監控方案
執行緒監控 - 死鎖、存活周期與 CPU 占用率

啟動速度與執行效率優化專案實戰
Android卡頓檢測及優化
- 卡頓
- 幀率
- 卡頓原因
- 卡頓檢測
使用dumpsys gfxinfo
使用systrace
使用BlockCanary

記憶體優化
- Android記憶體優化工具
top
dumpsys meminfo
Memory Profiler
Leak Canary
MAT
記憶體問題高效分析方法 - Android記憶體泄漏分析及檢測工具LeakCanary簡介
- 安卓記憶體優化



「上圖展示的只是一部分」
由于文章篇幅有限,后續還有耗電優化、網路傳輸與資料存盤優化、apk大小優化、實戰專案等知識要點
有需要的可在我的QQ技術交流群里可以自助拿走,如果在學習或作業中遇到了問題,群里會有一些大神幫忙解答,有時你悶頭想一天,不如別人的三言兩語就醍醐灌頂,群793544421
加入圈子,免費領取
轉載請註明出處,本文鏈接:https://www.uj5u.com/qita/290811.html
標籤:其他
