Android App 性能優化實踐

本文記錄了Android App優化需要用到的工具和以及在實踐中的Tips。也算對我這半年來部分工作的總結。

工具


Hierarchy Viewer 是 Android SDK 自帶的 Layout 嵌套檢查工具,以可視化的佈局角度直觀獲取 Layout 佈局設計和各種屬性信息,來幫助我們完成優化佈局的設計。需要注意的是,出於安全考慮 Hierarchy Viewer 只能連接Android開發版手機(需要安裝ViewServer)或是模擬器

1

注意上圖右半部分顯示的時間

  • Measure: 0.977ms

  • Layout: 0.167ms

  • Draw: 2.717ms

我們知道Android View在繪製圖形的時候主要耗時的操作就在 Measure、Layout 和 Draw 這三個過程;並且任何一個 View 繪製時間不能超過 16.7ms(每秒60幀才能保證流暢度)。

如果 UI 出現卡頓或掉幀,那麼 Hierarchy Viewer 這個工具及其有用,可以分析當前 View 是哪些 View 以及是 View 的哪個過程加載延遲,通過這些信息基本可定位到局部 Code。

如何讓QC快速追蹤和定位性能問題?

當然使用 Android 開發者工具裏的 Profile GPU rendering (GPU呈現模式分析) 工具(Android4.1以上)。它能夠從屏幕上活動的所有Android Activity生成性能視圖,其中綠線代表 16ms,頻繁超過此線的 Activity 就要排查性能問題了。

如何定位到某個方法?

用 Hierarchy Viewer 知道是哪一個子 View 耗時比較多,找到此 View 的Code,那麼如何定位到具體某個方法裏呢? 當然需要 traceview 工具。traceview 工具十分強大,可以輕鬆把每個方法佔用 CPU 時間計算出來,找到佔用時間最長的方法,然後分析此方法即可。

Lint 工具已經集成於 Android Studio,同樣是非常強大的工具。它會給出 Layout 優化提示(既包括圖片資源、layout文件,也有定義的String常量和Color常量以及Layout寫法不規範),告訴你哪些資源沒有被引用,Manifest文件的錯誤等;我主要用 lint 來哪些資源文件沒有被引用到(給APK瘦身),以及部分代碼不規範的地方。

內存優化工具

Memory Monitor:查看整個app所佔用的內存,以及發生GC的時刻,短時間內發生大量的GC操作是一個危險的信號(發生內存抖動)。

Allocation Tracker:追蹤內存的分配。

Heap Tool:查看當前內存快照,便於對比分析哪些對象有可能是泄漏了的。

佈局優化


佈局標籤

<include> 標籤,將佈局中公共部分提取出來共用;例如網易新聞一條新聞的標題欄和評論界面的標題欄。

<viewstub> 標籤,同 include,可引入佈局,但是默認情況引入的佈局不會佔用資源,在解析當前 Layout 時節省計算、內存資源。當需要加載此 View 的時候,需要動態 inflate 起來。

Tips:將一個view設置爲GONE不會被系統解析,從而提高layout解析速度,而VISIBLE和INVISIBLE這兩個可見性屬性會被正常解析。

<merge> 標籤,解決 Layout 嵌套過多的問題,通過工具通過 hierarchy viewer 可直觀的顯示出來。

其他

減少 inflate 次數:inflate 是比較耗資源的,當內存夠用時,可以將 View 緩存起來,下次直接使用;用空間換時間。

ListView 優化,請見我另外一篇博客

關於 Layout 優化,推薦一篇博客,給我很大幫助,性能優化系列

代碼Tips


性能優化之Java(Android)代碼優化,這篇博客詳細介紹瞭如何進行代碼優化,包括緩存、數據存儲、異步、數據庫和網絡等操作的優化。

關於緩存,上文沒有提到一個重要的庫:DiskLruCache;DiskLruCache 是關於數據硬盤緩存的,Android DiskLruCache完全解析,硬盤緩存的最佳方案 這篇博客詳細介紹了 DiskLruCache 使用方法和注意事項。

避免隨意使用靜態變量,當某個對象被定義爲stataic變量所引用,虛擬機通常是不會回收這個對象所佔有的內存。

避免過多過常的創建java對象,JVM 創建和回收耗時,頻繁使用對象,最好創建緩存;每次回收對象,都是 STW(Stop the World),所以如果對象過多,可能引起卡頓(大於16ms,引起掉幀)。可用 Memory Monitor 或 Allocation Tracker 工具來查看這類問題。

多使用局部變量,函數執行完,就釋放內存被虛擬機回收。

使用StringBuilder和StringBuffer進行字符串連接,尤其在做 SQL 拼裝的時候。

單線程應儘量使用HashMap, ArrayList,如果不確定是單線程還是多線程,建議還是用 ConcurrentHashMap...

儘量在finally塊中釋放資源,例如很多 Cursor。

慎用異常,創建一個異常時,需收集一個棧記錄(stack track),用於描述異常是在何處創建的。構建這些此棧時需要爲運行時棧做一份快照,這一部分開銷很大。

View繪製


過度繪製問題

爲什麼會出現過度繪製:多個 View 重疊,複雜 Layout 疊加;導致 GPU 需要繪製多層,有些時候非常耗時。

Android性能優化之過渡繪製,這篇博客作者用實例來解決過度繪製的問題,解決過度繪製問題時,作者也使用了我們上面介紹的幾個工具。

View局部更新

一些複雜的 View,如果每次 View 有局部更新都要重新繪製 View的話,GPU 會顯得力不從心。通過canvas.clipRect() 方法來讓系統識別可繪製區域。這個方法可以指定一塊矩形區域,只有在這個區域內纔會被繪製,其他的區域會被忽視。clipRect方法節約了CPU與GPU資源,不會繪製clipRect區域外的地方,僅僅繪製內容在矩形區域內的組件。

電量優化


儘量減少喚醒屏幕的次數與持續的時間(屏幕是用電大戶),用WakeLock來處理喚醒的問題,能夠正確執行喚醒操作並根據設定及時關閉操作進入睡眠狀態,使用 wakelock.acquice() 方法,一定要加上超時處理(例如釋放鎖)。

等到設備處於充電狀態或者電量充足的時候才進行耗時耗電操作(如分享傳送數據、圖片處理等)

觸發網絡請求的操作,每次都會保持無線信號持續一段時間,我們可以把零散的網絡請求打包進行一次操作,避免過多的無線信號引起的電量消耗(例如APP的數據採集)。

Battery Historian Tool(Android 5.0)這個工具可以詳細查看各類應用的用電情況。

APK 瘦身


代碼瘦身

庫的使用可以極大方便開發者快速開發產品,但也引入了潛在的 bug 以及庫過大導致APK過大的問題。移除沒有用的 dependency libraries 是一個很好的建議。另外適當的給庫瘦身(提取自己想要的功能)也很重要。如果對 APK 代碼非常熟悉可以使用 Proguard (會遍歷你的所有代碼然後找出無用處的代碼)優化。

控制資源文件

剔除沒有用的資源文件(使用 Lint 可輕鬆檢測到)。

資源裏的照片先進行壓縮再使用。合適的時候可以用代碼控制圖片大小作爲不同分辨率屏幕的資源。

爲應用提供 hdpi, xhdpi 和 xxhdpi 這幾個屏幕密度的支持。如果某些設備不是這幾個屏幕密度的,不用擔心,Android 系統會自動使用存在的資源爲設備計算然後提供資源文件。

總結


出現卡頓的根本原因:系統繪製 View 超過 16ms,出現掉幀才導致卡頓或不流暢。解決方法:

  • Hierarchy Viewer,Profile GPU rendering,traceview

  • 抽象佈局標籤,使用標籤 include、viewstub、merge

  • 多使用緩存

  • 儘量避免過度繪製

  • 自定義複雜 View,動態更新 View 內容

  • 正確使用 wakelock,保持 App 用電量

Reference


Android Performance Patterns

Android性能優化之過渡繪製

Android性能優化典範

Performance Tuning On Android


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章