1. 程式人生 > >Android ANR產生的原因以及其定位分析

Android ANR產生的原因以及其定位分析

前言

   ANR是Android中一個獨有的概念,它的全稱是Application Not Responding(應用程式無響應)。

   相信從事Android開發的同學,或多或少都遇到過,對於高質量的程式碼,ANR在開發者自測過程中可能不會經常遇到,但一旦測試人員進行Monkey測試,ANR出現的概率就比較高了,如何快速分析定位並解決,是開發者的必修課。

  ANR的直觀體驗是使用者在操作APP的過程中,感覺介面卡頓,比如按下某個按鈕,開啟某個頁面等,當卡頓超過一定時間(一般是5秒)時就會出現ANR對話方塊,如下圖所示:


 這時檢視Logcat,一般可以發現ANR以及traces.txt等字樣。可以發現,出現ANR主要是因為我們在主執行緒中做了耗時操作。這時你可以選擇“等待”按鈕,等待應用程式結束主執行緒耗時操作,或者選擇“確定”按鈕,結束這個應用程式。

1、ANR產生的原因

  只有當應用程式的UI執行緒響應超時才會引起ANR,超時產生原因一般有兩種:

   · 當前的事件沒有機會得到處理,列如UI執行緒正在響應另外一個事件,當前事件由於某種原因被阻塞了。

   · 當前的事件正在處理,但是由於耗時太長沒能及時完成。

  根據ANR產生的原因不同,超時時間也不盡相同,從本質上講,產生ANR的原因有三種,大致可以對應到Android中四大元件中的三個(Activity/View、BroadcastReceiver和Service)。

KeyDispatchTiemout
  • 1
  • 2
  • 1

  最常見的一種型別,原因是View的按鍵事件或者觸控事件在特定的時間(5秒)內無法得到響應。

BroadcastTiemout
  • 1
  • 2
  • 1

 原因是BroadcastReceiver的onReceive()函式執行在主執行緒中,在特定的時間(10秒)內無法完成處理。

ServiceTiemout
  • 1
  • 2
  • 1

比較少出現的一種型別,原因是Service的各個生命週期函式在特定時間(20秒)內無法完成處理。


2、典型的ANR問題場景

 · 應用程式UI執行緒存在耗時操作,例如在UI執行緒中進行網路請求、資料庫操作或者檔案操作,可能會導致UI執行緒無法及時處理使用者輸入等。當然在Android4.0之後,如果在UI執行緒中進行網路操作,將會丟擲NetworkOnMainThreadException異常。

 · 應用程式的UI執行緒等待子執行緒釋放某個鎖,從而無法處理使用者的輸入。

 · 耗時的動畫需要大量的計算工作,可能導致CPU負載過量。

3、ANR的定位和分析

當發生ANR時,開發者可以通過結合Logcat日誌和生成的位於手機內部儲存的/data/anr/traces.txt檔案進行定位和分析。

4、ANR的避免和檢測

為了避免在開發中引入可能導致應用發生ANR的問題,除了切記不要在主執行緒中作耗時操作,我們也可以藉助於一些工具來進行檢測,從而更有效的避免ANR的引入。

  4.1 StrictMode

嚴格模式StrictMode是Android SDK提供的一個用來檢測程式碼中是否存在違規操作的工具類,StrictMode主要檢測兩大類問題:

   · 執行緒策略ThreadPolicy

     - detectCustomSlowCalls: 檢測自定義耗時操作。

     - detectDiskReads: 檢測是否存在磁碟讀取操作。

     - detectDiskWrites: 檢測是否存在磁碟寫入操作。

     - detectNetwork: 檢測是否存在網路操作。

    · 虛擬機器策略VmPolicy

     - detectActivityLeaks: 檢測是否存在Activity洩漏。

     - detectLeakedClosableObjects: 檢測是否存在未關閉的Closeable物件洩漏。

     - detectLeakedSqlLiteObjects: 檢測是否存在Sqlite物件洩漏。

     - setClassInstanceLimit: 檢測類例項個數是否超過限制。

4.2 BlockCanary

BlockCanary是一個非侵入式的效能監控函式庫,它的用法和LeakCanary類似,只不過LeakCanary監控應用的記憶體洩漏,而BlockCanary主要用來監控應用主執行緒的卡頓。它的基本原理是利用主執行緒的訊息佇列處理機制,通過對比訊息分發開始和結束的時間點來判斷是否超過設定的時間,如果是,即判斷為主執行緒卡頓。它的整合很簡單,首先在build.gradle中新增線上依賴,如下:

dependencies {
      compile 'com.github.moduth:blockcanary-android:1.2.1'
      // 僅在debug包中啟用BlockCanary進行卡頓監控和提示的話,可以這麼用
      debugCompile 'comgithub.moduth:blockcanary-android:1.2.1'
      releaseCompile 'comgithub.moduth:blockcanary-no-op:1.2.1'
}

  然後在Application類中進行配置和初始化即可,
public class DemoApplication extends Application {
       @Override
       public void onCreate() {
             // 在主程序初始化呼叫
             BlockCanary.install(this, new AppBlockCanaryContext()).start());
       }
}