微信Android ANR原理解析及解決方案


最近微信安卓版的“兩位數字+15個中文字符句號”BUG把ANR帶回了大家的視野。

 

前情介紹-微信bug事件

 

在微信上給安卓手機用戶發送:

“11。。。。。。。。。。。。。。。”

(兩位數字+15個中文字符句號)接收到這樣的信息以后,部分安卓手機在發送或收到這條消息時微信會無響應,如下圖。

圖片

 

本文將從如下幾個方面給大家介紹一下Android ANR相關的知識:

 

ANR的原理

如何定位分析ANR(以微信為例)

如何解決&避免ANR

 

 

ANR的原理

什么是ANR

ANR全稱Application Not Responding,意為程序未響應。Android基於消息處理機制系統對於一些事件需要在一定的時間范圍內完成,如果超過預定時間能未能得到有效響應就會造成ANR。發生ANR時的表現一般是彈出一個提示框,告知程序無響應,讓用戶選擇等待還是強制退出。時的表現一般是彈出一個提示框,告知程序無響應,讓用戶選擇等待還是強制退出。

 

什么場景會發生ANR

一般情況下,如下場景會導致ANR:

 

Service Timeout:比如前台服務在20s內未執行完成

 

BroadcastReceiver Timeout:比如前台廣播在10s內未執行完成

 

ContentProvider Timeout:內容提供者,在publish過超時10s

 

InputDispatching Timeout: 輸入事件分發超時5s,包括按鍵和觸摸事件

 

系統如何發現ANR

Android基於消息處理機制在系統層實現了一套發現ANR的機制,其其核心原理是消息調度和超時處理。

 

下面以Service為例簡單介紹下ANR的檢測流程。

 

Service的ANR機制

Service的ANR檢測流程可以簡單歸納為首先埋一個超時消息,然后操作在超時內完成之后移除超時消息,否則消息被系統收到觸發ANR。

 

Service的ANR消息埋點

Service的啟動的大致過程如下圖: 

圖片

 

在Service進程啟動之后並attach到system_server進程時會進行ANR埋點,代碼如下:

圖片

 

該方法的主要工作就是發送delay消息(SERVICE_TIMEOUT_MSG),所以,如果我們不能在操作完成時remove該消息,則消息會被發送到MainHandler從而觸發ANR。

 

圖片

Service的ANR消息移除

根據ANR對監測原理,既然埋下了超時消息,那必然也有對應對移除邏輯,對於Service,在Service啟動完成時會對消息進行移除,代碼如下:

 

圖片

 

可以看出該方法的主要工作就是在service啟動完成時則移除服務超時消息。

 

觸發ANR

當然,如果沒能及時remove消息,則會觸發ANR,通過上述代碼,我們可以發現在system_server進程中有一個Handler線程, 名叫”ActivityManager”,而ANR對消息也是向該Handler線程發送。該Handler對ANR對處理邏輯如下:

 

圖片

 

圖片

總結

 

通過如上分析,可以大致總結初Android對ANR對監測機制主要基於消息機制,通過預發送超時消息並及時移除來實現超時檢測對功能。

 

 ANR的信息收集

通過如上的ANR觸發邏輯可以看到ANR觸發時最終都會調用AMS.AppNotResponding()方法,下面從這個方法說起。

圖片

圖片

 

當發生ANR時, 會按順序依次執行:

  • 第一次更新CPU的統計信息。這是發生ANR時,第一次CPU使用信息的采樣

     

  • 輸出ANR Reason信息到EventLog, 也就是說ANR觸發的時間點最接近的就是EventLog中輸出的am_anr信息

     

  • 填充firstPids和lastPids數組

     

  • 收集並輸出重要進程列表中的各個線程的traces信息

     

  • 第二次更新CPU統計信息,跟第一次一樣,兩次采樣的數據分別對應ANR發生前后的CPU使用情況

     

  • 將traces文件和CPU使用情況信息保存到dropbox,即data/system/dropbox目錄

     

  • 根據進程類型,來決定直接后台殺掉,還是彈框告知用戶

     

可以看出,除了前端提示之外,ANR發生的時候會寫各種類型的日志來方便后續排查問題,主要的日志包括如下:

 

  • event log,主要包括ANR的發生時間,類型等基本信息

  • main log,主要包括ANR發生時的詳細信息,如CPU的使用情況等

  • dropbox日志,通過dumpsys dropbox找到,主要為ANR發生時的詳細信息,trace、函數調用、CPU信息等

  • trace日志,最常用的ANR分析文件,主要包括CPU信息和各進程的函數調用棧信息

 

 

如何定位和分析ANR的問題

通過對ANR的原理了解,我們可以從多個角度對ANR進行問題分析,如通過分析trace日志,dropbox日志等。

 

通過logcat分析

簡單的情景,我們可以直接在logcat中檢索ANR in關鍵詞即可找到對應的ANR原因、堆棧信息和CPU使用情況,格式大致如下:

 

圖片

 

通過日志能看到ANR的原因(Input dispatching timed out),發生時的CPU負載等信息。

 

分析traces文件

實際情況中,這些信息相對還是偏少的,所以我們就需要通過分析trace文件或者dropbox文件來獲取詳細信息來定位問題。下面以微信為例:

 

首先,我們拿到trace文件,一般情況下,trace文件在如下目錄:

圖片

 

但是實際中,部分機型會對trace有rename的過程,所以trace文件的存儲可能如下: 

圖片

 

如圖,trace文件的格式為traces_[package]_[time].txt

 

將trace文件pull到本地,打開后可以看到如下的內容: 

圖片

 

有源碼場景下問題定位

一般情況下,如果是自有的App,我們直接通過分析堆棧信息,即可定位到具體的代碼行然后可以直接通過源碼調試解決問題。

 

無源碼場景下問題定位

如果App是黑盒,比如微信這樣,那我們就需要借助一些反編譯的手段來進行定位。

 

以微信的這個ANR為例,通過分析多個ANR的trace文件后,發現每次的堆棧其實都有細微的不一樣,上面的這個堆棧信息里面顯示的錯誤可能是由於正則匹配,但是還有如下的情況: 

圖片

 

通過這個堆棧信息可以看出是由於獲取textWidth導致的問題,所以可以通過找到相同的父調用鏈來分析問題:

圖片

 

所以我們基本可以定位到問題是在celltextview到這幾個方法調用里面。

 

然后,我們就可以開始進行反編譯定位問題了,一般我們有兩種方案:

 

  1. 通過動態調試來找到問題。首先,我們需要co.debuggable = 1的設備(root或者模擬器均可),然后先通過baksmali將微信apk反編譯成smali文件,然后通過在android studio安裝SmaliIDEA調試插件,導入反編譯好的smali項目,即可開始調試(具體操作可以自行查閱)。

     

  2. 直接反編譯代碼,然后人肉定位問題。首先,我們需要先將apk解壓,取出dex文件,借助dex2jar將dex文件轉換成jar文件,然后借助jd-gui來查看源碼,然后人肉定位問題。

     

通過如上的兩個方案,可以幫助我們在黑盒的情況下定位到具體問題。

 

 

如何避免ANR

ANR本質是一個性能問題,要求主線程在超時內完成操作,所以想要避免ANR,根本的就是需要杜絕主線程中的耗時操作。 


所以我們可以使用一些異步的方式來進行耗時操作,常用的有AsynkTask、Thread等,然后通過Handler來處理結果,只在主程序進行耗時較少的更新操作。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM