《Android開發(fā)高手課》是極客時(shí)間上為數(shù)不多的質(zhì)量高的課程,通過(guò)學(xué)習(xí)確實(shí)讓我開拓了眼界,之前對(duì)于Android的優(yōu)化可能僅僅停留在基礎(chǔ)的階段,通過(guò)對(duì)這個(gè)課程的學(xué)習(xí),確實(shí)了解了更多的監(jiān)測(cè)手段以及優(yōu)化手段。
Android兩種崩潰
- Java崩潰: 在Java代碼中,出現(xiàn)了未捕獲的異常,導(dǎo)致程序異常退出
- Native崩潰: 一般都是因?yàn)樵贜ative代碼中訪問(wèn)非法地址,也可能是地址對(duì)齊出現(xiàn)了問(wèn)題,或者發(fā)生了程序主動(dòng)abort,這些都會(huì)產(chǎn)生相應(yīng)的signal信號(hào),導(dǎo)致程序異常退出。
Native崩潰捕獲
- 編譯端. 編譯c/c++代碼時(shí),需要將帶符號(hào)信息的文件保留下來(lái)
- 客戶端. 捕獲到崩潰時(shí),將收集到盡可能多的有用信息寫入日志,然后選擇合適的時(shí)機(jī)上傳到服務(wù)器
- 服務(wù)端. 讀取客戶端上報(bào)的日志文件,尋找適合的符號(hào)文件,生成可讀的c/c++調(diào)用棧

image.png
BreakPad是目前Native崩潰中最成熟的方案
選擇合適的崩潰服務(wù)
- 騰訊Bugly: 除了有crash數(shù)據(jù)還有運(yùn)營(yíng)數(shù)據(jù)
- UC 啄木鳥:可以捕獲Java、Native異常,被系統(tǒng)強(qiáng)殺的異常,ANR,Low Memory Killer、killProcess。技術(shù)深度以及捕獲能力強(qiáng)
- 網(wǎng)易云捕:繼承便捷,訪問(wèn)快,捕獲以及上報(bào)速度及時(shí),支持實(shí)時(shí)報(bào)警,提供多種報(bào)警選項(xiàng),可以自定義參數(shù)。
- Google的Firebase
- crashlytics:服務(wù)器在國(guó)外,訪問(wèn)速度慢,會(huì)丟掉數(shù)據(jù)
- 友盟:crash之后會(huì)在再次啟動(dòng)的時(shí)候上報(bào)數(shù)據(jù),所以不能立即獲得這部分信息
客觀的衡量崩潰
- UV崩潰率:崩潰造成的用戶影響范圍,UV崩潰率 = 發(fā)生崩潰的UV / 登錄UV
- 異常率:UV異常率 = 發(fā)生異常退出或崩潰的UV / 登錄UV
- PV崩潰率
- 啟動(dòng)崩潰率
- 重復(fù)崩潰率
使用安全模式:天貓App啟動(dòng)保護(hù)實(shí)踐來(lái)解決啟動(dòng)崩潰的問(wèn)題
檢測(cè)應(yīng)用ANR異常的方法
- 使用FileObserver監(jiān)聽 /data/anr/trace.txt的變化。很多高版本的系統(tǒng)沒(méi)有這個(gè)權(quán)限
- 監(jiān)控消息隊(duì)列的運(yùn)行時(shí)間,無(wú)法準(zhǔn)確判斷是否會(huì)出現(xiàn)ANR,而且也無(wú)法得到完整的ANR日志。
應(yīng)用退出情況總結(jié)
- 主動(dòng)自殺 Process.killProcess、exit()等
- 崩潰 出現(xiàn)了Java或Native崩潰
- 系統(tǒng)重啟
- 被系統(tǒng)殺死,被low memory killer或用戶從后臺(tái)任務(wù)中移除
- ANR
在應(yīng)用啟動(dòng)的時(shí)候設(shè)定標(biāo)志,在主動(dòng)自殺或崩潰(單獨(dú)統(tǒng)計(jì))后更新日志,下次啟動(dòng)檢查該標(biāo)志就能確認(rèn)運(yùn)行期間是否發(fā)生過(guò)異常退出,通過(guò)這個(gè)檢測(cè),可以反映ANR、low memory killer、系統(tǒng)強(qiáng)殺、死機(jī)、斷電等其他無(wú)法正常捕獲到的異常。
崩潰現(xiàn)場(chǎng)的信息采集
崩潰信息
- 進(jìn)程名、線程名。崩潰是發(fā)生在前臺(tái)進(jìn)程還是后臺(tái)進(jìn)程,是否發(fā)生在UI線程
- 崩潰堆棧和類型
系統(tǒng)信息
- Logcat:系統(tǒng)的event logcat會(huì)記錄App運(yùn)行的一些基本情況,在文件 /system/etc/event-log-tags中
- 機(jī)型,系統(tǒng),廠商,CPU,ABI,Linux版本等
- 設(shè)備狀態(tài),是否root,是否是模擬器等
內(nèi)存信息
- 系統(tǒng)剩余內(nèi)存:對(duì)于系統(tǒng)內(nèi)存狀態(tài),可以讀取/proc/meminfo
- 應(yīng)用使用內(nèi)存:包括java內(nèi)存,RSS,PSS
- 虛擬內(nèi)存:可以通過(guò)/proc/self/status得到,通過(guò)/proc/self/maps文件可以得到具體的分布情況
資源信息
- 文件句柄fd:文件句柄限制通過(guò)/proc/self/limits獲得,一般單個(gè)進(jìn)程允許打開的最大文件句柄個(gè)數(shù)是1024,超過(guò)800個(gè)比較危險(xiǎn)
- 線程數(shù),超過(guò)400個(gè)比較危險(xiǎn)
- JNI
應(yīng)用信息
- 崩潰場(chǎng)景,發(fā)生的Activity或Fragment
- 關(guān)鍵操作路徑
- 其他自定義信息
獲取Logcat方法
-
通過(guò)logcat命令獲取
優(yōu)點(diǎn):非常簡(jiǎn)單,兼容性好。
缺點(diǎn):整個(gè)鏈路比較長(zhǎng),可控性差,失敗率高,特別是堆破壞或者堆內(nèi)存不足時(shí),基本會(huì)失敗。 -
hook liblog.so實(shí)現(xiàn)。通過(guò)hook liblog.so 中__android_log_buf_write 方法,將內(nèi)容重定向到自己的buffer中。
優(yōu)點(diǎn):簡(jiǎn)單,兼容性相對(duì)還好。
缺點(diǎn):要一直打開。 -
自定義獲取代碼。通過(guò)移植底層獲取logcat的實(shí)現(xiàn),通過(guò)socket直接跟logd交互。
優(yōu)點(diǎn):比較靈活,預(yù)先分配好資源,成功率也比較高。
缺點(diǎn):實(shí)現(xiàn)非常復(fù)雜
獲取Java 堆棧
native崩潰時(shí),通過(guò)unwind只能拿到Native堆棧。我們希望可以拿到當(dāng)時(shí)各個(gè)線程的Java堆棧
-
Thread.getAllStackTraces()。
優(yōu)點(diǎn):簡(jiǎn)單,兼容性好。
缺點(diǎn):
a. 成功率不高,依靠系統(tǒng)接口在極端情況也會(huì)失敗。
b. 7.0之后這個(gè)接口是沒(méi)有主線程堆棧。
c. 使用Java層的接口需要暫停線程 -
hook libart.so。
通過(guò)hook ThreadList和Thread的函數(shù),獲得跟ANR一樣的堆棧。為了穩(wěn)定性,我們會(huì)在fork子進(jìn)程執(zhí)行。
優(yōu)點(diǎn):信息很全,基本跟ANR的日志一樣,有native線程狀態(tài),鎖信息等等。
缺點(diǎn):黑科技的兼容性問(wèn)題,失敗時(shí)可以用Thread.getAllStackTraces()兜底