RecyclerView緩存原理及優(yōu)化方向

前沿

Android新增的Recyclerview主要用于代替ListView。Recyclerview可擴(kuò)展性強(qiáng)粉私。

  • 可以通過LayoutManager形成線性(橫向與豎向)壁畸、網(wǎng)格、瀑布流布局。
  • 通過OnItemTouchListener監(jiān)聽 Item 的事件碌尔,雖然比ListView.OnItemClickListener麻煩了點(diǎn),但是可以實(shí)現(xiàn)更復(fù)雜的功能券敌,比如item滑動唾戚。
  • 提供了notifyItemInserted、notifyItemRemoved待诅、notifyItemChanged颈走、notifyItemMoved來提高局部刷新的效率。
  • 沒有ListView那種 HeaderView 和 FooterView , 但可以通過來 getItemViewType來生成不同的視圖咱士。
  • RecyclerView還定義了ViewHolder立由,配合RecyclerView.Adapter轧钓,封裝重用ItemView的邏輯,還有四級緩存锐膜,效率大大增加毕箍。

1. RecyclerView緩存機(jī)制與性能優(yōu)化關(guān)系

RecyclerView做性能優(yōu)化要說復(fù)雜也復(fù)雜,比如說布局優(yōu)化道盏,緩存而柑,預(yù)加載等等。

其優(yōu)化的點(diǎn)很多荷逞,在這些看似獨(dú)立的點(diǎn)之間媒咳,其實(shí)存在一個樞紐:Adapter。

因為所有的ViewHolder的創(chuàng)建和內(nèi)容的綁定都需要經(jīng)過Adaper的兩個函數(shù)onCreateViewHolder和onBindViewHolder种远。

因此我們性能優(yōu)化的本質(zhì)就是要**減少這兩個函數(shù)的調(diào)用時間和調(diào)用的次數(shù)**涩澡。

如果我們想對RecyclerView做性能優(yōu)化,必須清楚的了解到我們的每一步操作背后坠敷,onCreateViewHolder和onBindViewHolder調(diào)用了多少次妙同。

因此,了解RecyclerView的緩存機(jī)制是RecyclerView性能優(yōu)化的基礎(chǔ)膝迎。


1655326cbdee046f.png

2. 繪制原理簡述

2.1 假設(shè)

為了簡化問題粥帚,繪制原理介紹提供以下假設(shè):

  • RecyclerView
    • 以LinearLayoutManager為例
    • 忽略ItemDecoration
    • 忽略ItemAnimator
    • 忽略Measure過程
    • 假設(shè)RecyclerView的width和height是確定的
  • Recycler
    • 忽略mViewCacheExtension

2.2 繪制過程

(1)類的職責(zé)介紹
LayoutManager:接管RecyclerView的Measure,Layout限次,Draw的過程

Recycler:緩存池

Adapter:ViewHolder的生成器和內(nèi)容綁定器芒涡。

(2)繪制過程簡介

  • 1、RecyclerView.requestLayout開始發(fā)生繪制卖漫,忽略Measure的過程
  • 2拖陆、在Layout的過程會通過LayoutManager.fill去將RecyclerView填滿
  • 3、LayoutManager.fill會調(diào)用LayoutManager.layoutChunk去生成一個具體的ViewHolder
  • 4懊亡、然后LayoutManager就會調(diào)用Recycler.getViewForPosition向Recycler去要ViewHolder
  • 5依啰、Recycler首先去一級緩存(Cache)里面查找是否命中,如果命中直接返回店枣。如果一級緩存沒有找到速警,則去三級緩存查找,如果三級緩存找到了則調(diào)用Adapter.bindViewHolder來綁定內(nèi)容鸯两,然后返回闷旧。如果三級緩存沒有找到,那么就通過Adapter.createViewHolder創(chuàng)建一個ViewHolder钧唐,然后調(diào)用Adapter.bindViewHolder綁定其內(nèi)容忙灼,然后返回為Recycler。
  • 6、一直重復(fù)步驟3-5该园,知道創(chuàng)建的ViewHolder填滿了整個RecyclerView為止酸舍。


    1655326cbddd7d9e.png

3. 緩存機(jī)制

3.1 Recyclerview的緩存類

RecyclerView緩存基本上是通過三個內(nèi)部類管理的,Recycler里初、RecycledViewPoolViewCacheExtension啃勉。
Recycler
用于管理已經(jīng)廢棄或者與RecyclerView分離的ViewHolder,為了方便理解這個類双妨,整理了下面的資料民宿,內(nèi)部類的成員變量和他們的含義:

變量 作用
mChangedScrap 與RecyclerView分離的ViewHolder列表
mAttachedScrap 未與RecyclerView分離的ViewHolder列表
mCachedViews ViewHolder緩存列表
mViewCacheExtension 開發(fā)者可以控制的ViewHolder緩存的幫助類
mRecyclerPool ViewHolder緩存池

RecycledViewPool
RecycledViewPool類是用來緩存Item用儒陨,是一個ViewHolder的緩存池璧函,如果多個RecyclerView之間用setRecycledViewPool(RecycledViewPool)設(shè)置同一個RecycledViewPool回论,他們就可以共享Item。
其實(shí)RecycledViewPool的內(nèi)部維護(hù)了一個Map挑随,里面以不同的viewType為Key存儲了各自對應(yīng)的ViewHolder集合状您。可以通過提供的方法來修改內(nèi)部緩存的Viewholder镀裤。
ViewCacheExtension
開發(fā)者可自定義的一層緩存竞阐,是虛擬類ViewCacheExtension的一個實(shí)例缴饭,開發(fā)者可實(shí)現(xiàn)方法getViewForPositionAndType(Recycler recycler, int position, int type)來實(shí)現(xiàn)自己的緩存暑劝。

3.2 Recyclerview的四級緩存

3.2.1 屏幕內(nèi)緩存

屏幕內(nèi)緩存指在屏幕中顯示的ViewHolder,這些ViewHolder會緩存在mAttachedScrap颗搂、mChangedScrap中 :

  • mChangedScrap 表示數(shù)據(jù)已經(jīng)改變的ewHolder列表
    表示數(shù)據(jù)已經(jīng)改變的viewHolder列表,存儲 notifXXX 方法時需要改變的 ViewHolder,匹配機(jī)制按照position和id進(jìn)行匹配
  • mAttachedScrap 未與RecyclerView分離的ViewHolder列表
    未與RecyclerView分離的ViewHolder列表,如果仍依賴于 RecyclerView (比如已經(jīng)滑動出可視范圍担猛,但還沒有被移除掉),但已經(jīng)被標(biāo)記移除的 ItemView 集合會被添加到 mAttachedScrap 中丢氢。

3.2.2 屏幕外緩存

當(dāng)列表滑動出了屏幕時傅联,ViewHolder會被緩存在 mCachedViews ,其大小由mViewCacheMax決定疚察,默認(rèn)DEFAULT_CACHE_SIZE為2蒸走,可通過Recyclerview.setItemViewCacheSize()動態(tài)設(shè)置。

3.2.3 自定義緩存

可以自己實(shí)現(xiàn)ViewCacheExtension類實(shí)現(xiàn)自定義緩存貌嫡,可通過Recyclerview.setViewCacheExtension()設(shè)置比驻。

  • 適用場景:android.jlelse.eu/anatomy-of-…

  • 位置固定

  • 內(nèi)容不變

  • 數(shù)量有限

  • 三級緩存:返回View

    • 按照position和type進(jìn)行匹配
    • 直接返回View
    • 需要自己繼承ViewCacheExtension實(shí)現(xiàn)
    • 位置固定,內(nèi)容不發(fā)生改變的情況岛抄,比如說Header如果內(nèi)容固定别惦,就可以使用

3.2.4 緩存池

ViewHolder在首先會緩存在 mCachedViews 中,當(dāng)超過了個數(shù)(比如默認(rèn)為2)夫椭, 就會添加到 RecycledViewPool 中掸掸。
在有限的mCachedViews中如果存不下ViewHolder時,就會把ViewHolder存入RecyclerViewPool中。
* 按照Type來查找ViewHolder
* 每個Type默認(rèn)最多緩存5個

RecycledViewPool 會根據(jù)每個ViewType把ViewHolder分別存儲在不同的列表中扰付,每個ViewType最多緩存DEFAULT_MAX_SCRAP = 5 個ViewHolder堤撵,如果RecycledViewPool沒有被多個RecycledView共享,對于線性布局悯周,每個ViewType最多只有一個緩存粒督,如果是網(wǎng)格有多少行就緩存多少個。


1155837-c10b3fce7c974aee.png
  • 四級緩存:返回布局有效禽翼,內(nèi)容無效的ViewHolder
    • 按照type進(jìn)行匹配屠橄,每個type緩存值默認(rèn)=5
    • layout是有效的,但是內(nèi)容是無效的
    • 多個RecycleView可共享,可用于多個RecyclerView的優(yōu)化

3.3 緩存策略

Recyclerview在獲取ViewHolder時按四級緩存的順序查找闰挡,如果沒找到就創(chuàng)建锐墙。其中只有RecycledViewPool找到時才會調(diào)用 bindViewHolder,其它緩存不會重新bindViewHolder 长酗。

1155837-e5365d4a8d217428.png

3.4 總結(jié)

通過了解RecyclerView的四級緩存溪北,我們可以知道,RecyclerView最多可以緩存 N(屏幕最多可顯示的item數(shù)) + 2 (屏幕外的緩存) + 5*M (M代表M個ViewType夺脾,緩存池的緩存)之拨,只有RecycledViewPool找到時才會重新調(diào)用 bindViewHolder。

RecyclerView在Recyler里面實(shí)現(xiàn)ViewHolder的緩存咧叭,Recycler里面的實(shí)現(xiàn)緩存的主要包含以下5個對象:

  • ArrayList mAttachedScrap:

    • 按照id和position來查找ViewHolder
  • ArrayList mChangedScrap:

  • ArrayList mCachedViews:緩存ViewHolder蚀乔,主要用于解決RecyclerView滑動抖動時的情況,還有用于保存Prefetch的ViewHoder

    • 最大的數(shù)量為:mViewCacheMax = mRequestedCacheMax + extraCache(extraCache是由prefetch的時候計算出來的)
  • ViewCacheExtension mViewCacheExtension:開發(fā)者可自定義的一層緩存菲茬,是虛擬類ViewCacheExtension的一個實(shí)例吉挣,開發(fā)者可實(shí)現(xiàn)方法getViewForPositionAndType(Recycler recycler, int position, int type)來實(shí)現(xiàn)自己的緩存。

  • mRecyclerPool ViewHolder緩存池婉弹,

3.2 緩存機(jī)制圖解

RecyclerView在設(shè)計的時候講上述5個緩存對象分為了3級睬魂。
每次創(chuàng)建ViewHolder的時候,會按照優(yōu)先級依次查詢緩存創(chuàng)建ViewHolder镀赌。
三級緩存分別是:

  • 一級緩存:返回布局和內(nèi)容都都有效的ViewHolder
    • 按照position或者id進(jìn)行匹配
    • 命中一級緩存無需onCreateViewHolder和onBindViewHolder
    • mAttachScrap在adapter.notifyXxx的時候用到
    • mChanedScarp在每次View繪制的時候用到氯哮,因為getViewHolderForPosition非調(diào)用多次,后面將
    • mCachedView:用來解決滑動抖動的情況商佛,默認(rèn)值為2

3. RecyclerView性能優(yōu)化方向總結(jié)

1655326cbe18dc01.png

參考

RecyclerView緩存原理喉钢,有圖有真相
關(guān)于Recyclerview的緩存機(jī)制的理解

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市威彰,隨后出現(xiàn)的幾起案子出牧,更是在濱河造成了極大的恐慌,老刑警劉巖歇盼,帶你破解...
    沈念sama閱讀 218,755評論 6 507
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件舔痕,死亡現(xiàn)場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)伯复,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,305評論 3 395
  • 文/潘曉璐 我一進(jìn)店門慨代,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人啸如,你說我怎么就攤上這事侍匙。” “怎么了叮雳?”我有些...
    開封第一講書人閱讀 165,138評論 0 355
  • 文/不壞的土叔 我叫張陵想暗,是天一觀的道長。 經(jīng)常有香客問我帘不,道長说莫,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,791評論 1 295
  • 正文 為了忘掉前任寞焙,我火速辦了婚禮储狭,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘捣郊。我一直安慰自己辽狈,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,794評論 6 392
  • 文/花漫 我一把揭開白布呛牲。 她就那樣靜靜地躺著刮萌,像睡著了一般。 火紅的嫁衣襯著肌膚如雪侈净。 梳的紋絲不亂的頭發(fā)上尊勿,一...
    開封第一講書人閱讀 51,631評論 1 305
  • 那天僧凤,我揣著相機(jī)與錄音畜侦,去河邊找鬼。 笑死躯保,一個胖子當(dāng)著我的面吹牛旋膳,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播途事,決...
    沈念sama閱讀 40,362評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼验懊,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了尸变?” 一聲冷哼從身側(cè)響起义图,我...
    開封第一講書人閱讀 39,264評論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎召烂,沒想到半個月后碱工,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,724評論 1 315
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,900評論 3 336
  • 正文 我和宋清朗相戀三年怕篷,在試婚紗的時候發(fā)現(xiàn)自己被綠了历筝。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 40,040評論 1 350
  • 序言:一個原本活蹦亂跳的男人離奇死亡廊谓,死狀恐怖梳猪,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情蒸痹,我是刑警寧澤春弥,帶...
    沈念sama閱讀 35,742評論 5 346
  • 正文 年R本政府宣布,位于F島的核電站叠荠,受9級特大地震影響惕稻,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜蝙叛,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,364評論 3 330
  • 文/蒙蒙 一俺祠、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧借帘,春花似錦蜘渣、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,944評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至际起,卻和暖如春拾碌,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背街望。 一陣腳步聲響...
    開封第一講書人閱讀 33,060評論 1 270
  • 我被黑心中介騙來泰國打工校翔, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人灾前。 一個月前我還...
    沈念sama閱讀 48,247評論 3 371
  • 正文 我出身青樓防症,卻偏偏與公主長得像,于是被迫代替她去往敵國和親哎甲。 傳聞我的和親對象是個殘疾皇子蔫敲,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,979評論 2 355