作者|張軍(皓遠)、秦靜超(非臺)、儲龍江(大音)
出品|阿里巴巴新零售淘系技術部
前言
白屏、卡頓、頁面加載失敗、頁面跳轉慢、圖片空窗、崩潰、耗電等問題一直都是用戶頻繁輿情反饋的痛點。
如何衡量和提升客戶端的性能和提升體驗,一直都是技術在不斷探索和解決的問題。
Google也在每一次的Android Studio的迭代中,不斷增加了大量的性能排查工具幫助開發提升代碼質量,作為了全世界第一的電商APP,如何定義APP的可用性,以及提升APP的可用性,一直以來都是手淘技術不斷追求的目標。
▐ 用戶痛點
輿情反饋
輿情信息的整理,主要是Crash、白屏、網絡出錯、黑屏、圖片不顯示、ANR等。
輿情的統計數據,其中每天有X條與性能體驗相關的輿情,一條輿情背後是上萬級別的用戶面臨的痛點。
線上性能問題形勢嚴峻,其中卡頓、打不開、白屏、無反應、閃退、頁面加載慢等用戶痛點問題是輿情的TOP問題,也是全鏈路性能體驗治理的發力點。
▐ 全鏈路性能體驗目標
客戶端的穩定性和性能既是用戶體驗的痛點,也一直都是各個應用技術團隊關心的指標,Google的開發團隊也為Android的開發者們量身打造了Android Studio去發現和定位各種性能問題。
如卡頓分析工具TraceView、SysTrace,佈局優化工具Hierarchy Viewer,過度繪製工具開發者模式下的OverDraw,內存分析工具Memory Monitor、Heap Viewer,內存跟蹤工具Allocation Tracker,內存洩漏分析工具MAT,ANR 分析工具Analyze Stacktrace,耗電分析工具Battery Historian,其他還提供了非常強大的能力如DropBox、StrictMode等等。各式各樣的專項工具都在全方位的幫助開發者快速的發現和定位,從而打造一款良好體驗的APP。
然而各式各樣Google Android專項工具都需要開發主動去使用才能發現問題,一般都是在開發同學遇到某一個或者某一類問題的時候才會想到用特定的工具去排查問題,往往只能在事後才能發現問題。
本系列從全新的視野和角度出發,打造一款業務無入侵性的SDK-Olympic,Olympic是對手機淘寶歷史性能問題的排查、分析和解決的沉澱後的產物,將在手淘歷史上出現的問題的排查方法積累下來後,面向Android客戶端的性能排查平臺。
其使命是“數據為中心,平臺為依託,打造移動端高可用旗艦APP”,幫助開發同學事先分析和定位問題,問題直指代碼調用行、View路徑和內存引用鏈,並通過問題的排查和積累,提供《問題快速解決手冊》幫助開發同學快速解決問題,從而減少定位問題的成本,降低線上風險,Olympic的目標是發現可以從技術上優化解決的性能問題。相比與Google Android Studio提供的排查能力,Olympic更加輕量和準確。
全鏈路性能體驗,基於Olympic-SDK,打通TMQ線下性能自動化卡口,EMAS性能數據採集與統計,實現完整的性能相關問題發現、分析、卡口、統計的鏈路,並命名為Olympic,賦能手淘性能,Olympic的目標是成為手淘性能問題的“線下集成卡口、線上發佈標準”。
打造高可用APP(GSM)
圖1 打造一款高可用的移動APP(Goal)
整體架構
以數據為中心,平臺為依託,打造移動端高可用旗艦APP,全方位的性能和穩定性監控,分為穩定性相關的檢測、卡頓相關的檢測、安全相關的檢測、耗電相關檢測、流量相關檢測,每個領域都是一個大的專項,細分許多子專項進行詳解。項目名稱我們取之為Olympic,意味著讓手機客戶端“更快,更高,更遠”。
Olympic治理性能問題並不是衡量Olympic好壞的唯一標準,Olympic真正的價值在於沉澱提升體驗的方法論,沉澱提升體驗的基礎設施,從幫助開發快速的解決痛點,拿到好的結果。
★ 數據是整個項目中的靈魂,它如同石油,只有數據才能讓整個項目流暢的運作起來。
Olympic幫助HA-EMAS做數據呈現,報表展示,風險分析,訂閱跟報警;
Olympic幫助MOTU-EYE做AB實驗,通過數據幫助業務更好的決策;
Olympic幫助TMQ做線下驗收,持續集成,把問題攔在線下;
Olympic幫助輿情平臺提供問題信息,幫助開發快速解決用戶問題。
核心技術創新
▐ TMQ 性能卡口自動化
全鏈路性能體驗(Olympic)性能卡口自動化部分,基於 TMQ 平臺智能Monkey自動化測試能力,通過華為、VIVO、OPPO、榮耀、小米和Pixel等設備,系統覆蓋Android 4.x-10.x,以Olympic性能SDK能力為核心,對內存相關問題、資源洩漏、卡頓和安全等問題進行檢測,實現線下自動化卡口方案和手工驗收方案。
其中線下自動化卡口方案,打通手淘Android Debug包、DebugSetting、TMQ、TMQ設備中臺、Aone平臺和摩天輪平臺,實現全自動化的性能問題發現、存儲、定位、分發和卡口。其中手工驗收方案,在測試驗收過程中可以通過DebugSetting的“全鏈路性能驗收工具(Olympic)”查看驗收過程中發生的異常性能問題列表,並對確認的問題,提bug到aone必改問題空間。
Olympic性能卡口自動化部分至今累計發現內存洩漏(Activity洩露、Service洩露、Broadcast洩露)、資源洩漏(文件描述符未關閉、Sqlite未關閉、圖片資源未釋放)和ANR等必改問題X00+個。
▐ 大圖片檢測
大圖片是指控件所持有的Bitmap對象的寬高遠超了View自身的寬高,導致內存處於不必要的浪費,不但對內存存在挑戰,同時也對IO存在挑戰,合理的使用圖片不僅能提升內存質量,減少IO,同時也能帶來視覺上的良好體驗。手淘客戶端對圖片的有著大量的應用,解析圖片導致的OOM也不勝枚舉。
Olympic中開發了OverBitmapPlugin,針對大圖片問題量身定製,直接發現大圖片和其頁面中的View路徑,幫助開發快速發現和解決問題,大圖檢測單項累計發現X000+問題。
▐ SharedPreferences優化
主線程進行SharedPreferences的commit和apply操作會導致ANR的產生(此插件幫組手淘100%解決了SP導致的ANR),Olympic的SharedPreferences檢查插件可以幫助你發現可優化的SharedPreferences代碼,同時同步實現了手淘的SharedPreferences,能幫你快速替換系統的SharedPreferences提升代碼質量,改善用戶體驗,減少不必要的ANR。一個小細節給用帶來的不僅僅是體驗,更是手機淘寶“用戶第一”的態度。
圖2 線上主線程操作SharedPreferences導致ANR堆棧
通過線上ANR堆棧可以看到,主線程操作SharedPreferences,導致主線程耗時4481ms,此操作很容易導致主線程ANR。
既然UI線程SharedPreferences調用commit或者非UI線程SharedPreferences調用apply都有可能導致ANR,那是否有方式能很好的避免ANR呢?
答案是通過在非UI線程的SharedPreferences調用commit來優雅的解決以上問題。思維很好,但是實際操作比較麻煩:
Android的開發者已經被SharedPreferences訓練了很多次,包括AndroidStudio也會推進開發者將commit改成apply,從而提升體驗(這個Android官方的推薦使用方法其實我本人在手淘的世始建過程中是不認可的);
在大應用模塊化開發的環境下,如何保證所有的開發同學(包含新手同學)能遵守“非UI線程的SharedPreferences調用commit”這個方案來解決ANR是很難的(我個人的看法是‘如果沒有合理的規範化的開發規範,人是最容易出錯的’)。
基於對以上兩點的思考,開始著手思考,通過無入侵的SharedPreferences使用不合理的探測插件,通過規範化的開發手冊,快速幫助開發同學矯正SharedPreferences開發方式,以最快的速度獲取最大的收益。
問題快速解決手冊
▐ 內存監控
內存問題,說的更直白點,就是你想讓一個對象在下次GC的時候徹底被回收,但是這個對象所處的條件不符合GC所認定的應當回收的條件,而導致實際上沒有被回收依然佔用著內存空間,像這樣的對象多了,遲早會把內存撐爆引發大名鼎鼎的OOM問題。
內存撐爆引發大名鼎鼎的OOM問題其實有兩類,一類是內存該釋放不釋放,主要是靜態全局對象引用了不該引用的對象,如Activity洩漏,Service洩漏和Receiver洩漏;另一類則是內存使用不合理,如可以用更少內存完成的任務,卻用了大量的內存來完成,主要發生的圖片端,如在Android設備中res目錄下的drawable目錄存放高清大圖。
Activity洩漏
Activity洩漏是指當Activity對象生命週期結束後Activity被其他對象強引用,得不到及時的釋放導致的內存洩漏,Activity一般承接在頁面的整體視覺,持有著圖片,因此Activity洩漏會對內存造成很大的負擔,也是開發者治理內存洩漏的重中之重,當然開源社區也有成熟的檢測SDK幫助開發者開始的發現Activity的洩漏問題,如LeakCanary[1]。
Olympic中開發了ActivityLeaksPlugin,針對Activity洩漏量身定製,直接發現洩漏的Activity和其引用鏈,幫助開發快速發現和解決問題,詳細原理參考LeakCanary[1]。
Service洩漏
Service洩漏是指在使用Activity進行bindService後,在Activity生命週期銷燬之前沒有及時的unbindService導致的Service洩漏。bindService的行為一定要慎重的思考,bindService的生命週期,如果伴隨者APP整個生命週期運行的Service,建議使用applicaion去bindService,而避免使用其他的context去bindService。bindService還有一些點要注意,bindService可以通過Context去註冊運行時Service,意味著Service和Receiver的Context也可以去BindService,隨著Service和Receiver的創建和銷燬可能帶來了大量的Service的洩漏。
Olympic中開發了ServiceLeaksPlugin,針對Service洩漏量身定製,直接發現洩漏的Service和調用棧,幫助開發快速發現和解決問題。
Receiver洩漏
Receiver洩漏是指在使用Activity進行registerReceiver後,在Activity生命週期銷燬之前沒有及時的unregisterReceiver導致的Receiver洩漏。registerReceiver的行為也要慎重的思考,registerReceiver的生命週期,如果伴隨者APP整個生命週期運行的Receiver,建議使用applicaion去registerReceiver,而避免使用其他的context去registerReceiver。registerReceiver還有一些點要注意,registerReceiver可以通過Context去註冊運行時Service,意味著Service和Receiver的Context也可以去registerReceiver,隨著Service和Receiver的創建和銷燬可能帶來了大量的Receiver的洩漏。
Olympic中開發了ReceiverLeaksPlugin,針對Recevier洩漏量身定製,直接發現洩漏的Recevier和調用棧,幫助開發快速發現和解決問題。
▐ 資源洩漏
資源洩漏也是開發者老生常談的另一個令人頭痛的問題,隨著洩漏的增加系統的可用資源不斷減少,最終走向不可求藥的崩潰,而這類問題和內存問題一樣一般很難排查到問題的根因。最常見的文件描述符洩漏、線程洩漏、Socket洩漏和數據庫洩漏。
文件描述符洩漏
這裡的文件描述符洩漏主要是指打開的文件沒有及時的關閉導致的洩漏問題。Olympic中開發了CloseableObjectsLeaksPlugin,針對資源未釋放的問題量身定製,直接發現資源open的調用棧,幫助開發快速發現和解決問題。
線程洩漏
線程洩漏是指在程序運行過程中創建的線程對象在程序運行過程中不會被使用,同時也不會釋放的線程創建行為,在Android應用中經常發生在死鎖的線程,不會退出的HandlerThread,以及非靜態全局的線程池創建。
Olympic中開發了ThreadLeaksPlugin,針對線程洩漏問題量身定製,直接發現洩漏的線程名,幫助開發快速發現和解決問題。
Socket洩漏
Socket洩漏是指打開的網絡連接沒有及時的關閉,或在網絡異常的時候沒有關閉Socket導致的洩漏。Olympic中開發了CloseableObjectsLeaksPlugin,針對資源未釋放的問題量身定製,直接發現資源open的調用棧,幫助開發快速發現和解決問題。
數據庫洩漏
數據庫洩漏是指打開的數據庫連接沒有及時的關閉,或在數據庫異常的時候沒有關閉數據庫連接而導致的洩漏。Olympic中開發了SqlLiteObjectsLeaksPlugin,針對數據庫未釋放的問題量身定製,直接發現數據庫open的調用棧及對應的數據庫名和SQL語句,幫助開發快速發現和解決問題。
▐ 卡頓
卡頓是指用戶在滑動過程中或在播放視頻和動畫的時候出現畫面滯幀的場景,都是卡頓的現象。FPS是衡量客戶端卡頓的一個很重要的指標。嚴格意義上來講,一般來講客戶端的FPS在60幀每秒,未達到60幀每秒的時候,都可以理解為出現了卡頓。而導致卡斷的原因也很多樣化,一般有消息耗時、主線程讀/寫、主線程網絡讀寫、主線圖片解析、主線程SlowCall,主線程碎片讀寫、主線程BinderCall、OverLayout、OverDraw、不合理SharedPreferences使用等。
消息耗時
Android的UI渲染主要是通過消息隊列的方式實現的,當之前的消息出現耗時行為的時候,會導致後續的消息得不到及時的執行,而出現卡頓的問題,更嚴重的話會出現ANR。Olympic中開發了MainBlockedPlugin,針對主線程卡頓問題量身定製,直接發現消息執行耗時超過1s的消息名和其調用棧,幫助開發快速發現和解決問題。
主線程讀/寫
主線程讀寫是指在Android的UI線程中有IO操作,用於存儲設備的硬件特性,往往寄存器的讀寫性能好過Cache,Cache的讀寫性能好過內存,內存的讀寫性能好過硬盤,而硬盤的讀寫性能往往是相對於內存的讀寫是及其糟糕的,因此Google的Android團隊也是建議開發者儘量避免在主線程操作IO。主線程操作IO會出現卡頓的問題,更嚴重的話會出現ANR。
Olympic中開發了DiskReadOnMainPlugin/DiskWriteOnMainPlugin,針對主線程IO問題量身定製,直接發現在主線程IO的方法調用棧,幫助開發快速發現和解決問題。
主線程網絡讀寫
主線程網絡讀寫是指在Android的UI線程中有網絡操作,用網絡操作的問題和主線IO的問題一樣甚至更嚴重,網絡的建聯和數據的接手都是非常耗時的操作,建議開發者避免在主線程操作網絡。主線程操作網絡會出現卡頓的問題,更嚴重的話會出現ANR。
Olympic中開發了NetworkPlugin,針對主線程網絡讀寫問題量身定製,直接發現在主線程網絡讀寫的方法調用棧,幫助開發快速發現和解決問題。
主線圖片解析
主線程圖片解析是指在Android的UI線程中執行Bitmap的創建操作。主線程圖片解析也會導致卡頓的問題,更嚴重的話會出現ANR。
Olympic中開發了BitmapPlugin,針對主線程圖片解析進行檢測。在詳細的原理篇中也會給出在主線程解析圖片優雅的解決方案。
主線程SlowCall
主線程SlowCall是指在Android的UI線程中處理比較耗時的方法,主要是圖片的Bitmap內存Copy,和網絡的是實例查找,都是比較耗時的方法,因此大家有儘可能的避免使用,或使用其代替方案。
Olympic中開發了SlowCallPlugin,針對主線程SlowCall問題量身定製,直接發現在主線程SlowCall的方法調用棧,幫助開發快速發現和解決問題。
主線程碎片讀寫
主線程碎片讀寫是指在主線程做大量的碎片讀寫,如inputStream.read()每次只讀取一個字節,操作系統對內存的讀寫有LRU的內存緩存命中優化,連續的讀寫有助於提高IO性能的,當然碎片讀寫的會導致LRU的內存緩存失效,從而觸發大量的內存交互,降低了手機的性能,導致頁面卡頓。
Olympic中開發了UnbufferedIOPlugin,針對主線程碎片讀寫問題量身定製,直接發現在主線程碎片讀寫方法調用棧,幫助開發快速發現和解決問題。
主線程BinderCall
主線程BinderCall是指在主線程做IBinder調用的時遠程對象時出現了異常,導致主線耗時的場景。開發者因儘可能的避免主線程BinderCall導致用戶體驗下降,更應避免存在異常的BinderCall,這會讓用戶體驗更糟糕。
Olympic中開發了BinderCallPlugin,針對主線程BinderCall問題量身定製,直接發現在主線程BinderCall方法調用棧,幫助開發快速發現和解決問題。
OverLayout
OverLayout是指在頁面佈局的時候存在冗餘的View,導致View在繪製的時候存現性能下降,而Android的開發團隊在Android Studio中為此類問題量身定製了Hierarchy Viewer插件,可見Android開發團隊對View佈局的重視。
Olympic中開發了OverLayoutPlugin,針對頁面佈局OverLayout量身定製,直接發現佈局結構中存在過度佈局的View並獲取其在頁面中的位置,幫助開發快速發現和解決問題。
OverDraw
OverDraw是指在頁面佈局的時候存在冗餘的背景設置,導致View在繪製的時候存現性能下降,而Android的開發團隊在“開發者模式”下為此類問題量身定製了“調試GPU過度繪製”功能,可見Android開發團隊對View OverDraw的重視。
Olympic中開發了OverDrawPlugin,採用線段樹算法針對頁面OverDraw量身定製,直接發現佈局結構中存在過度繪製的View並獲取其在頁面中的位置,幫助開發快速發現和解決問題。
▐ 視覺
白屏檢測
白屏檢測是指頁面View未渲染或者是在指定的時間內未得到及時的渲染導致頁面的曾現處於完全或者部分無預期內容的問題。
Olympic中開發了WhiteScreenPlugin,針對H5、WEEX、原生Native的特性量身實現,能發現各類白屏問題,通過輕量級UI描述還原白屏現場。目前正在“手淘架構實驗室-白屏實驗”發光發熱。
圖片404檢測
圖片空窗檢測是指頁面ImageView控件已經呈現,而網絡中下載的圖片由於已經被刪除或者CDN拼接出錯導致的圖片空窗的問題。
Olympic中開發了Image404Plugin,能發現頁面中存在圖片空窗的View,並指出其頁面所在的位置。
▐ 安全
近期facebook的隱私洩漏事件再度喚起了公眾對隱私的關注,在這個越來越重視個人隱私的時代裡,安全早已是一個無法繞開的話題。作為一個開發人員,必須具備安全意識,掌握基礎的安全知識,為打造更加安全的應用做出努力。涉及網絡明文、文件明文和外跳,其他更深層次的安全問題,如四大組件導出、WebView、存儲、傳輸、日誌、混淆、數據加密、應用加固等安全漏洞及防護策略,由於個人的知識面原因會在後續的文章中有所涉及但不會作為性能專項詳細講解。
網絡明文檢測
網絡明文是指在使用非安全的域名請求網絡請求,如請求http://example.com/,而不是請求https://example.com/。
Olympic中開發了CleartextNetworkPlugin,能幫助開發無意間明文請求網絡而導致數據洩漏。能定位到訪問的IP地址,幫助開發快速發現和解決問題。CleartextNetworkPlugin為了幫助開發者降低數據洩漏的風險,從而更好的保護開發者。
文件明文導出檢測
Android不再允許在app中把file://Uri暴露給其他app,包括但不侷限於通過Intent或ClipData 等方法。
原因在於使用file://Uri會有一些風險,比如:
文件是私有的,接收file://Uri的app無法訪問該文件。
在Android6.0之後引入運行時權限,如果接收file://Uri的app沒有申請READ_EXTERNAL_STORAGE權限,在讀取文件時會引發崩潰。
Olympic中開發了FileUriExposurePlugin,能幫助開發無意間明文請求網絡而導致數據洩漏。能定位到訪問的IP地址,幫助開發快速發現和解決問題。
外跳檢測
外跳檢查是指通過當前進程去喚起二方或者三方APP的問題。對外跳進行監控主要是為了更好的保護用戶,隨著小程序、WEEX和H5等容器化的控件,手機淘寶將手機淘寶的客戶端基礎能力開放出來給商家使用,難免會放生一些不可控的行為,如商家使用jdmobile://、pk206://link/room/984551等不受控的APP。
Olympic中開發了SafeActivityPlugin,能幫助開發無意間外跳行為,更好的保護業務安全。
總結
“性能問題的終結者”是Olympic對自身價值的定位。Olympic從手機淘寶中孕育為生,在手機淘寶中的過去、現在和未來都將發揮重要的價值。Olympic不僅要去發現問題,並針對Olympic發現的問題提供對應的解決方案,真正的去釋放業務開發的時間,為業務賦能。
Olympic具備全面、精準、高效、創新、自動化、開放和賦能開發等特點。用極少的精力去解決性能問題,用更多的時間關注自身業務成長。
▐ 全面
30+個性能專項插件,涉及內存、資源、卡頓、安全等多領域全方位的性能監控,打造手機淘寶的“超嚴格模式”。
▐ 精準高效
細粒度的問題定位(棧級別,View級別,文件名級別)幫助開發者快速定位問題根因。全方位的原理介紹、常見問題排查手冊,經典問題回顧,不費餘力的解決性能問題。
▐ 創新
全新視角,從0-1提供專業的性能監控—大圖檢查、白屏檢查
深入原理,另闢蹊徑,突破系統障礙,強化系統能力—SharePerferences
精益求精,“Geek精神”,做細粒度的性能優化—OverDraw、OverLayout
▐ 自動化
打通TMQ,Aone,EMAS,MTL卡口等流程,自動化定位問題、分發問題、持續集成,釋放開發者時間。
▐ 開放
開放包容的思想,吸收能專項治理某一類性能問題的任何想法。集團內部開源,全方位提升集團APP性能。
▐ 賦能
橫向縱向的性能數據對比,幫助業務更好的決策,判斷新技術的價值。
參考
【1】LeakCanary
鏈接:https://square.github.io/leakcanary/