天天看點

Android App 性能優化實踐

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

Hierarchy Viewer 是 Android SDK 自帶的 Layout 嵌套檢查工具,以可視化的布局角度直覺擷取 Layout 布局設計和各種屬性資訊,來幫助我們完成優化布局的設計。需要注意的是,出于安全考慮 Hierarchy Viewer 隻能連接配接Android開發版手機(需要安裝ViewServer)或是模拟器。

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

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:檢視目前記憶體快照,便于對比分析哪些對象有可能是洩漏了的。

<code>&lt;include&gt;</code> 标簽,将布局中公共部分提取出來共用;例如網易新聞一條新聞的标題欄和評論界面的标題欄。

<code>&lt;viewstub&gt;</code> 标簽,同 include,可引入布局,但是預設情況引入的布局不會占用資源,在解析目前 Layout 時節省計算、記憶體資源。當需要加載此 View 的時候,需要動态 inflate 起來。

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

<code>&lt;merge&gt;</code> 标簽,解決 Layout 嵌套過多的問題,通過工具通過 hierarchy viewer 可直覺的顯示出來。

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

ListView 優化,請見我另外一篇部落格。

關于 Layout 優化,推薦一篇部落格,給我很大幫助,性能優化系列。

性能優化之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 重疊,複雜 Layout 疊加;導緻 GPU 需要繪制多層,有些時候非常耗時。

Android性能優化之過渡繪制,這篇部落格作者用執行個體來解決過度繪制的問題,解決過度繪制問題時,作者也使用了我們上面介紹的幾個工具。

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

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

等到裝置處于充電狀态或者電量充足的時候才進行耗時耗電操作(如分享傳送資料、圖檔處理等)

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

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

庫的使用可以極大友善開發者快速開發産品,但也引入了潛在的 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 用電量

Android Performance Patterns

Android性能優化之過渡繪制

Android性能優化典範

Performance Tuning On Android