說說Android中的ANR
有過Android開發經歷的人都不會對ANR陌生,它和崩潰一樣是程序設計的問題。本文將以較為深入的視角來介紹什么是ANR,出現場景,如何避免以及如何定位分析ANR,希望可以幫助大家在編寫程序時有所幫助。
什么是ANR
ANR全稱Application Not Responding,意思就是程序未響應。如果一個應用無法響應用戶的輸入,系統就會彈出一個ANR對話框,如下圖所示,用戶可以自行選擇繼續等待亦或者是停止當前程序。
出現場景
- 主線程被IO操作(從4.0之后網絡IO不允許在主線程中)阻塞。
- 主線程中存在耗時的計算
- 主線程中錯誤的操作,比如Thread.wait或者Thread.sleep等
Android系統會監控程序的響應狀況,一旦出現下面兩種情況,則彈出ANR對話框
- 應用在 5秒 內未響應用戶的輸入事件(如按鍵或者觸摸)
- BroadcastReceiver未在 10秒 內完成相關的處理
如何避免
基本的思路就是將IO操作在工作線程來處理,減少其他耗時操作和錯誤操作
- 使用AsyncTask處理耗時IO操作。
- 使用Thread或者HandlerThread時,調用Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND)設置優先級,否則仍然會降低程序響應,因為默認Thread的優先級和主線程相同。
- 使用Handler處理工作線程結果,而不是使用Thread.wait()或者Thread.sleep()來阻塞主線程。
- Activity的onCreate和onResume回調中盡量避免耗時的代碼
- BroadcastReceiver中onReceive代碼也要盡量減少耗時,建議使用IntentService處理。
畫龍點睛
通常100到200毫秒就會讓人察覺程序反應慢,為了更加提升響應,可以使用下面的幾種方法
- 如果程序正在后臺處理用戶的輸入,建議使用讓用戶得知進度,比如使用ProgressBar控件。
- 程序啟動時可以選擇加上歡迎界面,避免讓用戶察覺卡頓。
- 使用Systrace和TraceView找出影響響應的問題。
如何定位
如果開發機器上出現問題,我們可以通過查看/data/anr/traces.txt即可,最新的ANR信息在最開始部分。我們從stacktrace中即可找到出問題的具體行數。本例中問題出現在MainActivity.java 27行,因為這里調用了Thread.sleep方法。
root@htc_m8tl:/ # cat /data/anr/traces.txt | more ----- pid 30307 at 2015-05-30 14:51:14 ----- Cmd line: com.example.androidyue.bitmapdemo JNI: CheckJNI is off; workarounds are off; pins=0; globals=272 DALVIK THREADS: (mutexes: tll=0 tsl=0 tscl=0 ghl=0) "main" prio=5 tid=1 TIMED_WAIT | group="main" sCount=1 dsCount=0 obj=0x416eaf18 self=0x416d8650 | sysTid=30307 nice=0 sched=0/0 cgrp=apps handle=1074565528 | state=S schedstat=( 0 0 0 ) utm=5 stm=4 core=3 at java.lang.VMThread.sleep(Native Method) at java.lang.Thread.sleep(Thread.java:1044) at java.lang.Thread.sleep(Thread.java:1026) at com.example.androidyue.bitmapdemo.MainActivity$1.run(MainActivity.java:27) at android.app.Activity.runOnUiThread(Activity.java:4794) at com.example.androidyue.bitmapdemo.MainActivity.onResume(MainActivity.java:33) at android.app.Instrumentation.callActivityOnResume(Instrumentation.java:1282) at android.app.Activity.performResume(Activity.java:5405)
如果是線上版本引起的,Google Play后臺有相關的數據可以幫助查看分析并解決問題。
細致分析
提問: BroadcastReceiver過了60秒居然沒有ANR? 現場代碼如下
public class NetworkReceiver extends BroadcastReceiver{ private static final String LOGTAG = "NetworkReceiver"; @Override public void onReceive(Context context, Intent intent) { Log.i(LOGTAG, "onReceive intent=" + intent); try { Thread.sleep(60000); } catch (InterruptedException e) { e.printStackTrace(); } Log.i(LOGTAG, "onReceive end"); } }
回答:實際上已經發生了ANR,只是沒有進行對話框彈出而已。這種ANR就是background ANR,即后臺程序的ANR,我們可以通過過濾日志驗證
adb logcat | grep "NetworkReceiver|ActivityManager|WindowManager" I/NetworkReceiver( 4109): onReceive intent=Intent { act=android.net.conn.CONNECTIVITY_CHANGE flg=0x8000010 cmp=com.example.androidyue.bitmapdemo/.NetworkReceiver (has extras) } I/ActivityManager( 462): No longer want com.android.exchange (pid 1054): empty #17 I/NetworkReceiver( 4109): onReceive end W/BroadcastQueue( 462): Receiver during timeout: ResolveInfo{5342dde4 com.example.androidyue.bitmapdemo.NetworkReceiver p=0 o=0 m=0x108000} E/ActivityManager( 462): ANR in com.example.androidyue.bitmapdemo E/ActivityManager( 462): Reason: Broadcast of Intent { act=android.net.conn.CONNECTIVITY_CHANGE flg=0x8000010 cmp=com.example.androidyue.bitmapdemo/.NetworkReceiver (has extras) } E/ActivityManager( 462): Load: 0.37 / 0.2 / 0.14 E/ActivityManager( 462): CPU usage from 26047ms to 0ms ago: E/ActivityManager( 462): 0.4% 58/adbd: 0% user + 0.4% kernel / faults: 1501 minor E/ActivityManager( 462): 0.3% 462/system_server: 0.1% user + 0.1% kernel E/ActivityManager( 462): 0% 4109/com.example.androidyue.bitmapdemo: 0% user + 0% kernel / faults: 6 minor E/ActivityManager( 462): 1.5% TOTAL: 0.5% user + 0.9% kernel + 0% softirq E/ActivityManager( 462): CPU usage from 87ms to 589ms later: E/ActivityManager( 462): 1.8% 58/adbd: 0% user + 1.8% kernel / faults: 30 minor E/ActivityManager( 462): 1.8% 58/adbd: 0% user + 1.8% kernel E/ActivityManager( 462): 4% TOTAL: 0% user + 4% kernel W/ActivityManager( 462): Killing ProcessRecord{5326d418 4109:com.example.androidyue.bitmapdemo/u0a10063}: background ANR I/ActivityManager( 462): Process com.example.androidyue.bitmapdemo (pid 4109) has died.
除了日志,我們還可以根據前面提到的查看traces.txt文件。
提問:可以更容易了解background ANR么?
回答:當然可以,在Android開發者選項—>高級—>顯示所有”應用程序無響應“勾選即可對后臺ANR也進行彈窗顯示,方便查看了解程序運行情況。
本文由用戶 ydx3 自行上傳分享,僅供網友學習交流。所有權歸原作者,若您的權利被侵害,請聯系管理員。
轉載本站原創文章,請注明出處,并保留原始鏈接、圖片水印。
本站是一個以用戶分享為主的開源技術平臺,歡迎各類分享!