Android中的ANR用法詳解

技術小黑屋發表於2015-07-20

有過Android開發經歷的人都不會對ANR陌生,它和崩潰一樣是程式設計的問題。本文將以較為深入的視角來介紹什麼是ANR,出現場景,如何避免以及如何定位分析ANR,希望可以幫助大家在編寫程式時有所幫助。

什麼是ANR

ANR全稱Application Not Responding,意思就是程式未響應。如果一個應用無法響應使用者的輸入,系統就會彈出一個ANR對話方塊,如下圖所示,使用者可以自行選擇繼續等待亦或者是停止當前程式。

說說Android中的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也進行彈窗顯示,方便檢視了解程式執行情況。

相關文章