Android應用效能優化指南
對我們技術從業者而言,很多時候時候不是我們不知道怎麼做,而是不知道做什麼?今天系統的總結自己關於如何對Android應用進行優化的一些經驗,共計八個維度.

image.png
1.佈局優化
為什麼?
Android系統每個16ms發出VSYNC訊號,觸發對UI的渲染,要想達到介面流暢,必須實現60fps,也就意味著大多數的操作必須在16ms完成.
除了上面介面過於複雜導致渲染不能及時完成之外,還存在過度繪製問題.所謂過度繪製就是某個畫素在同一幀的時間內被繪製多次.在多層次的UI介面中,如果不可見的UI也在進行繪製,那麼這些重合區域的畫素就會被繪製多次,從而浪費大量的CPU和GPU資源.過度繪製也發生在背景重疊的情況下,比如Layout中有自己的背景,同時子View中又有自己的背景.
如何檢測?
- 使用HierarchyViewer來查詢Activity中的佈局是否過於複雜
- 在開發者選項中開啟Show GPU Overdraw選項進行觀察是否存在過度繪製
- 在開發者選項中選擇Profile GPU Rendering,選中On screen as bar
- 使用TraceView來觀察CPU執行情況
如何優化?
- 減少佈局的層級,合理的使用include,merge,ViewStub
- 自定義元件的onDraw()中避免大量建立臨時物件,比如String,以免頻繁觸發GC
- 自定義元件的onDraw()中,考慮使用canvas.clipRect()繪製需要被繪製的區域
- 對像ListView這樣的元件容器,考慮使用convertView,使用ViewHolder,
- 考慮使用效能更高的元件,比如推薦使用RecycleView來代替ListView,使用staticlayout來實現自動換行
2.記憶體優化
為什麼?
資源總是有限的,記憶體同樣也是一種資源.在Android當中,過度的/不恰當佔用記憶體資源,會導致應用頻繁被殺死,最終也會影響使用者的整體體驗.任何一名開發者,都應該將節省記憶體牢記心中.
如何檢測?
- 使用LeakCanary
- 使用MAT分析Java堆
- 使用Android Device Monitor中的Application Tracker追蹤記憶體分配資訊
- Android Studio中的Android Monitor,選擇其中的Memory
如何優化?
-
主動的釋放記憶體,在onLowMemory()和onTrimMemory()中適當的釋放記憶體
-
避免記憶體洩漏和記憶體溢位
-
在使用Bitmap的時候,考慮對其進行壓縮,使用快取或者改變顏色模式,比如android預設的顏色格式是ARGB_8888,在要求不高的情況下可以採用RGB__565,這樣每個畫素1佔用的記憶體就可懂4byte到2byte.
-
減少幀動畫的使用,如果需要,通過SurfaceView/">SurfaceView實現
-
使用更輕量級的資料結構,比如ArrayMap/SparseArray
-
合理的使用相關元件,比如Service和Webview,在不需要的時候主動結束其生命週期
-
合理的使用多程序,比如像音樂播放器類,可以分為主程序和播放程序
-
使用非同步佇列時考慮有界佇列
-
如果你能明確知道HashMap的大小,那就再初始化時為其制定容量
3.電量優化
為什麼?
電量是移動裝置非常寶貴的資源,作為一名開發者,有必要為使用者著想,減少電量的消耗.調查顯示通常只有30%左右的電量是被程式核心的功能所消耗,比如介面渲染,剩下的70%則是被上報資料,位置更新,後臺通知所消耗.
注‘Android技術交流群752016839,歡迎大家加入交流,暢談!本群有免費學習資料視訊’
如何檢測?
- 手機選項中通過檢視APP的電量消耗的統計資料
- 使用Battery Historian Tool來檢視詳細的電量消耗
如何優化?
- 減少喚醒螢幕的次數與持續的時間,正確的使用WakeLock.
- 延遲非必須的操作到充電狀態時,比如日誌上報完全可以在夜間充電時完成,這點可以結合JobScheduler使用
- 使用感測器採集資料時,一旦不需要記得取消註冊.
- 減少網路通訊,合併通訊.
- 合理使用定位功能,減少位置更新頻率以及根據實際情況使用不同精度的定位需求
4.網路優化
為什麼?
現在App幾乎都需要聯網操作,做好網路優化一方面可以提高體驗,另一方面可以減少流量和電量的損耗.另外,無論是對使用者還是網路服務提供者,網路同樣是一種資源,任何開發者都不應該假設網路資源是無限制的.
如何檢測?
- 使用Android Studio裡的Network Traffic Tools來檢視網路請求
- 使用Android Studio中的Monitor
- 使用Fidder或者Charles等抓包工具分析網路資料包
如何優化?
- 有必要的時候務必做好快取,無論是圖片還是普通的資料,使用LruCache和DiskLruCache構建自己的快取系統,並根據實際場景設計快取策略
- 避免過度的網路同步,合併相關的網路請求
- 根據實際場景確定請求策略,避免使用固定的間隔頻率來進行網路操作.比如連線WiFi並充電的情況下請求頻率可以高,第一次網路請求失敗後可以使用雙倍的時間間隔來進行下一次
- 減少資料傳輸量,對傳輸的資料做壓縮.如果傳輸的是圖片,需要選擇合適的圖片格式以及根據顯示大小請求合適規格的圖片.對於普通資料,可以考慮使用ProtocalBuffers來減小傳輸資料的大小.
- 某些情況下可以採用IP直連,一方面可以減少DNS解析時間,另一方面可以防止域名劫持
5.啟動優化
為什麼?
啟動優化看起來並不是那麼必要,但從心理學角度而言,越快的啟動速度往往給使用者以效能好,高效可靠的心理暗示,這就很容易讓使用者對其產生好感,為你後面打動使用者留下了餘地.
如何檢測?
- 使用Method Tracing
- 使用Systrace,比如在onCreate中新增trace.beginSection()和trace.endSection()
- 使用adb shell am start -W [packageName]/[packageName.MainActivity]測量冷啟動時間
如何優化?
- Activity的onCreate()中減少複雜和耗時的操作
- Application的onCreate(),attachBaseContext()中同樣減少複雜和耗時的操作,但是對於很多App在此處會執行大量元件和服務的初始化操作,如果可能考慮並行初始化
- 提供自定義啟動視窗,比如將一張圖片通過設定主題的方式顯示為啟動視窗.
- 優化佈局
6.體積優化
為什麼?
對使用者而言,無論是使用者空間還是網路,亦或是時間,都是資源.體積優化就是為使用者節省資源的重要一環.如果你現在做的是SDK類產品,那麼體積優化同樣重要.
注‘Android技術交流群752016839,歡迎大家加入交流,暢談!本群有免費學習資料視訊’
如何檢測?
- 使用Android Lint檢查沒有使用的資源
如何優化?
- 減少不必要的依賴庫/Jar,在滿足需求的前提下優先選擇體積小的.
- 使用Proguard工具進行程式碼瘦身,優化,混淆
- 減少so檔案的數量,根據實際情況提供so檔案
- 使用Gradle中的shrinkResource來將無用的程式碼和資源排除在APK安裝包之外
- 減少圖片資源的大小,考慮圖片壓縮或者使用Vertor Drawable替代png/jpeg
- 有選擇的提供對應解析度的圖片資源
- 複用已經存在的圖片,多用通過程式碼對已有圖片進行變換的方式實現複用
- 使用外掛化技術(如果專案簡單就不要使用)
7.效能優化
能發揮出100%的能力就不要只發揮其中的50%,這對應用而言並非壞事.同樣的價格賣給使用者兩輛車,我想大多數人會選擇效能更好的.
如何檢測?
- 使用Lint執行靜態分析,在Android Studio的Analysis->Inspect Code
- 在開發者選項中開啟StrictMode或者在程式碼中開啟
- 程式碼Review
如何優化?
- 任務並行化,對可能的任務進行並行操作,多借助執行緒池而非直接使用執行緒
- 如何需要序列化資料,優先考慮Android自身提供的而非Java提供的Serializable
- 選擇合適的資料結構,明確List/Set/Map/Stack操作的複雜度
- 使用Android提供更高效的容器,比如使用ArrayMap來代替HashMap,此外還是有-
- SparseBoolMap,SparseIntMap,SparseLongMap
- 使用靜態常量代替Enum型別,可以減少至少兩倍的記憶體消耗
- 使用物件池技術,比如提供想String一樣的物件池
- 使用快取技術
- 字串拼接操作有限使用StringBuilder
- 對相關的演算法和邏輯進行優化,減少不必要的流程
- 採用JNI,對計算量較大的邏輯將其協程so檔案,如圖片處理
業務優化
除了上述比較通用的優化方案之外,也應該花點時間放在業務優化上.很多時候,迫於時間壓迫,當前實現業務的方案並非最優.比如為了支援多張圖片上傳,很多人直接使用序列操作,儘管這樣做容易實現,但是卻並非最佳.
由於每個產品的業務並不相同,也就很難有通用的優化方案,這裡又兩個目標值得思考:
- 如果有可能,序列業務並行化
- 如果有可能,簡化業務流程.將一大象關進冰箱的方法就是開啟冰箱,將大象放進去,最後關閉冰箱.
之所以把業務優化放在最後的根本原因是業務優化的風險較高,需要團隊的整體配合來完成.
1. Android中圖片有四種顏色格式,分別是

image.png
預設的是ARGB_8888,其中ARGB分別代表的是透明度,紅色,綠色,藍色,每個值分別用8位來記錄,也就是一個畫素會佔用4byte,共32位.
而ARGB_4444和以上很類似,但是每個值分別用4位來記錄,也就是一個畫素會佔用2byte,共16位.
RGB_565則分別用5位,6位,5位來記錄每個值,不存在透明度,每個畫素會佔用2byte,共16位.
ALPHA_8:該畫素只儲存透明度,會佔用1byte,共8位.
在實際應用中而言,值推薦使用ARGB_8888以及RGB_565,如果你不需要透明度,那麼就選擇RGB_565,可以減少一半的記憶體佔用.