隨著移動互聯網紅利的結束,移動應用開發的爆發期已經結束,現在已經進入穩定期,現在大家講得最多是用戶體驗和應用質量,現在各種移動應用功能同質化很嚴重,所以如何打造出一款高質量的移動應用是留住用戶的的先決條件。
過去的 iOS 開發者可能做夢也想不到,現在也要開始適配屏幕和雙卡雙待,更不用說Android那么多如繁星的機型,廠家和操作系統,如果應用要出海,還要面對幾十個國家不同的語言和環境。另一方面,我們的業務越來越復雜,如何管理上十幾個上百個模塊,以及還要面對React Native,Flutter,Kotlin,Tensorflow等各種語言跟框架堆積在一起的情況,所以做一款高質量的應用需要做很多的工作。一個應用至少要經過開發,編譯CI,測試,灰度和發布幾個階段,見如下圖所示:
除了在開發測試以及灰度階段對應用進行分析外,應用一旦安裝到用戶手機上后對用戶操作數據的分析才是最難捕獲的,所以選擇一款成熟穩定的移動端APM(Application Performance Management)平臺是我們分析數據的堅實后盾,國內像阿里,騰訊,美團點評,餓了么,愛奇藝這些大廠都在大力投入研發應用性能管理平臺。Google今年也發力Android Vitals監控,新增了耗電,權限管理等模塊。
移動APM質量平臺好處
1、統一管理,所有階段的異常數據都匯總到一個平臺;
2、統一三端,現在大部分應用都由Android,IOS,H5多個端組成,隨著技術的發展還可能增加React Native,Flutter,Kotlin等新技術模塊的監控,所以統一各個端非常重要。
移動應用的質量主要包括穩定性和性能,像崩潰,卡死,白屏這些問題對于用戶而言是致命的,另一大類問題就是性能問題,安裝包大小,啟動,耗時,耗電,流量等范疇,具體分類如下:
由于Android碎片化和國內Android生態的亂象,手機廠商的隨便定制ROM,導致國內Android應用需要對各個廠商的手機進行適配,在今年11月份舉辦的Android綠色聯盟開發者大會上推出的應用體驗標準,對應用的兼容性,穩定性,性能,功能和安全做了詳細的定義,我們可以根據這組數據對我們的Android應用進行優化。
雖然移動APM質量平臺可以幫助我們快速發現和定位問題,但是監控不能保證實現高質量,這里還需要程序員進行分析和優化,根據上面提到的移動應用質量指標,本文從崩潰,內存優化,卡頓定位和分析,以及應用啟動等幾個方面淺談一下如何進行優化。
Android app崩潰率可以用:UV崩潰率=發生崩潰的UV / 登錄UV,只要用戶發生過一次崩潰就會被計算到。
1、Android崩潰分類:
1、java崩潰;
2、Native崩潰。
簡單來說,Java崩潰就是在Java代碼中,出現了未捕獲異常,導致程序異常退出,Java崩潰相對來說比較容易捕獲。在Android系統中有一個UncaughtExceptionHandler類,可以在uncaughtException回調函數中對異常進行捕獲然后上報到APM質量平臺。但是Native崩潰會比較麻煩,Native崩潰一般是在c/c++代碼中訪問了非法地址,也可能是地址對齊出現了問題,或者發生了程序主動abort,這些都會產生signal信號,導致程序異常退出。
2、Native崩潰的捕獲流程:
1、編譯階段:編譯c/c++的時候需要把符號信息保留下來;
2、客戶端,捕獲到異常的時候,盡可能地將有用的信息保存到本地,然后選擇適當的時機上報服務器;
3、服務端,讀取客戶端上報的日志文件,尋找的的符號文件,生成可讀的c/c++調用棧。目前Native崩潰捕獲最成熟的方案就是google的breakpad方案,在github上git clone https://github.com/google/breakpad.git ,可以在Linux或者mac平臺上編譯出minidump_stackwalk,dump_syms工具。通過dump_sysm工具可以生成發生崩潰so文件的符號表,通過mindump_stackwalk工具可以生成上報native崩潰日志的調用棧,結合符號表就能定位到發生崩潰的位置。
崩潰處理
1、Java崩潰類型比較明顯,實際開發過程中NullPointerException空指針的情況比較多,從后臺獲取的數據沒有判空就就進行使用等情況容易產生空指針異常,或者OutOfMemoryError,使用了大圖片沒有及時釋放導致內存耗盡;
2、Native崩潰需要觀察signal,code,fault addr等信息;
3、ANR的時候先看主線程的堆棧,是否因為鎖等待導致,接著看ANR日志文件中iowait、CPU、GC、system server等信息,進一步確定是I/O問題,或者是CPU競爭問題,還是由于大量GC導致卡死。
內存優化
內存優化是崩潰優化中非常重要的一個部分,類似OOM,很多的異常退出都是由于內存問題引起的。內存引起的第一個問題就是異常,包括OOM,內存分配失敗,內存整體不足引起應用被殺死;內存造成的第二個問題是卡頓,java內存不足會引起頻繁地GC,除了頻繁GC造成卡頓外,物理內存不足會引起系統觸發low memoery kill機制。
內存優化主要從以下三個方面入手:
1、設備分級;
2、bitmap優化;
3、內存泄漏。
Facebook 開發的檢測手機主流配置工具device-year-class,我們可以對低端手機關閉復雜的動畫效果,使用565格式圖片,使用更小的緩存策略來提升應用在低端機上的體驗。
根據以上的設備內存分配圖,可以使用一下代碼,根據不同設備使用不同的動畫顯示策略。
if (year >= 2013) {
// Do advanced animation
} else if (year > 2010) {
// Do simple animation
} else {
// Phone too slow, don't do any animations
}
一個空的進程也會占用10MB的內存,所以在低端機器上盡可能減少應用啟動進程數,減少常駐進程數,盡量不要使用進程?;罴夹g。一個80MB的應用很難在512MB內存的手機上流暢地運行起來,可以針對低端機用戶推出輕量版本,比如facebook Lite,今日頭條極速版本都是這個思路。
Bitmap內存一般占據應用總內存很大一部分,把bitmap放到native內存,雖然可以減少GC頻繁調用帶來的問題,提高了系統內存利用率,但是并不能解決bitmap占用內存過大的問題。Bitmap優化前提就是限制圖片的調用,即限制Bitmap.createBitmap,BitmapFactory相關接口的調用,可以考慮使用統一的圖片庫比如Glide,Fresco等。檢測大圖片,例如長寬遠遠大于view甚至屏幕的寬高,就需要對這個大圖片進行優化,重復圖片監控,如果多個bitmap的像素數據完全一致,就應該刪除冗余的圖片。內存泄漏,應該從架構上進行設計,例如,避免長周期的對象持有短周期對象,各種監聽器盡量不要引用Activity或者Fragment的context。Java內存泄漏可以借助類似LeakCanary自動化檢查工具,可以做到Activity和Fragment的內存泄漏檢查。
應用卡頓都和CPU時間相關,CPU時間分為兩種:用戶時間和系統時間。用戶時間是應用程序執行代碼消耗的時間;系統時間是執行內核系統調用所消耗的時間,包括I/O、鎖、中斷以及其他系統調用時間。通過cat /proc/stat得到整個系統CPU的使用情況,然后通過cat /proc/[pid]/stat獲取某個進程的CPU使用情況,如果CPU使用率長期大于60%,標識系統處于繁忙狀態,就需要進一步分析用戶時間和系統時間的比例。對于普通的應用程序,系統時間一般不會超過30%,如果超過這個值,就需要進一步檢查是不是I/O過多,或者是其他系統調用問題。
Andriod卡頓排查的主流工具
1、Traceview;
Traceview利用Android Runtime函數調用的event事件,將函數運行的耗時和調用關系寫入trace文件,此工具本身有很大的性能開銷,有時無法真是反應實際情況,比如一個函數本身耗時1秒,但是Traceview工具開啟后可能變成了5秒。
2、Nanoscope;
Nanoscope是uber開源的工具,它直接修改Android虛擬機源碼,在ArtMethod執行入口和執行結束位置增加埋點代碼,將所有信息寫入到內存,等到trace結束統一生成結果文件,它不會帶來額外的性能開銷,可以任意分析一個應用,但是需要自己刷ROM,目前只支持Nexus 6P。
3、systrace;
Systrace利用Linux的ftrace工具,在系統各個關鍵位置增加了監控埋點,可以對Graphics,Activity Manager,Dalvik VM,System server進行監控,而且性能開銷非常低,但是它不支持應用程序代碼耗時分析,使用起來有一定的局限性。
4、Simpleperf。
Simpleperf,可以分析Native函數耗時,它是Android5.0以后增加的性能分析工具,它可以監控dex,verify class等的耗時,在Android studio3.2可以直接在profiler中直接使用??偟膩碚f卡頓分析的話,如果分析Native代碼耗時,可以選擇simpleperf;如果想分析系統調用可以選擇systrace;如果想分析整個程序執行流程的耗時可以選擇traceview或者插樁版本的systrace。
Android APP啟動過程優化
Android APP啟動過程:
1、點擊桌面圖標解析Manifest;
2、Application創建,閃屏Activity創建;
3、MainActivity主界面創建;
4、啟動完成界面可操作。
根據整個啟動流程我們可以把啟動優化分為:閃屏優化,業務梳理,業務優化,線程優化,GC優化和系統調用優化。
一般應用都會先創建SplashActivity,然后在創建MainActivity,如果能把兩個Activity合成一個,可以節省100ms左右的優化,通過MainActivity先展示SplashFragment,展示完畢有remove掉,同時在閃屏的2秒時間內進行首頁網絡數據的緩存,同時采用viewstub形式對activity_main的布局進行懶加載,防止首頁過于復雜耽誤view的解析時間。
由于很多項目會使用插件化,熱更新等技術,而這些框架會使用各種反射,各種HOOK技術,這是非常耗時的(平均需要幾百毫秒),所以應該對這些業務模塊進行優化。
線程優化主要是減少CPU調度帶來的波動,應該控制線程數,線程太多會互相競爭CPU資源,統一的線程池來管理,根據機器性能控制線程數。另外可以利用systrace檢查是否有線程鎖,因為主線程會因為有線程鎖而等待,出現主線程長時間空轉。
啟動過程盡量減少GC次數,避免造成主線程長時間卡頓,通過systrace查看整個啟動過程GC的時間,如果發現GC同步等待,那就需要使用allocation工具做進一步分析。
啟動過程中避免進行大量字符串操作,特別是序列化和反序列化。一些頻繁的創建對象,比如在網絡庫和圖片庫中byte數組,buffer盡量重復使用。如果一些模塊確實需要頻繁創建對象,可以考慮移到Native實現。通過systrace的System Service類型可以查看System Server的CPU工作情況,在app啟動過程中,盡量不要做系統調用,比如PackageMangerService操作,Binder調用等等。
結語:
開一發一款高質量的應用涉及到的知識和內容比較多,本文基本上歸納總結了大致的方向,和一些實踐中的應用總結,把所有的方面都做到了,非常耗費人力和時間,但是我們可以把這個作為一個終極目標,不斷打磨產品,爭取為用戶帶來更好的移動體驗。
原文轉自:http://college.creditease.cn/#/detail/205