RecyclerViewPrefetch功能探究
在Android的開發中,滾動列表是一個出鏡率非常高的元件。這其中RecyclerView是當然不讓的明星。從Native到weex,RecyclerView有著非常廣泛的使用。正因如此,我們持續不斷地針對RecyclerView頁面進行優化。在最近一次調研RecyclerView優化的過程中,偶然看到google在最新版版本的RecyclerView中增加了Prefetch功能。Prefetch的功能到底做了哪些事情,是不是真的能提升頁面效能,使用的成本有多高?
針對這些問題,本文將分三步來做一個簡單的探究:
- (1)功能怎麼用
- (2)效果怎麼樣
- (3)原理是什麼
1. Prefetch功能的使用
在研究Prefetch功能的實際效果之前,首先需要能正常使用Prefetch功能。
- 第一步 :升級 Support Library的版本到 25.3.1。
google官方在 Support Library v25 版本中,為RecyclerView增加了Prefetch。
並且在 v25.1.0 以及25.3.0版本中進行了完善。在最新的穩定版本25.3.1中已經基本穩定。
如果需要使用建議升級到25.3.1版本。詳細可見support-library change log
##### 注意: android support v4 包從24.2.0版本開始拆分成了多個更小的模組。
包括:support-compat,support-core-utils,support-core-ui,support-media-compat以及
support-fragment。如果出現使用
compile group: `com.android.support`, name: `support-v4`,
version: `25.3.1`, ext: `jar`
出現了類找不到的情況,需要按照需要依賴上述子包。
-
第二步:實現LayoutManager
- 如果你使用的是官方的LayoutManager,那麼直接可以獲取Prefetch的功能,無需任何其他的定製。
- 但是如果你使用的是自定義的LayoutManager,需要重寫LayoutManager.collectAdjacentPrefetchPositions()
方法。如果巢狀RecyclerView使用需要複寫setInitialPrefetchItemCount
詳細資訊可以參見具體的API文件(點選直接跳轉)
- 第三步:設定開啟Prefetch
預設情況下Prefetch的功能是開啟的。當然也可以手動選擇關閉:
LayoutManager.setItemPrefetchEnabled(false);
所以確認一下不要手動關閉預取功能
在完成上述三個步驟以後,可以像往常一樣使用RecyclerView,並且自帶了prefetch功能。
2.Prefetch實際效果
RecyclerView的Prefetch功能正常運轉以後。那麼這個功能到底能帶來多少效能的收益是現在需要確認的。分幾個部分來看:
demo
- demo內容:
使用Android官方提供的StaggeredGridLayoutManager實現了一個瀑布流。
瀑布流中文字的數量和圖片的大小都是可以變的。展示如下:
衡量標準&方法:
-
GPU呈現模式分析 開啟的GPU渲染條形圖。圖中的綠線是流暢度的分割線。超過綠線的幀都是渲染超時的。比較的核心指標是滑動過程中,超出綠線frame佔比。
(注:流暢的標準是1s 60幀 一幀的渲染時間大約是16ms)
- 測試裝置小米5s
- 資料繫結複雜度模擬:
為了檢測預取功能在不同的頁面渲染複雜度情況下的實際效果。在RecyclerView資料繫結函式:onBindViewHolder函式中,使用
Thread.sleep(time);
來模擬頁面渲染的複雜度。複雜度的大小,通過time時間的長短來體現。時間越長,複雜度越高。
原始demo流暢度對比
開啟prefetch | 關閉prefetch |
---|---|
在原始demo中,由於複雜度有限,無論是開啟或者關閉prefetch功能,滑動過程中無明顯差異。
延時8ms 流暢度對比
開啟prefetch | 關閉prefetch |
---|---|
通過延時8ms,整個資料繫結的複雜度急劇上升。在關閉prefetch功能的情況下,在16ms 內無法完成的渲染的幀數明顯增多。相比之下開啟prefetch以後,頁面依然非常流暢。
延時12ms 流暢度對比
開啟prefetch | 關閉prefetch |
---|---|
通過延時12ms在關閉prefetch功能的情況下,在16ms 內無法完成的渲染的幀數進一步增多,頁面卡頓愈加明顯。但是開啟prefetch以後,頁面依然非常流暢。
延時17ms 流暢度對比
開啟prefetch | 關閉prefetch |
---|---|
通過延時17ms,在16ms時間內完成渲染已經無法做到。滑動過程中,滑動卡頓可以明顯被感知。在關閉prefetch功能的情況下,在16ms內無法完成的渲染的幀數進一步增多,並且延時也更長。開啟prefetch以後,同樣不可避免的會出現16ms無法渲染幀,但是頁面對比下還是更加流暢。
結論:基於上述實驗,可以得出以下幾個結論:
- 使用prefetch以後,recyclerView的流暢度對頁面複雜度的敏感性降低。
- 在高複雜度(recyclerView的子項view的建立和繫結更加耗時)情況下,prefetch功能確實能顯著提高頁面流暢度。
3.Prefetch原理:
android 5.0以後,android系統為了提高UI渲染的效率引入了RenderThread。通過這樣的設計,將主執行緒從UI
渲染的繁重工作中解脫出來。在UI渲染過程中,主執行緒可以更加專注於跟使用者進行互動。這樣可以大大提高頁面流暢度。
通過Systrace工具跟蹤一個頁面,獲取頁面渲染詳細資料。
注意:如果使用DDMS來抓取。一定要記得在Enable Application Traces from 中選擇APP所在的程式。否則你可能無法觀察到Prefetch的渲染。如下圖所示:
一個常規頁面的渲染流程如下:
通過上面的展示,UI執行緒準備頁面資料並且在ready以後交由Render執行緒渲染。幀與幀之間通過幀同步訊號sync來同步。如果頁面渲染無法在規定時間內完成,就會出現丟幀情況渲染的時序會變成如下情況:
不難發現在正常渲染過程中,有一個非常的明顯的特點:在UI執行緒將頁面資料交由Render
執行緒渲染以後,會出現大量的空閒時間。如下圖所示:
這些空閒等待時間就被浪費了。Prefetch的核心思想就是利用這部分空閒時間來預先處理item的建立和資料繫結[2]。
對比一下使用了Prefetch以後的渲染時序圖如下:
時空上的複用,會大大提高頁面渲染的效率,提高頁面流暢度。
4. 收益
在實際開發的過程中,任何的升級都需要考量投入產出的比例。Prefetch優化影響的頁面包括:
- (1)Native頁面中使用RecyclerView的頁面
- (2)weex頁面中使用list的頁面
注意: 只有Android 5.0以後版本的手機才能獲得這個優化。
通過簡單的升級(可能需要解決一些相容問題),就可以使幾乎所有的RecyclerView頁面效能從中受益。並且在sourceCode層面不需要做額外的定製(自定義和巢狀除外)。投入產出比還是非常可觀的,所以
建議大家儘快升級吧!
5. 引用
- [1]Android應用程式UI硬體加速渲染的動畫執行過程分析
- [2]RecyclerView Prefetch 注意
- [3]Profile GPU Rendering Walkthrou
- [4]Android Support Library
- [5]Systrace
相關文章
- 利用Postman和Chrome的開發者功能探究專案PostmanChrome
- 深入探究 Golang 反射:功能與原理及應用Golang反射
- MDK中One ELF Section per Function選項功能探究Function
- WiFiAp探究實錄--功能實現與原始碼分析WiFi原始碼
- 探究 canvas 繪圖中撤銷(undo)功能的實現方式Canvas繪圖
- Android中探究抖音短視訊的動態桌布功能以及擴充功能使用Android
- synchronized探究synchronized
- webAR 探究Web
- JAVA 探究NIOJava
- iOS Block探究iOSBloC
- 探究Java集合Java
- iOS RunLoop 探究iOSOOP
- RAC原理探究
- Flutter BuildContext 探究FlutterUIContext
- iOS 深入探究 AutoreleasePooliOS
- Mach-O 探究Mac
- 探究Spring原理Spring
- context包探究Context
- cherrypy應用探究
- exchange partition原理探究
- Oracle深入Undo探究Oracle
- 深度探究MMO社交對話系統(二):聊天系統結構設計和功能邏輯
- 物件導向再探究物件
- 探究Flutter Engine除錯Flutter除錯
- RunLoop底層原理探究OOP
- vuex持久化方案探究Vue持久化
- Go unsafe 包探究Go
- 探究 Kotlin 類代理Kotlin
- iOS - Block探究系列一iOSBloC
- MySQL複合索引探究MySql索引
- Flutter Dart mixins 探究FlutterDart
- zookeeper使用和原理探究
- 責任鏈模式探究模式
- 深入探究Object.definePropertyObject
- WebSocket協議深入探究Web協議
- HttpContext探究之RequestServicesHTTPContext
- .NET Core Session原始碼探究Session原始碼
- .NET Core HttpClient原始碼探究HTTPclient原始碼