RecyclerViewPrefetch功能探究

bkaq發表於2017-04-28

在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`

出現了類找不到的情況,需要按照需要依賴上述子包。

詳細資訊可以參見具體的API文件(點選直接跳轉)

  • 第三步:設定開啟Prefetch

預設情況下Prefetch的功能是開啟的。當然也可以手動選擇關閉:

LayoutManager.setItemPrefetchEnabled(false);

所以確認一下不要手動關閉預取功能

在完成上述三個步驟以後,可以像往常一樣使用RecyclerView,並且自帶了prefetch功能。

2.Prefetch實際效果

RecyclerView的Prefetch功能正常運轉以後。那麼這個功能到底能帶來多少效能的收益是現在需要確認的。分幾個部分來看:

demo
  • demo內容:
    使用Android官方提供的StaggeredGridLayoutManager實現了一個瀑布流。

瀑布流中文字的數量和圖片的大小都是可以變的。展示如下:

TB1CTI3QFXXXXahXXXXXXXXXXXX-640-960.gif

衡量標準&方法:
  • 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無法渲染幀,但是頁面對比下還是更加流暢。

結論:基於上述實驗,可以得出以下幾個結論:
  1. 使用prefetch以後,recyclerView的流暢度對頁面複雜度的敏感性降低。
  2. 在高複雜度(recyclerView的子項view的建立和繫結更加耗時)情況下,prefetch功能確實能顯著提高頁面流暢度。

3.Prefetch原理:

android 5.0以後,android系統為了提高UI渲染的效率引入了RenderThread。通過這樣的設計,將主執行緒從UI
渲染的繁重工作中解脫出來。在UI渲染過程中,主執行緒可以更加專注於跟使用者進行互動。這樣可以大大提高頁面流暢度。
通過Systrace工具跟蹤一個頁面,獲取頁面渲染詳細資料。

  注意:如果使用DDMS來抓取。一定要記得在Enable Application Traces from 中選擇APP所在的程式。否則你可能無法觀察到Prefetch的渲染。如下圖所示:

TB18ZINQFXXXXaBXpXXXXXXXXXX-525-619.png

一個常規頁面的渲染流程如下:

通過上面的展示,UI執行緒準備頁面資料並且在ready以後交由Render執行緒渲染。幀與幀之間通過幀同步訊號sync來同步。如果頁面渲染無法在規定時間內完成,就會出現丟幀情況渲染的時序會變成如下情況:

不難發現在正常渲染過程中,有一個非常的明顯的特點:在UI執行緒將頁面資料交由Render
執行緒渲染以後,會出現大量的空閒時間。如下圖所示:

這些空閒等待時間就被浪費了。Prefetch的核心思想就是利用這部分空閒時間來預先處理item的建立和資料繫結[2]。

對比一下使用了Prefetch以後的渲染時序圖如下:

時空上的複用,會大大提高頁面渲染的效率,提高頁面流暢度。

4. 收益

在實際開發的過程中,任何的升級都需要考量投入產出的比例。Prefetch優化影響的頁面包括:

  • (1)Native頁面中使用RecyclerView的頁面
  • (2)weex頁面中使用list的頁面

注意: 只有Android 5.0以後版本的手機才能獲得這個優化。

通過簡單的升級(可能需要解決一些相容問題),就可以使幾乎所有的RecyclerView頁面效能從中受益。並且在sourceCode層面不需要做額外的定製(自定義和巢狀除外)。投入產出比還是非常可觀的,所以

建議大家儘快升級吧!

5. 引用


相關文章