Android:基于 Handler、Looper 實現(xiàn) ANR 監(jiān)控症昏,獲取堆棧

在上一篇文章《Android源碼剖析:基于 Handler掘宪、Looper 實現(xiàn)攔截全局崩潰攘烛、監(jiān)控ANR等》介紹了如何實現(xiàn)簡單的ANR監(jiān)控鼠次,判斷是否出現(xiàn)了ANR觅捆,但是沒有介紹如何分析栅炒,這篇文章將會詳細介紹如何分析解決ANR問題职辅。

觸發(fā) ANR
  1. 5s內(nèi)無法響應用戶輸入事件(例如鍵盤輸入, 觸摸屏幕等)
  2. BroadcastReceiver在10s內(nèi)無法結束
  3. Service在特定的時間內(nèi)無法處理完成
檢測是否存在ANR
class MyApplication : Application() {
    override fun onCreate() {
        super.onCreate()
        var startWorkTimeMillis = 0L
        Looper.getMainLooper().setMessageLogging {
            if (it.startsWith(">>>>> Dispatching to Handler")) {
                startWorkTimeMillis = System.currentTimeMillis()
            } else if (it.startsWith("<<<<< Finished to Handler")) {
                val duration = System.currentTimeMillis() - startWorkTimeMillis
                if (duration > 500) {
                    Log.e("主線程執(zhí)行耗時過長","$duration 毫秒簇秒,$it")
                }
            }
        }
    }
}

通過上述代碼可以檢測是否執(zhí)行耗時過長趋观,當出現(xiàn)ANR的時候皱坛,ANR執(zhí)行超過5秒,系統(tǒng)會把堆棧打印在/data/anr/traces.txt

獲取trace.txt 文件
adb shell cat /data/anr/traces.txt > d:/traces.txt

但是這種方式?jīng)]有辦法做檢測吭服,沒辦法上報到服務端艇棕,無法協(xié)助我們遠程分析問題北苟。

通過代碼獲取出現(xiàn) ANR 堆棧
class MyApplication : Application() {
    private val TAG = "MyApplication"
    private var startWorkTimeMillis = 0L
    private val mRunnable = Runnable {
        // 獲取主線程
        val thread = Looper.getMainLooper().thread
        val stringBuilder = StringBuilder()
        // 打印主線程的堆棧
        for (stack in thread.stackTrace) {
            stringBuilder.append(stack).append('\n')
        }
        Log.e("耗時過長", stringBuilder.toString())
    }

    override fun onCreate() {
        super.onCreate()

        val handlerThread = HandlerThread("anr")
        handlerThread.start()
        val stackHandler = Handler(handlerThread.looper)

        Looper.getMainLooper().setMessageLogging {
            if (it.startsWith(">>>>> Dispatching to Handler")) {
                startWorkTimeMillis = System.currentTimeMillis()
                stackHandler.removeCallbacks(mRunnable)
                stackHandler.postDelayed(mRunnable, 500)
            } else if (it.startsWith("<<<<< Finished to Handler")) {
                stackHandler.removeCallbacks(mRunnable)
                val duration = System.currentTimeMillis() - startWorkTimeMillis
                if (duration > 500) {
                    Log.e("主線程執(zhí)行耗時過長", "$duration 毫秒桃移,$it")
                }
            }
        }
    }
}
模擬測試
class MainActivity : AppCompatActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

        button1.setOnClickListener {
            Thread.sleep(1000)
        }
        button2.setOnClickListener {
            Thread.sleep(5000)
            Thread(Runnable {
                throw RuntimeException()
            }).start()
        }
    }
}

測試結果

E/耗時過長: java.lang.Thread.sleep(Native Method)
    java.lang.Thread.sleep(Thread.java:373)
    java.lang.Thread.sleep(Thread.java:314)
    com.taoweiji.handleranalyze.MainActivity$onCreate$1.onClick(MainActivity.kt:18)
    android.view.View.performClick(View.java:6597)
    android.view.View.performClickInternal(View.java:6574)
    android.view.View.access$3100(View.java:778)
    android.view.View$PerformClick.run(View.java:25885)
    android.os.Handler.handleCallback(Handler.java:873)
    android.os.Handler.dispatchMessage(Handler.java:99)
    android.os.Looper.loop(Looper.java:193)
    android.app.ActivityThread.main(ActivityThread.java:6669)
    java.lang.reflect.Method.invoke(Native Method)
    com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:493)
    com.android.internal.os.ZygoteInit.main(ZygoteInit.java:858)
E/主線程執(zhí)行耗時過長: 1003 毫秒进泼,<<<<< Finished to Handler (android.view.ViewRootImpl$ViewRootHandler) {5dddc1b} android.view.View$PerformClick@b1d8dda
總結

通過上述代碼可以獲取ANR绞惦,打印堆棧信息济蝉,但是并不能獲取所有情況的ANR王滤,比如CPU計算資源耗盡導致整個APP所有線程都卡死雁乡,這種情況下是解決不了。

最后編輯于
?著作權歸作者所有,轉載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末珠月,一起剝皮案震驚了整個濱河市锌钮,隨后出現(xiàn)的幾起案子侵浸,更是在濱河造成了極大的恐慌掏觉,老刑警劉巖,帶你破解...
    沈念sama閱讀 221,695評論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件危虱,死亡現(xiàn)場離奇詭異蕊玷,居然都是意外死亡垃帅,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,569評論 3 399
  • 文/潘曉璐 我一進店門赴涵,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人髓窜,你說我怎么就攤上這事”罘螅” “怎么了程拭?”我有些...
    開封第一講書人閱讀 168,130評論 0 360
  • 文/不壞的土叔 我叫張陵,是天一觀的道長崖媚。 經(jīng)常有香客問我,道長畅哑,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 59,648評論 1 297
  • 正文 為了忘掉前任赛蔫,我火速辦了婚禮泥张,結果婚禮上,老公的妹妹穿的比我還像新娘圾结。我一直安慰自己,他們只是感情好晌姚,可當我...
    茶點故事閱讀 68,655評論 6 397
  • 文/花漫 我一把揭開白布歇竟。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪窿祥。 梳的紋絲不亂的頭發(fā)上听系,一...
    開封第一講書人閱讀 52,268評論 1 309
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死歌粥,一個胖子當著我的面吹牛嬉探,可吹牛的內(nèi)容都是我干的胎围。 我是一名探鬼主播上岗,決...
    沈念sama閱讀 40,835評論 3 421
  • 文/蒼蘭香墨 我猛地睜開眼滞造,長吁一口氣:“原來是場噩夢啊……” “哼丰泊!你這毒婦竟也來了?” 一聲冷哼從身側響起吞杭,我...
    開封第一講書人閱讀 39,740評論 0 276
  • 序言:老撾萬榮一對情侶失蹤童擎,失蹤者是張志新(化名)和其女友劉穎哑芹,沒想到半個月后末购,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體擎场,經(jīng)...
    沈念sama閱讀 46,286評論 1 318
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 38,375評論 3 340
  • 正文 我和宋清朗相戀三年逃糟,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 40,505評論 1 352
  • 序言:一個原本活蹦亂跳的男人離奇死亡几苍,死狀恐怖刽宪,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情庇谆,我是刑警寧澤哥攘,帶...
    沈念sama閱讀 36,185評論 5 350
  • 正文 年R本政府宣布欣簇,位于F島的核電站,受9級特大地震影響衫仑,放射性物質發(fā)生泄漏瞄崇。R本人自食惡果不足惜凿掂,卻給世界環(huán)境...
    茶點故事閱讀 41,873評論 3 333
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧埃疫,春花似錦、人聲如沸茵乱。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,357評論 0 24
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春专钉,著一層夾襖步出監(jiān)牢的瞬間站叼,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,466評論 1 272
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留下梢,地道東北人。 一個月前我還...
    沈念sama閱讀 48,921評論 3 376
  • 正文 我出身青樓崭歧,卻偏偏與公主長得像仔粥,于是被迫代替她去往敵國和親诵竭。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 45,515評論 2 359

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