Android ANR監(jiān)測診斷以及解決辦法

當(dāng)UI線程阻塞時間太長,應(yīng)用無響應(yīng)(ANR)錯誤便會觸發(fā)球榆。如果應(yīng)用位于前臺娃兽,系統(tǒng)還會顯示給用戶一個ANR對話框菇民,讓用戶有機會強制關(guān)閉應(yīng)用。

ANR是一個問題投储,因為應(yīng)用中負(fù)責(zé)更新UI的主線程無法處理用戶輸入事件或者繪制操作第练,從而導(dǎo)致用戶感到沮喪。

一般分三種情況:

  • KeyDispatchTimeout(5 seconds):主要情況玛荞。按鍵或者觸摸事件無法在特定時間內(nèi)完成娇掏。
  • BroadcastTimeout(10 seconds) :BroadcastReceiver在特定時間內(nèi)無法處理完成
  • ServiceTimeout(20 seconds): Service在特定的時間內(nèi)無法處理完成(所以雖然service是后臺執(zhí)行的,但是他是運行在UI線程的勋眯,如果處理一些耗時操作婴梧,會造成ANR)

監(jiān)測和診斷問題

如果應(yīng)用已經(jīng)發(fā)布了下梢,Android vitals可以向你警告ANR問題的發(fā)生。(PS:前提是要發(fā)布到Google Play Store上志秃,國內(nèi)如果不是面向海外的怔球,可以集成友盟SDK或者騰訊Bugly等)

Android vitals

Google Play Console的Android vitals模塊統(tǒng)計了應(yīng)用的性能相關(guān)情況,包括ANR和Crash等浮还【固常可以根據(jù)上面的統(tǒng)計來分析解決ANR和Crash問題。

診斷ANR

診斷ANR可用的常規(guī)套路:

  • 在主線程中執(zhí)行IO操作
  • 在主線程執(zhí)行長時間的計算
  • 主線程執(zhí)行同步Binder操作訪問另一個進(jìn)程钧舌,該進(jìn)程執(zhí)行很長時間再返回
  • 非主線程持有l(wèi)ock担汤,導(dǎo)致主線程等待lock超時
  • 主線程和另一個線程發(fā)生死鎖,可以是位于當(dāng)前進(jìn)程或者通過Binder調(diào)用洼冻。

用下面的技巧幫助你分析到底是上面的哪種情況引起了ANR崭歧。

Strict mode

使用StrictMode幫你找到主線程哪里調(diào)用了IO操作。這個模式打開后撞牢,可以盡可能幫助你找到主線程中的磁盤訪問和網(wǎng)絡(luò)訪問操作率碾,網(wǎng)絡(luò)訪問操作是肯定需要放到子線程中執(zhí)行的。而磁盤操作的話屋彪,通常都會執(zhí)行很快所宰,當(dāng)然能做到子線程中最好。官方文檔也說了畜挥,不要強迫修復(fù)StrictMode幫你找到的所有內(nèi)容仔粥,特別是,在正常的Activity生命周期中蟹但,許多磁盤訪問操作是需要的躯泰。

But don't feel compelled to fix everything that StrictMode finds. In particular, many cases of disk access are often necessary during the normal activity lifecycle

調(diào)試模式下打開,但是發(fā)布狀態(tài)不允許打開华糖。

可以在Activity或者Appliction的onCreat()方法中打開:

public void onCreate() {
     if (DEVELOPER_MODE) {
         StrictMode.setThreadPolicy(new StrictMode.ThreadPolicy.Builder()
                 .detectDiskReads()
                 .detectDiskWrites()
                 .detectNetwork()   // or .detectAll() for all detectable problems
                 .penaltyLog()
                 .build());
         StrictMode.setVmPolicy(new StrictMode.VmPolicy.Builder()
                 .detectLeakedSqlLiteObjects()
                 .detectLeakedClosableObjects()
                 .penaltyLog()
                 .penaltyDeath()
                 .build());
     }
     super.onCreate();
 }

打開后臺ANR彈框

設(shè)備的開發(fā)者選項中可以打開“顯示所有ANR”麦向。默認(rèn)Android只對于前臺應(yīng)用發(fā)生ANR時彈框,打開后缅阳,后臺應(yīng)用發(fā)生ANR也會彈框磕蛇。

TraceView

使用TraceView分析應(yīng)用運行時你根據(jù)用例情況在卡頓前后捕捉的方法調(diào)用trace文件。trace文件可以通過代碼調(diào)用生成十办,或者通過Android Studio捕捉秀撇。

Debug.startMethodTracing("hellotrace");    //開始 trace,保存文件到 "/sdcard/hellotrace.trace"
    // ...
Debug.stopMethodTracing();    //結(jié)束

使用adb命令將trace文件導(dǎo)出到電腦向族,然后放到DDMS中打開分析

adb pull /sdcard/hellotrace.trace /tmp

拉取traces文件

當(dāng)ANR發(fā)生時呵燕,Android系統(tǒng)會將一些trace信息存儲到設(shè)備的/data/anr/traces.txt文件中。你可以利用adb命令將其拉取出來分析(前提是要root件相?不記得了)再扭。

對于模擬器氧苍,簡單快速查看:

adb root
adb shell
cat /data/anr/traces.txt

還可以用bugreport命令導(dǎo)出。

解決問題

主線程執(zhí)行慢代碼

將耗時操作異步執(zhí)行泛范。

主線程執(zhí)行IO操作

建議將所有IO操作放到子線程執(zhí)行让虐。

鎖爭用

工作線程持有主線程需要獲取某個資源的鎖又不能及時釋放的情況。

通常發(fā)生ANR時罢荡,主線程處于Monitor或者BLOCKED狀態(tài)赡突。例子:

@Override
public void onClick(View v) {
    // The worker thread holds a lock on lockedResource
   new LockTask().execute(data);

   synchronized (lockedResource) {
       // The main thread requires lockedResource here
       // but it has to wait until LockTask finishes using it.
   }
}

public class LockTask extends AsyncTask<Integer[], Integer, Long> {
   @Override
   protected Long doInBackground(Integer[]... params) {
       synchronized (lockedResource) {
           // This is a long-running operation, which makes
           // the lock last for a long time
           BubbleSort.sort(params[0]);
       }
   }
}

上述代碼,主線程需要獲取lockedResource鎖区赵,而該鎖被LockTask持有惭缰,其sort方法為耗時操作,導(dǎo)致不能及時釋放鎖笼才,從而引發(fā)主線程阻塞超時漱受,導(dǎo)致ANR。

另外一個例子骡送,主線程等待子線程執(zhí)行結(jié)果超時:

public void onClick(View v) {
   WaitTask waitTask = new WaitTask();
   synchronized (waitTask) {
       try {
           waitTask.execute(data);
           // Wait for this worker thread’s notification
           waitTask.wait();
       } catch (InterruptedException e) {}
   }
}

class WaitTask extends AsyncTask<Integer[], Integer, Long> {
   @Override
   protected Long doInBackground(Integer[]... params) {
       synchronized (this) {
           BubbleSort.sort(params[0]);
           // Finished, notify the main thread
           notify();
       }
   }
}

這些情況需要評估鎖耗時昂羡,保證鎖盡可能占有最少的時間∷猓或者移除鎖紧憾。

死鎖

盡可能避免死鎖。

執(zhí)行緩慢的Broadcast Receiver

當(dāng)應(yīng)用花費了太長時間處理廣播消息時候也會導(dǎo)致ANR發(fā)生昌渤。

下面的情況會導(dǎo)致ANR發(fā)生:

  • 廣播接收者沒能及時執(zhí)行完成onReceive()方法(通常10s)
  • 廣播接收者調(diào)用了goAsync()方法,但是沒有調(diào)用PendingResult對象的finish()方法憔四。

如果onReceive方法中要執(zhí)行耗時操作膀息,可以將任務(wù)放到IntentService中執(zhí)行。

@Override
public void onReceive(Context context, Intent intent) {
    // The task now runs on a worker thread.
    Intent intentService = new Intent(context, MyIntentService.class);
    context.startService(intentService);
}

public class MyIntentService extends IntentService {
   @Override
   protected void onHandleIntent(@Nullable Intent intent) {
       BubbleSort.sort(data);
   }
}

或者了赵,調(diào)用BroadcastReceiver告訴系統(tǒng)潜支,我需要更多時間來處理消息。你處理完成之后柿汛,必須調(diào)用PendingResult對象的finish方法冗酿。

final PendingResult pendingResult = goAsync();
new AsyncTask<Integer[], Integer, Long>() {
   @Override
   protected Long doInBackground(Integer[]... params) {
       // This is a long-running operation
       BubbleSort.sort(params[0]);
       pendingResult.finish();
   }
}.execute(data);

但是使用goAsync()仍然可能導(dǎo)致ANR,你必須在10s之內(nèi)完成操作

參考資料:https://developer.android.com/topic/performance/vitals/anr.html

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末络断,一起剝皮案震驚了整個濱河市裁替,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌貌笨,老刑警劉巖弱判,帶你破解...
    沈念sama閱讀 222,104評論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異锥惋,居然都是意外死亡昌腰,警方通過查閱死者的電腦和手機开伏,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,816評論 3 399
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來遭商,“玉大人固灵,你說我怎么就攤上這事〗倭鳎” “怎么了巫玻?”我有些...
    開封第一講書人閱讀 168,697評論 0 360
  • 文/不壞的土叔 我叫張陵,是天一觀的道長困介。 經(jīng)常有香客問我大审,道長,這世上最難降的妖魔是什么座哩? 我笑而不...
    開封第一講書人閱讀 59,836評論 1 298
  • 正文 為了忘掉前任徒扶,我火速辦了婚禮,結(jié)果婚禮上根穷,老公的妹妹穿的比我還像新娘姜骡。我一直安慰自己,他們只是感情好屿良,可當(dāng)我...
    茶點故事閱讀 68,851評論 6 397
  • 文/花漫 我一把揭開白布圈澈。 她就那樣靜靜地躺著,像睡著了一般尘惧。 火紅的嫁衣襯著肌膚如雪康栈。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 52,441評論 1 310
  • 那天喷橙,我揣著相機與錄音啥么,去河邊找鬼。 笑死贰逾,一個胖子當(dāng)著我的面吹牛悬荣,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播疙剑,決...
    沈念sama閱讀 40,992評論 3 421
  • 文/蒼蘭香墨 我猛地睜開眼氯迂,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了言缤?” 一聲冷哼從身側(cè)響起嚼蚀,我...
    開封第一講書人閱讀 39,899評論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎管挟,沒想到半個月后驰坊,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 46,457評論 1 318
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 38,529評論 3 341
  • 正文 我和宋清朗相戀三年拳芙,在試婚紗的時候發(fā)現(xiàn)自己被綠了察藐。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 40,664評論 1 352
  • 序言:一個原本活蹦亂跳的男人離奇死亡舟扎,死狀恐怖分飞,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情睹限,我是刑警寧澤譬猫,帶...
    沈念sama閱讀 36,346評論 5 350
  • 正文 年R本政府宣布,位于F島的核電站羡疗,受9級特大地震影響染服,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜叨恨,卻給世界環(huán)境...
    茶點故事閱讀 42,025評論 3 334
  • 文/蒙蒙 一柳刮、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧痒钝,春花似錦秉颗、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,511評論 0 24
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至栋荸,卻和暖如春菇怀,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背晌块。 一陣腳步聲響...
    開封第一講書人閱讀 33,611評論 1 272
  • 我被黑心中介騙來泰國打工敏释, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人摸袁。 一個月前我還...
    沈念sama閱讀 49,081評論 3 377
  • 正文 我出身青樓,卻偏偏與公主長得像义屏,于是被迫代替她去往敵國和親靠汁。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 45,675評論 2 359

推薦閱讀更多精彩內(nèi)容