Android APP 性能優化的一些思考(一)
說到 Android 系統手機,大部分人的印象是用了一段時間就變得有點卡頓,有些程序在運行期間莫名其妙的出現崩潰,打開系統文件夾一看,發現多了很多文件,然后用手機管家 APP 不斷地進行清理優化 ,才感覺運行速度稍微提高了點,就算手機在各種性能跑分軟件面前分數遙遙領先,還是感覺無論有多大的內存空間都遠遠不夠用。相信每個使用 Android 系統的用戶都有過以上類似經歷,確實,Android 系統在流暢性方面不如 IOS 系統,為何呢,明明在看手機硬件配置上時,Android 設備都不會輸于 IOS 設備,甚至都強于它,關鍵是在于軟件上。
造成這種現象的原因是多方面的,簡單羅列幾點如下:
- 其實近年來,隨著 Android 版本不斷迭代,Google 提供的Android 系統已經越來越流暢,目前最新發布的版本是 Android 8.0 Oreo 。但是在國內大部分用戶用的 Android 手機系是各大廠商定制過的版本,往往不是最新的原生系統內核,可能絕大多數還停留在 Android 5.0 系統上,甚至 Android 6.0 以上所占比例還偏小,更新存在延遲性。
- 由于 Android 系統源碼是開放的,每個人只要遵從相應的協議,就可以對源碼進行修改,那么國內各個廠商就把基于 Android 源碼改造成自己對外發布的系統,比如我們熟悉的小米手機 Miui 系統、華為手機 EMUI 系統、Oppo 手機 ColorOS 系統等。由于每個廠商都修改過 Android 原生系統源碼,這里面就會引發一個問題,那就是著名的Android 碎片化問題,本質就是不同 Android 系統的應用兼容性不同,達不到一致性。
- 由于存在著各種 Android 碎片化和兼容性問題,導致 Android 開發者在開發應用時需要對不同系統進行適配,同時每個 Android 開發者的開發水平參差不齊,寫出來的應用性能也都存在不同類型的問題,導致用戶在使用過程中用戶體驗感受不同,那么有些問題用戶就會轉化為 Android 系統問題,進而影響對Android 手機的評價。
性能優化
今天想說的重點是Android APP 性能優化,也就是在開發應用程序時應該注意的點有哪些,如何更好地提高用戶體驗。一個好的應用,除了要有吸引人的功能和交互之外,在性能上也應該有高的要求,即時應用非常具有特色,在產品前期可能吸引了部分用戶,但是用戶體驗不好的話,也會給產品帶來不好的口碑。
那么一個好的應用應該如何定義呢?主要有以下三方面:
- 業務/功能
- 符合邏輯的交互
- 優秀的性能
眾所周知,Android 系統作為以移動設備為主的操作系統,硬件配置是有一定的限制的,雖然配置現在越來越高級,但仍然無法與 PC 相比,在 CPU 和內存上使用不合理或者耗費資源多時,就會碰到內存不足導致的穩定性問題、CPU 消耗太多導致的卡頓問題等。
面對問題時,大家想到的都是聯系用戶,然后查看日志,但殊不知有關性能類問題的反饋,原因也非常難找,日志大多用處不大,為何呢?因為性能問題大部分是非必現的問題,問題定位很難復現,而又沒有關鍵的日志,當然就無法找到原因了。這些問題非常影響用戶體驗和功能使用,所以了解一些性能優化的一些解決方案就顯得很重要了,并在實際的項目中優化我們的應用,進而提高用戶體驗。
四個方面
可以把用戶體驗的性能問題主要總結為4個類別:
- 流暢
- 穩定
- 省電、省流量
- 安裝包小
性能問題的主要原因是什么,原因有相同的,也有不同的,但歸根到底,不外乎內存使用、代碼效率、合適的策略邏輯、代碼質量、安裝包體積這一類問題,整理歸類如下:
從圖中可以看到,打造一個高質量的應用應該以4個方向為目標:快、穩、省、小。
- 快:使用時避免出現卡頓,響應速度快,減少用戶等待的時間,滿足用戶期望。
- 穩:減低 crash 率和 ANR 率,不要在用戶使用過程中崩潰和無響應。
- 省:節省流量和耗電,減少用戶使用成本,避免使用時導致手機發燙。
- 小:安裝包小可以降低用戶的安裝成本。
要想達到這4個目標,具體實現是在右邊框里的問題:卡頓、內存使用不合理、代碼質量差、代碼邏輯亂、安裝包過大,這些問題也是在開發過程中碰到最多的問題,在實現業務需求同時,也需要考慮到這點,多花時間去思考,如何避免功能完成后再來做優化,不然的話等功能實現后帶來的維護成本會增加。
一、卡頓優化
Android 應用啟動慢,使用時經常卡頓,是非常影響用戶體驗的,應該盡量避免出現。卡頓的場景有很多,按場景可以分為4類:UI 繪制、應用啟動、頁面跳轉、事件響應,如圖:
這4種卡頓場景的根本原因可以分為兩大類:
• 界面繪制。
主要原因是繪制的層級深、頁面復雜、刷新不合理,由于這些原因導致卡頓的場景更多出現在 UI 和啟動后的初始界面以及跳轉到頁面的繪制上。
• 數據處理。
導致這種卡頓場景的原因是數據處理量太大,一般分為三種情況:
- 數據在處理 UI 線程,
- 數據處理占用 CPU 高,導致主線程拿不到時間片,
- 內存增加導致 GC 頻繁,從而引起卡頓。
引起卡頓的原因很多,但不管怎么樣的原因和場景,最終都是通過設備屏幕上顯示來達到用戶,歸根到底就是顯示有問題,所以,要解決卡頓,就要先了解 Android 系統的顯示原理。
Android系統顯示原理
Android 顯示過程可以簡單概括為:Android 應用程序把經過測量、布局、繪制后的 surface 緩存數據,通過 SurfaceFlinger 把數據渲染到顯示屏幕上, 通過 Android 的刷新機制來刷新數據。也就是說應用層負責繪制,系統層負責渲染,通過進程間通信把應用層需要繪制的數據傳遞到系統層服務,系統層服務通過刷新機制把數據更新到屏幕上。
我們都知道在 Android 的每個 View 繪制中有三個核心步驟:Measure、Layout、Draw。具體實現是從 ViewRootImp 類的performTraversals() 方法開始執行,Measure 和 Layout都是通過遞歸來獲取 View 的大小和位置,并且以深度作為優先級,可以看出層級越深、元素越多、耗時也就越長。
真正把需要顯示的數據渲染到屏幕上,是通過系統級進程中的 SurfaceFlinger 服務來實現的,那么這個SurfaceFlinger 服務主要做了哪些工作呢?如下:
- 響應客戶端事件,創建 Layer 與客戶端的 Surface 建立連接。
- 接收客戶端數據及屬性,修改 Layer 屬性,如尺寸、顏色、透明度等。
- 將創建的 Layer 內容刷新到屏幕上。
- 維持 Layer 的序列,并對 Layer 最終輸出做出裁剪計算。
既然是兩個不同的進程,那么肯定是需要一個跨進程的通信機制來實現數據傳遞,在 Android 顯示系統中,使用了 Android 的匿名共享內存:SharedClient,每一個應用和 SurfaceFlinger 之間都會創建一個SharedClient ,然后在每個 SharedClient 中,最多可以創建 31 個 SharedBufferStack,每個 Surface 都對應一個 SharedBufferStack,也就是一個 Window。
一個 SharedClient 對應一個Android 應用程序,而一個 Android 應用程序可能包含多個窗口,即 Surface 。也就是說 SharedClient 包含的是 SharedBufferStack的集合,其中在顯示刷新機制中用到了雙緩沖和三重緩沖技術。最后總結起來顯示整體流程分為三個模塊:應用層繪制到緩存區,SurfaceFlinger 把緩存區數據渲染到屏幕,由于是不同的進程,所以使用 Android 的匿名共享內存 SharedClient 緩存需要顯示的數據來達到目的。
除此之外,我們還需要一個名詞:FPS。FPS 表示每秒傳遞的幀數。在理想情況下,60 FPS 就感覺不到卡,這意味著每個繪制時長應該在16 ms 以內。但是 Android 系統很有可能無法及時完成那些復雜的頁面渲染操作。Android 系統每隔 16ms 發出 VSYNC 信號,觸發對 UI 進行渲染,如果每次渲染都成功,這樣就能夠達到流暢的畫面所需的 60FPS。如果某個操作花費的時間是 24ms ,系統在得到 VSYNC 信號時就無法正常進行正常渲染,這樣就發生了丟幀現象。那么用戶在 32ms 內看到的會是同一幀畫面,這種現象在執行動畫或滑動列表比較常見,還有可能是你的 Layout 太過復雜,層疊太多的繪制單元,無法在 16ms 完成渲染,最終引起刷新不及時。
卡頓根本原因
根據Android 系統顯示原理可以看到,影響繪制的根本原因有以下兩個方面:
- 繪制任務太重,繪制一幀內容耗時太長。
- 主線程太忙,根據系統傳遞過來的 VSYNC 信號來時還沒準備好數據導致丟幀。
繪制耗時太長,有一些工具可以幫助我們定位問題。主線程太忙則需要注意了,主線程關鍵職責是處理用戶交互,在屏幕上繪制像素,并進行加載顯示相關的數據,所以特別需要避免任何主線程的事情,這樣應用程序才能保持對用戶操作的即時響應。
總結起來,主線程主要做以下幾個方面工作:
- UI 生命周期控制
- 系統事件處理
- 消息處理
- 界面布局
- 界面繪制
- 界面刷新
除此之外,應該盡量避免將其他處理放在主線程中,特別復雜的數據計算和網絡請求等。
性能分析工具
性能問題并不容易復現,也不好定位,但是真的碰到問題還是需要去解決的,那么分析問題和確認問題是否解決,就需要借助相應的的調試工具,比如查看 Layout 層次的 Hierarchy View、Android 系統上帶的 GPU Profile 工具和靜態代碼檢查工具 Lint 等,這些工具對性能優化起到非常重要的作用,所以要熟悉,知道在什么場景用什么工具來分析。
1. Profile GPU Rendering
在手機開發者模式下,有一個卡頓檢測工具叫做:Profile GPU Rendering,如圖:
它的功能特點如下:
- 一個圖形監測工具,能實時反應當前繪制的耗時
- 橫軸表示時間,縱軸表示每一幀的耗時
- 隨著時間推移,從左到右的刷新呈現
- 提供一個標準的耗時,如果高于標準耗時,就表示當前這一幀丟失
2. TraceView
TraceView 是 Android SDK 自帶的工具,用來分析函數調用過程,可以對 Android 的應用程序以及 Framework 層的代碼進行性能分析。它是一個圖形化的工具,最終會產生一個圖表,用于對性能分析進行說明,可以分析到每一個方法的執行時間,其中可以統計出該方法調用次數和遞歸次數,實際時長等參數維度,使用非常直觀,分析性能非常方便。
3. Systrace UI 性能分析
Systrace 是 Android 4.1及以上版本提供的性能數據采樣和分析工具,它是通過系統的角度來返回一些信息。它可以幫助開發者收集 Android 關鍵子系統,如 surfaceflinger、WindowManagerService 等 Framework 部分關鍵模塊、服務、View系統等運行信息,從而幫助開發者更直觀地分析系統瓶頸,改進性能。Systrace 的功能包括跟蹤系統的 I/O 操作、內核工作隊列、CPU 負載等,在 UI 顯示性能分析上提供很好的數據,特別是在動畫播放不流暢、渲染卡等問題上。
優化建議
1. 布局優化
布局是否合理主要影響的是頁面測量時間的多少,我們知道一個頁面的顯示測量和繪制過程都是通過遞歸來完成的,多叉樹遍歷的時間與樹的高度h有關,其時間復雜度 O(h),如果層級太深,每增加一層則會增加更多的頁面顯示時間,所以布局的合理性就顯得很重要。
那布局優化有哪些方法呢,主要通過減少層級、減少測量和繪制時間、提高復用性三個方面入手。
總結如下:
- 減少層級。合理使用 RelativeLayout 和 LinerLayout,合理使用Merge。
- 提高顯示速度。使用 ViewStub,它是一個看不見的、不占布局位置、占用資源非常小的視圖對象。
- 布局復用。可以通過 標簽來提高復用。
- 盡可能少用wrap_content。wrap_content 會增加布局 measure 時計算成本,在已知寬高為固定值時,不用wrap_content 。
- 刪除控件中無用的屬性。
2. 避免過度繪制
過度繪制是指在屏幕上的某個像素在同一幀的時間內被繪制了多次。在多層次重疊的 UI 結構中,如果不可見的 UI 也在做繪制的操作,就會導致某些像素區域被繪制了多次,從而浪費了多余的 CPU 以及 GPU 資源。
如何避免過度繪制呢,如下:
- 布局上的優化。移除 XML 中非必須的背景,移除 Window 默認的背景、按需顯示占位背景圖片
- 自定義View優化。使用 canvas.clipRect()來幫助系統識別那些可見的區域,只有在這個區域內才會被繪制。
3. 啟動優化
通過對啟動速度的監控,發現影響啟動速度的問題所在,優化啟動邏輯,提高應用的啟動速度。啟動主要完成三件事:UI 布局、繪制和數據準備。
因此啟動速度優化就是需要優化這三個過程:
- UI 布局。應用一般都有閃屏頁,優化閃屏頁的 UI 布局,可以通過 Profile GPU Rendering 檢測丟幀情況。
- 啟動加載邏輯優化。可以采用分布加載、異步加載、延期加載策略來提高應用啟動速度。
- 數據準備。數據初始化分析,加載數據可以考慮用線程初始化等策略。
4. 合理的刷新機制
在應用開發過程中,因為數據的變化,需要刷新頁面來展示新的數據,但頻繁刷新會增加資源開銷,并且可能導致卡頓發生,因此,需要一個合理的刷新機制來提高整體的 UI 流暢度。
合理的刷新需要注意以下幾點:
- 盡量減少刷新次數。
- 盡量避免后臺有高的 CPU 線程運行。
- 縮小刷新區域。
5. 其他
在實現動畫效果時,需要根據不同場景選擇合適的動畫框架來實現。有些情況下,可以用硬件加速方式來提供流暢度。
責任編輯:售電衡衡
-
權威發布 | 新能源汽車產業頂層設計落地:鼓勵“光儲充放”,有序推進氫燃料供給體系建設
2020-11-03新能源,汽車,產業,設計 -
中國自主研制的“人造太陽”重力支撐設備正式啟運
2020-09-14核聚變,ITER,核電 -
探索 | 既耗能又可供能的數據中心 打造融合型綜合能源系統
2020-06-16綜合能源服務,新能源消納,能源互聯網
-
新基建助推 數據中心建設將迎爆發期
2020-06-16數據中心,能源互聯網,電力新基建 -
泛在電力物聯網建設下看電網企業數據變現之路
2019-11-12泛在電力物聯網 -
泛在電力物聯網建設典型實踐案例
2019-10-15泛在電力物聯網案例
-
權威發布 | 新能源汽車產業頂層設計落地:鼓勵“光儲充放”,有序推進氫燃料供給體系建設
2020-11-03新能源,汽車,產業,設計 -
中國自主研制的“人造太陽”重力支撐設備正式啟運
2020-09-14核聚變,ITER,核電 -
能源革命和電改政策紅利將長期助力儲能行業發展
-
探索 | 既耗能又可供能的數據中心 打造融合型綜合能源系統
2020-06-16綜合能源服務,新能源消納,能源互聯網 -
5G新基建助力智能電網發展
2020-06-125G,智能電網,配電網 -
從智能電網到智能城市