1. 程式人生 > >【騰訊bugly幹貨分享】精神哥手把手教你怎樣智鬥ANR

【騰訊bugly幹貨分享】精神哥手把手教你怎樣智鬥ANR

waiting pen nag 技術分享 input 這就是 max-width 卡死 gravity

上帝說要有ANR,於是Bugly就有了ANR上報。那麽ANR究竟是什麽?

近期非常多童鞋問起精神哥ANR的問題,那麽這次就來聊一下,雞爪怎麽泡才好吃。噢不,是怎樣高速定位ANR。

ANR是什麽

簡單說,通常就是App執行的時候,duang~卡住了。怎麽搞都動不了。

當卡住超過一定時間。Android系統覺得這就是一次“ANR(Application Not Responding)”。

詳細說。在下面情況發生時,會發生ANR(可能在不同ROM 中時間有所更改):

  • 用戶的輸入在5s內沒被App響應。

  • BroadcastReceiver的onReceiver()超過10s;

  • Service中各生命周期函數運行超過20s。


ANR必須死

用戶在App的絕大部分操作,都須要有App的主動回應。比方按下button之後button樣式的改變、下拉滾動欄內容的移動、載入資源時的菊花轉轉轉,它們都是“操作-反饋”配對的模式。對於我們手機上最常見的觸摸操作。0.1s的響應延遲已經有非常明顯的卡頓感了。而對於常見的ANR。用戶至少要等5s以上!

發生了ANR,往往會彈出對話框,問用戶是繼續等待還是直接關掉:

技術分享

相信差點兒全部Android手機用戶都見過這個然並卵的ANR對話框,但大部分普通用戶根本不知道這個對話框在講什麽,而且往往也僅僅有關閉App。

漫長的等待就給我看這個?從用戶的體驗看,就是心中一萬僅僅草泥馬奔騰起來撞火車的感受。可見ANR對於應用的影響並不亞於Crash。

一般來說,界面相對越不“流暢”的App(說明UI線程耗時操作多)越easy發生ANR(一個輸入事件在某個設備A上4秒有了反饋。並不意味著它在其它設備B上是安全的)。

ANR事實上就是界面卡頓的極端情況。反過來,僅僅要通過合理的方案消滅了App出現的ANR,往往也同一時候會使App展示界面表現會更加順滑流暢。

一些典型的ANR 問題場景

這裏舉幾個easy發生ANR的場景:

1)最常見的錯誤,UI線程等待其他線程釋放某個鎖,導致UI線程無法處理用戶輸入。

2)遊戲中每幀動畫都進行了比較耗時的大量計算,導致CPU忙只是來;

3)Web應用中。網絡狀態不穩定。而界面在等待網絡數據;

4)UI線程中進行了一些磁盤IO(包含數據庫、SD卡等等)的操作。在個別設備上由於硬件損壞等原因堵塞住了;

5)手機被其它App占用著CPU。自己獲取不到足夠的CPU 時間片,純屬誤傷。

技術分享

通過ANR 日誌定位問題

當ANR發生時。我們往往通過Logcat和traces文件(文件夾/data/anr/)的相關信息輸出去定位問題。主要包括下面幾方面:

1)基本信息,包含進程名、進程號、包名、系統build號、ANR 類型等等;

2)CPU使用信息。包含活躍進程的CPU 平均占用率、IO情況等等。

3)線程堆棧信息。所屬進程包含發生ANR的進程、其父進程、近期有活動的3個進程等等。

這裏舉個簡單的樣例(實際上由於各App所處環境各異,可能出現各種各樣復雜的ANR情況)當App執行卡住,彈出ANR對話框。查看Logcat輸出:

ActivityManager: ANR in com.tencent.bugly.demo (com.tencent.bugly.demo/.MainActivity)
ActivityManager: PID: 18617
ActivityManager: Reason: Input dispatching timed out (Waiting because the touched window has not finished processing the input events that were previously delivered to it.)
ActivityManager: Load: 18.42 / 18.09 / 18.29
ActivityManager: CPU usage from 5924ms to 475ms ago:
ActivityManager: 93% 18617/com.tencent.bugly.demo: 93% user + 0% kernel / faults: 75 minor
……
ActivityManager: CPU usage from 2906ms to 3429ms later:
ActivityManager: 96% 18617/com.tencent.bugly.demo: 96% user + 0% kernel
……
ActivityManager: 55% TOTAL: 51% user + 3.8% kernel

分析一下,從Logcat能夠得到下面信息:

  1. com.tencent.bugly.demo這個App的MainActivity發生了ANR,進程號18617;

  2. ANR原因:用戶輸入超時。

  3. ANR發生前、後一段時間分別附在情況:在ANR發生前後,CPU有90+%耗費在這個demo上,說明非常可能是這個demo自身性能引起的。

接下來再看traces文件確認:

----- pid 18617 at xxxx -----
Cmd line: com.tencent.bugly.demo
JNI: CheckJNI is off; workarounds are off; pins=0; globals=272 (plus 2 weak)
DALVIK THREADS:
"main" prio=5 tid=1 SUSPENDED
| group="main" sCount=1 dsCount=0 obj=0x415e4e58 self=0x415d3028
| sysTid=18617 nice=0 sched=0/0 cgrp=apps handle=1074372948
| state=S schedstat=( 38588000572 591063492 5767 ) utm=3846 stm=12 core=0
at com.tencent.bugly.demo.MainActivity$3.doCalc(MainActivity.java:~38)
at com.tencent.bugly.demo.MainActivity$3.onClick(MainActivity.java:33)
……

分析一下,traces文件裏包括下面信息:

1、進程號:18617;包名:com.tencent.bugly.demo。

2、發生ANR時,main線程被掛起(也可能是其它等待狀態,比方TIMED_WAIT);

3、線程的幾個重要參數:

  • group:線程組名稱“main”;

  • sCount:Suspended個數“1”。

  • obj:線程的Java對象地址。

  • self:線程的Native對象地址;

  • sysTid:線程號(這裏主線程的線程號=進程號)“18617”。

4、詳細堆棧:從堆棧能夠非常清晰看出是doCalc()方法出的問題,由onClick觸發。

綜合以上分析。問題還原為:com.tencent.bugly.demo這個App的MainActivity中有個耗時的doCalc方法在跑。無法響應用戶的觸摸或按鍵輸入。OK。接下來在代碼裏找問題就好了。

怎樣解決ANR

當然是盡可能降低UI線程的耗時操作。以及BroadcastReceiver、Service生命周期中的標準回調方法啦。

Android官方文檔建議:

1)使用AsyncTask類,能夠非常方便地實現子線程耗時操作與UI更新;

2)對於BroadcastReceiver的耗時操作。建議放到Service中運行。

3)對於自建的Thread,能夠通過Handler使之與UI 線程通信(這裏須要註意的是。Thread默認優先級和UI線程是一樣的,建議設置一般線程優先級為Process.THREAD_PRIORITY_BACKGROUND)。

這些方案大家應該都知道。只是仍難免有大量的ANR是寫代碼時忽略了,在測試時沒發生,終於在用戶的手機上出現的。回憶一下是不是都經歷過用戶會反饋“App卡死沒反應了”。但開發GG客服MM們卻又由於缺少日誌或無法復現而束手無策?因此要修復ANR。首先是要能發現用戶ANR了。而且能知道是哪段代碼導致ANR了,這樣才幹談修復。


為了幫助廣大開發人員解決這一難題,騰訊Bugly針對iOS的卡頓及Android的ANR提供監測服務即將上線,協助開發人員輕松定位問題。

【騰訊bugly幹貨分享】精神哥手把手教你怎樣智鬥ANR