Android App 性能優化實踐
本文記錄了Android App優化需要用到的工具和以及實踐中的Tips。也算對我這半年來部分工作的總結。
</blockquote>工具
Hierarchy Viewer 是 Android SDK 自帶的 Layout 嵌套檢查工具,以可視化的布局角度直觀獲取 Layout 布局設計和各種屬性信息,來幫助我們完成優化布局的設計。需要注意的是,出于安全考慮 Hierarchy Viewer 只能連接Android開發版手機(需要安裝ViewServer)或是模擬器。
![]()
注意上圖右半部分顯示的時間
- Measure: 0.977ms
- Layout: 0.167ms
- Draw: 2.717ms
</ul>我們知道Android View在繪制圖形的時候主要耗時的操作就在 Measure、Layout 和 Draw 這三個過程;并且任何一個 View 繪制時間不能超過 16.7ms(每秒60幀才能保證流暢度)。
如果 UI 出現卡頓或掉幀,那么 Hierarchy Viewer 這個工具及其有用,可以分析當前 View 是哪些 View 以及是 View 的哪個過程加載延遲,通過這些信息基本可定位到局部 Code。
如何讓QC快速追蹤和定位性能問題?
當然使用 Android 開發者工具里的 Profile GPU rendering 工具(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這兩個可見性屬性會被正常解析。
</blockquote><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)這個工具可以詳細查看各類應用的用電情況。
總結
出現卡頓的根本原因:系統繪制 View 超過 16ms,出現掉幀才導致卡頓或不流暢。解決方法:
- Hierarchy Viewer,Profile GPU rendering,traceview
- 抽象布局標簽,使用標簽 include、viewstub、merge
- 多使用緩存
- 盡量避免過度繪制
- 自定義復雜 View,動態更新 View 內容
- 正確使用 wakelock,保持 App 用電量
</ul>Reference
來自:http://stackvoid.com/performance-tuning-on-android/