前沿
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ǔ)膝迎。
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
里初、RecycledViewPool
和ViewCacheExtension
啃勉。
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è)置比驻。
位置固定
內(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)格有多少行就緩存多少個。
- 四級緩存:返回布局有效禽翼,內(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 长酗。
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