您好,登錄后才能下訂單哦!
前言
開發人員碰到 APP 崩潰(閃退)什么辦?不少人會說根據 Log,找到閃退的代碼,捕獲異常,“消化”掉了所有 Java 崩潰。
至于程序是否會出現其他異常表現,那是上帝要管的事情。是的,這種方法對于緊急情況下不失為一種解決辦法,但閃退的真相是什么?
是否從根源上解決問題呢?
崩潰率是衡量一個應用質量高低的基本指標,那么,該怎樣客觀地衡量崩潰這個指標,以及又該如何看待和崩潰相關的穩定性。
Android 的兩種崩潰:
簡單來說,Java 崩潰就是在 Java 代碼中,出現了未捕獲異常,導致程序異常退出。
那 Native 崩潰一般都是因為在 Native 代碼中訪問非法地址,也可能是地址對齊出現了問題,或者發生了程序主動 Abort,這些都會產生相應的 Signal 信號,導致程序異常退出。
1.1 崩潰的收集
“崩潰”就是程序出現異常,而一個產品的崩潰率,跟我們如何捕獲、處理這些異常有比較大的關系。
對于很多中小型公司來說,可以選擇一些第三方的服務。
目前各種平臺也是百花齊放,包括阿里的友盟、騰訊的Bugly、網易云捕、Google 的 Firebase 等等。要懂得借力!
1.2 ANR
崩潰率是不是就能完全等價于應用的穩定性呢?答案是肯定不行。處理了崩潰,我們還會經常遇到 ANR(Application Not Responding,程序沒有響應)這個問題。
出現 ANR 的時候,系統還會彈出對話框打斷用戶的操作,這是用戶非常不能忍受的。
ANR處理方法:
使用 FileObserver 監聽 /data/anr/traces.txt 的變化。
非常不幸的是,很多高版本的 ROM,已經沒有讀取這個文件的權限了。這個時候你可能只能思考其他路徑,海外可以使用 Google Play 服務,而國內微信利用Hardcoder框架(HC 框架是一套獨立于安卓系統實現的通信框架,它讓 App 和廠商 ROM 能夠實時“對話”了
目標就是充分調度系統資源來提升 App 的運行速度和畫質,切實提高大家的手機使用體驗)向廠商獲取了更大的權限。
也可以將手機 ROOT 掉,然后取得 traces.txt 文件。
1.3 應用退出
除了常見的崩潰,還有一些會導致應用異常退出的情況,例如:
我們可以在應用啟動的時候設定一個標志,在主動自殺或崩潰后更新標志,這樣下次啟動時通過檢測這個標志就能確認運行期間是否發生過異常退出。
對應上面的五種退出場景,我們排除掉主動自殺和崩潰(崩潰會單獨的統計)這兩種場景,希望可以監控到剩下三種的異常退出,理論上這個異常捕獲機制是可以達到 100% 覆蓋的。
通過這個異常退出的檢測,可以反映如 ANR、low memory killer、系統強殺、死機、斷電等其他無法正常捕獲到的問題。
當然異常率會存在一些誤報,比如用戶從系統的任務管理器中劃掉應用。對于線上的大數據來說,還是可以幫助我們發現代碼中的一些隱藏問題。
根據應用的前后臺狀態,我們可以把異常退出分為前臺異常退出和后臺異常退出。
“被系統殺死” 是后臺異常退出的主要原因,當然我們會更關注前臺的異常退出的情況,這會跟 ANR、OOM 等異常情況有更大的關聯。
我們每天工作也會遇到各種各樣的疑難問題,“崩潰”就是其中比較常見的一種問題。解決問題跟破案一樣需要經驗,我們分析的問題越多越熟練,定位問題就會越快越準。
當然這里也有很多套路,比如對于 “案發現場” 我們應該留意哪些信息?怎樣找到更多的 “證人” 和 “線索” ?
“偵查案件” 的一般流程是什么?對不同類型的 “案件” 分別應該使用什么樣的調查方式?
要相信 “真相永遠只有一個”,崩潰也并不可怕。
2.1 崩潰現場
崩潰現場是我們的“第一案發現場”,它保留著很多有價值的線索。現在可以挖掘到的信息越多,下一步分析的方向就越清晰,而不是去靠盲目猜測。
崩潰信息
從崩潰的基本信息,我們可以對崩潰有初步的判斷。進程名、線程名。
崩潰的進程是前臺進程還是后臺進程,崩潰是不是發生在 UI 線程。
崩潰堆棧和類型。崩潰是屬于 Java 崩潰、Native 崩潰,還是 ANR,對于不同類型的崩潰關注的點也不太一樣。
特別需要看崩潰堆棧的棧頂,看具體崩潰在系統的代碼,還是 APP 代碼里面。
關鍵字:FATAL
FATAL EXCEPTION: main Process: com.cchip.csmart, PID: 27456 java.lang.NullPointerException: Attempt to invoke virtual method 'void android.widget.TextView.setText(int)' on a null object reference at com.cchip.alicsmart.activity.SplashActivity$1.handleMessage(SplashActivity.java:67) at android.os.Handler.dispatchMessage(Handler.java:102) at android.os.Looper.loop(Looper.java:179) at android.app.ActivityThread.main(ActivityThread.java:5672) at java.lang.reflect.Method.invoke(Native Method) at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:784) at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:674)
系統信息
系統的信息有時候會帶有一些關鍵的線索,對我們解決問題有非常大的幫助。
Logcat。這里包括應用、系統的運行日志。由于系統權限問題,獲取到的 Logcat 可能只包含與當前 APP 相關的。
其中系統的 event logcat 會記錄 APP 運行的一些基本情況,記錄在文件 /system/etc/event-log-tags 中。
//system logcat:10-25 17:13:47.788 21430 21430 D dalvikvm: Trying to load lib ... //event logcat:10-25 17:13:47.788 21430 21430 I am_on_resume_called: 生命周期10-25 17:13:47.788 21430 21430 I am_low_memory: 系統內存不足10-25 17:13:47.788 21430 21430 I am_destroy_activity: 銷毀 Activty10-25 17:13:47.888 21430 21430 I am_anr: ANR 以及原因10-25 17:13:47.888 21430 21430 I am_kill: APP 被殺以及原因
機型、系統、廠商、CPU、ABI、Linux 版本等。通過采集多達幾十個維度,這對尋找共性問題會很有幫助。
內存信息
OOM、ANR、虛擬內存耗盡等,很多崩潰都跟內存有直接關系。
如果把用戶的手機內存分為“2GB 以下”和“2GB 以上”兩個區,就會發現“2GB 以下”用戶的崩潰率是“2GB 以上”用戶的幾倍。
系統剩余內存。關于系統內存狀態,可以直接讀取文件 /proc/meminfo。
當系統可用內存很小(低于 MemTotal 的 10%)時,OOM、大量 GC、系統頻繁自殺拉起等問題都非常容易出現。
應用使用內存。包括 Java 內存、RSS(Resident Set Size)、PSS(Proportional Set Size),我們可以得出應用本身內存的占用大小和分布。
PSS 和 RSS 通過 /proc/self/smap 計算,可以進一步得到例如 apk、dex、so 等更加詳細的分類統計。
虛擬內存。虛擬內存可以通過 /proc/self/status 得到,通過 /proc/self/maps 文件可以得到具體的分布情況。
有時候我們一般不太重視虛擬內存,但是很多類似 OOM、tgkill 等問題都是虛擬內存不足導致的。
Name: com.xmamiga.name // 進程名FDSize: 800 // 當前進程申請的文件句柄個數VmPeak: 3004628 kB // 當前進程的虛擬內存峰值大小VmSize: 2997032 kB // 當前進程的虛擬內存大小Threads: 600 // 當前進程包含的線程個數
一般來說,對于 32 位進程,如果是 32 位的 CPU,虛擬內存達到 3GB 就可能會引起內存申請失敗的問題。如果是 64 位的 CPU,虛擬內存一般在 3~4GB 之間。
當然如果我們支持 64 位進程,虛擬內存就不會成為問題。
Google Play 要求 2019 年 8 月一定要支持 64 位,在國內雖然支持 64 位的設備已經在 90% 以上了,但是商店都不支持區分 CPU 架構類型發布,普及起來需要更長的時間。
資源信息
有的時候會發現應用堆內存和設備內存都非常充足,還是會出現內存分配失敗的情況,這跟資源泄漏可能有比較大的關系。
文件句柄 fd。
文件句柄的限制可以通過 /proc/self/limits 獲得,一般單個進程允許打開的最大文件句柄個數為 1024。
但是如果文件句柄超過 800 個就比較危險,需要將所有的 fd 以及對應的文件名輸出到日志中,進一步排查是否出現了有文件或者線程的泄漏。
opened files count 812:0 -> /dev/null1 -> /dev/log/main4 2 -> /dev/binder3 -> /data/data/com.xmamiga.sample/files/test.config ...
線程數。當前線程數大小可以通過上面的 status 文件得到,一個線程可能就占 2MB 的虛擬內存,過多的線程會對虛擬內存和文件句柄帶來壓力。
根據我的經驗來說,如果線程數超過 400 個就比較危險。
需要將所有的線程 id 以及對應的線程名輸出到日志中,進一步排查是否出現了線程相關的問題。
threads count 412: 1820 com.xmamiga.crashsdk 1844 ReferenceQueueD 1869 FinalizerDaemon ...
JNI。使用 JNI 時,如果不注意很容易出現引用失效、引用爆表等一些崩潰。
應用信息
除了系統,其實我們的應用更懂自己,可以留下很多相關的信息。崩潰場景。
崩潰發生在哪個 Activity 或 Fragment,發生在哪個業務中; 關鍵操作路徑,不同于開發過程詳細的打點日志,我們可以記錄關鍵的用戶操作路徑,這對我們復現崩潰會有比較大的幫助。其他自定義信息。
不同的應用關心的重點可能不太一樣。
2.2 崩潰分析
有了這么多現場信息之后,就可以開始真正的“破案”之旅了。
絕大部分的 “案件” 只要肯花功夫,最后都能真相大白。不要畏懼問題,經過耐心和細心地分析,總能敏銳地發現一些異常或關鍵點,并且還要敢于懷疑和驗證。
第一步:確定重點
確認和分析重點,關鍵在于終過日志中找到重要的信息,對問題有一個大致判斷。一般來說,我建議在確定重點這一步可以關注以下幾點。
Java 崩潰。Java 崩潰類型比較明顯,比如 NullPointerException 是空指針,OutOfMemoryError 是資源不足,這個時候需要去進一步查看日志中的 “內存信息”和“資源信息”。
Native 崩潰。需要觀察 signal、code、fault addr 等內容,以及崩潰時 Java 的堆棧。
關于各 signal 含義的介紹,你可以查看崩潰信號介紹。
比較常見的是有 SIGSEGV 和 SIGABRT,前者一般是由于空指針、非法指針造成,后者主要因為 ANR 和調用 abort() 退出所導致。
ANR。先看看主線程的堆棧,是否是因為鎖等待導致。
接著看看 ANR 日志中 iowait、CPU、GC、system server 等信息,進一步確定是 I/O 問題,或是 CPU 競爭問題,還是由于大量 GC 導致卡死。
第二步:查找共性
如果使用了上面的方法還是不能有效定位問題,我們可以嘗試查找這類崩潰有沒有什么共性。
找到了共性,也就可以進一步找到差異,離解決問題也就更進一步。
機型、系統、ROM、廠商、ABI,這些采集到的系統信息都可以作為維度聚合,共性問題例如是不是只出現在 x86 的手機,是不是只有三星這款機型,是不是只在 Android 8.0 的系統上。
應用信息也可以作為維度來聚合,比如正在打開的鏈接、正在播放的視頻、國家、地區等。
找到了共性,可以對你下一步復現問題有更明確的指引。
第三步:嘗試復現
如果我們已經大概知道了崩潰的原因,為了進一步確認更多信息,就需要嘗試復現崩潰。
如果我們對崩潰完全沒有頭緒,也希望通過用戶操作路徑來嘗試重現,然后再去分析崩潰原因。
“只要能本地復現,我就能解”,相信這是很多開發跟測試說過的話。
有這樣的底氣主要是因為在穩定的復現路徑上面,我們可以采用增加日志或使用 Debugger、GDB 等各種各樣的手段或工具做進一步分析。
我們可能會遇到了各種各樣的奇葩問題。
比如某個廠商改了底層實現、新的 Android 系統實現有所更改,都需要去 Google、翻源碼,有時候還需要去摳廠商的 ROM 或手動刷 ROM。
很多疑難問題需要我們耐得住寂寞,反復猜測、反復發灰度、反復驗證。
–但這種問題還是要看問題的嚴重程序,不可撿了芝麻丟了西瓜。
2.3 系統崩潰
系統崩潰常常令我們感到非常無助,它可能是某個 Android 版本的 Bug,也可能是某個廠商修改 ROM 導致。這種情況下的崩潰堆棧可能完全沒有我們自己的代碼,很難直接定位問題。能做的有:
崩潰攻防是一個長期的過程,我們盡可能地提前預防崩潰的發生,將它消滅在萌芽階段。
作為技術人員,我們不應該盲目追求崩潰率這一個數字,應該以用戶體驗為先,如果強行去掩蓋一些問題往往更加適得其反。
我們不應該隨意使用 try catch 去隱藏真正的問題,要從源頭入手,了解崩潰的本質原因,保證后面的運行流程。
在解決崩潰的過程,也要做到由點到面,不能只針對這個崩潰去解決,而應該要考慮這一類崩潰怎么解決和預防。
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。