阿里3輪面試都問了RecyclerView

a1322674015發表於2020-09-23

阿里面試總共4輪,其中有3輪面試都問到了RecyclerView的問題。面試的點各不相同,有原理、巢狀問題、有快取實現,但是最終都是殊途同歸,所有的問題都彙集在,如何對RecyclerView做效能優化?

1.1 RecyclerView第一次layout時,會發生預佈局pre-layout嗎?

​ 第一次佈局時,並不會觸發pre-layout。pre-layout只會在每次notify change時才會被觸發,目的是通過saveOldPosition方法將螢幕中各位置上的ViewHolder的座標記錄下來,並在重新佈局之後,通過對比實現Item的動畫效果。比如以下效果:

1.2 如果自定義LayoutManager需要注意什麼?

在RecyclerView的dispatchLayoutStep1階段,會呼叫自定義LayoutManager的 supportsPredictiveItemAnimations 方法判斷在某些狀態下是否展示predictive animation。以下LinearLayoutManager的實現:

 

@Override
public boolean supportsPredictiveItemAnimations() {
return mPendingSavedState == null && mLastStackFromEnd == mStackFromEnd;
}

​ 如果 supportsPredictiveItemAnimations 返回true,則LayoutManager中複寫onLayoutChildren方法會被呼叫2次:一次是在pre-layout,另一次是real-layout。

因為會有pre-layout和real-layout,所有在自定義LayoutManager中,需要根據RecyclerView.State中的isPreLayout方法的返回值,在這兩次佈局中做區分。比如LinearLayoutManager中的onLayoutChildren中有如下判斷:

上面程式碼中有一段註釋:

if the child is visible and we are going to move it around, we should layout extra items in the opposite direction to make sure new items animate nicely instead of just fading in

​ 代表的意思就是如果當前正在update的item是可見狀態,則需要在pre-layout階段額外填充一個item,目的是為了保證處於不可見狀態的item可以平滑的滑動到螢幕內。

1.3 舉例說明

​ 比如下圖中點選item2將其刪除,呼叫notifyItemRemoved後,在pre-layout之前item5並沒有被新增到RecyclerView中,而經過pre-layout之後,item5經過佈局會被填充到RecyclerView中

當item移出螢幕之後,item5會隨同item3和item4一起向上移動,如下圖所示:

​如果自定義LayoutManager並沒有實現pre-layout,或者實現不合理,則當item2移出螢幕時,只會將item3和item4進行平滑移動,而item5只是單純的appear到螢幕中,如下所示:

可以看出item5並沒有同item3和item4一起平滑滾動到螢幕內,這樣介面上顯示會給使用者卡頓的感覺。

1.4 ViewHolder何時被快取到RecycledViewPool中?

主要有以下2種情況:

  1. 當ItemView被滑動出螢幕時,並且CachedView已滿,則ViewHolder會被快取到RecycledViewPool中
  2. 當資料發生變動時,執行完disappearrance的ViewHolder會被快取到RecycledViewPool中

1.5 CachedView和RecycledViewPool的關係

​ 當一個ItemView被滑動滾出螢幕之後,預設會先被儲存在CachedView中。CachedView的預設大小為2,可以通過 setItemViewCacheSize 方法修改它的值。當CachedView已滿後,後續有新的ItemView從螢幕內滑出時,會迫使CachedView根據FIFO規則,將之前的快取的ViewHolder轉移到RecycledViewPool中,效果可以參考下圖:

RecycledViewPool預設大小為5,可以通過以下方式修改RecycledViewPool的快取大小:

RecyclerView.getRecycledViewPool().setMaxRecycledViews(int viewType, int max);

1.6 CachedView和RecycledViewPool兩者區別

快取到CachedView中的ViewHolder並不會清理相關資訊(比如position、state等),因此剛移出螢幕的ViewHolder,再次被移回螢幕時,只要從CachedView中查詢並顯示即可,不需要重新繫結(bindViewHolder)。

而快取到RecycledViewPool中的ViewHolder會被清理狀態和位置資訊,因此從RecycledViewPool查詢到ViewHolder,需要重新呼叫bindViewHolder繫結資料。

1.7 你是從哪些方面優化RecyclerView的?

我總結了幾點,主要可以從以下幾個方面對RecyclerView進行優化:

儘量將複雜的資料處理操作放到非同步中完成。RecyclerView需要展示的資料經常是從遠端伺服器上請求獲取,但是在網路請求拿到資料之後,需要將資料做扁平化操作,儘量將最優質的資料格式返回給UI執行緒。

優化RecyclerView的佈局,避免將其與ConstraintLayout使用

針對快速滑動事件,可以使用addOnScrollListener新增對快速滑動的監聽,當使用者快速滑動時,停止載入資料操作。

如果ItemView的高度固定,可以使用setHasFixSize(true)。這樣RecyclerView在onMeasure階段可以直接計算出高度,不需要多次計運算元ItemView的高度,這種情況對於垂直RecyclerView中巢狀橫向RecyclerView效果非常顯著。

當UI是Tab feed流時,可以考慮使用RecycledViewPool來實現多個RecyclerView的快取共享。



作者:Android進階架構
連結:https://www.jianshu.com/p/eabb00c500ef
來源:簡書
著作權歸作者所有。商業轉載請聯絡作者獲得授權,非商業轉載請註明出處。

相關文章