RecycleView的復(fù)用和優(yōu)化

最近了解了一下RecycleView的緩存機(jī)制芽腾,做了一些記錄往弓,防止遺忘

一疏唾、RecyleView四級(jí)緩存

image

首先明確RecyecleView中緩存的對(duì)象是ViewHolder.

Recycler負(fù)責(zé)管理和緩存所有的ViewHolder。
RecycleView的緩存從上到下分為四層:scrap函似、cache槐脏、ViewCacheExtension、RecycleViewPool

1.1撇寞、 scrap

scrap 用來(lái)緩存正在顯示的ViewHolder顿天。

scrap 分為兩個(gè)集合:mAttachedScrap 和 mChangedScrap。

  • mAttachedScrap 用于緩存正在顯示的ViewHolder
  • mChangedScrap 用于緩存屏幕上發(fā)生變化的viewHolder,可能是數(shù)據(jù)發(fā)生了變化蔑担,也可能是ViewHolder類(lèi)型發(fā)生了變化牌废,mChangedScrap 中的ViewHolder會(huì)被移動(dòng)到RecycledViewPool中。

當(dāng)我們調(diào)用 notifyItemRangeChanged 方法的時(shí)候钟沛,會(huì)觸發(fā) requestLayout 方法畔规,就會(huì)重新布局,重新布局的話恨统,就會(huì)先將 viewHolder 放到 scrap 中(屏幕上變化的放入mChangedScrap 中叁扫,其余的放入mAttachedScrap 中)三妈,然后 fill 布局的時(shí)候,再?gòu)?mAttachedScrap 里面取出來(lái)直接使用莫绣。mChangedScrap 中的 viewHolder 會(huì)被移動(dòng)到 RecycledViewPool 中畴蒲,所以 mChangedScrap 對(duì)應(yīng)的 item 需要從 pool 中取對(duì)應(yīng)的 viewHolder,然后重新綁定对室。

image

View中的detach和remove

  • detach 在ViewGroup中的實(shí)現(xiàn)很簡(jiǎn)單模燥,只是將當(dāng)前View從ParentView的ChildView數(shù)組中移除,將當(dāng)前View的mParent設(shè)置為null, 可以理解為輕量級(jí)的臨時(shí)remove掩宜。
  • remove 代表真正的移除蔫骂,不光從ChildView數(shù)組中移除,其他和View樹(shù)各項(xiàng)聯(lián)系也會(huì)被徹底斬?cái)唷?/li>

Recycled View中的Scrap View

  • Scrap View指的是在RecyclerView中牺汤,經(jīng)歷了detach操作的緩存辽旋。此類(lèi)緩存是通過(guò)position匹配的,不需要重新bindView檐迟。
  • Recycled View指代的就是真正的移除操作remove后的緩存补胚,取出時(shí)需重新bindView使用。

1.2追迟、cached

數(shù)據(jù)結(jié)構(gòu)mCachedViews溶其,用于緩存從屏幕中移除,但是可能很快被再次顯示的ViewHolder

  • 它是一個(gè) ArrayList 類(lèi)型,不區(qū)分 viewHolder 的類(lèi)型
  • mCachedViews大小限制為2敦间,但是你可以使用 setItemViewCacheSize()這個(gè)方法調(diào)整它的大小瓶逃。

1.3、ViewCacheExtension

這個(gè)是需要自定義的每瞒,而且使用有很大的限制金闽,所以不深入介紹了。

1.4剿骨、RecycledViewPool

RecycledViewPool 儲(chǔ)存各個(gè)類(lèi)型的 viewHolder 它緩存的是被恢復(fù)出廠設(shè)置的viewHolder,需要重新調(diào)用bind 綁定數(shù)據(jù)代芜。

  • RecycledViewPool 是按照ItemViewType 存儲(chǔ)ViewHolder的,每種ItemViewType最大數(shù)量為5
  • 可以通過(guò) setMaxRecycledViews() 方法來(lái)設(shè)置每個(gè)類(lèi)型儲(chǔ)存的容量浓利。
  • 針對(duì)RecycleView嵌套的場(chǎng)景挤庇,如一個(gè)縱向的RecycleView 嵌套橫向的RecycleView ,可以使用 setRecycledViewPool() 方法贷掖,公用RecycledViewPool

RecycleView滑出屏幕時(shí)的ViewHolder的復(fù)用過(guò)程

image

滾出屏幕的View會(huì)優(yōu)先保存到mCacheViews, 如果mCacheViews中保存滿了嫡秕,就會(huì)保存到RecyclerViewPool中。

  • 檢查mCacheViews集合中是否還有空位苹威,如果有空位昆咽,則直接放到mCacheViews集合
  • 如果沒(méi)有的話就把mCacheViews集合中最前面的ViewHolder拿出來(lái)放到RecyclerViewPool中,然后再把最新的這個(gè)ViewHolder放到mCacheViews集合
  • 如果沒(méi)有成功緩存到mCacheViews集合中,就直接放到RecyclerViewPool

二掷酗、Recycler 緩存加載流程

image
  • scrap負(fù)責(zé)緩存屏幕中正在顯示的ViewHolder调违,命中緩存后直接使用,不需要create和Bind
  • 如果在 cache (mCachedViews)負(fù)責(zé)緩存剛剛移出屏幕泻轰,很可能被復(fù)用的ViewHolder技肩。通過(guò)position獲取,命中后不需create和bind
  • ViewCacheExtension google預(yù)留的一個(gè)空的緩存,暫不討論
  • pool (RecycledViewPool )根據(jù)ViewType緩存ViewHolder浮声, 用于緩存數(shù)據(jù)解綁后的ViewHolder虚婿,pool中命中的viewHolder 需要進(jìn)行重新bind 進(jìn)行數(shù)據(jù)綁定
  • 如果所有緩存中都沒(méi)有命中 viewHolder,會(huì)重新調(diào)用createViewHolder 和 bindViewHolder

三泳挥、一些優(yōu)化方法

3.1然痊、 setHastFixedSize

當(dāng)知道Adapter內(nèi)Item的改變不會(huì)影響RecyclerView寬高的時(shí)候,可以設(shè)置為true讓RecyclerView避免重新計(jì)算大小羡洁。

注意兩點(diǎn):

  • 當(dāng)調(diào)用Adapter的增刪改插方法玷过,最后就會(huì)根據(jù)mHasFixedSize這個(gè)值來(lái)判斷需要不需要requestLayout();
onItemRangeChanged(),

onItemRangeInserted(),

onItemRangeRemoved(),

onItemRangeMoved()

上面四個(gè)方法會(huì)調(diào)用triggerUpdateProcessor方法

    void triggerUpdateProcessor() {
            if (POST_UPDATES_ON_ANIMATION && mHasFixedSize && mIsAttached) {
                ViewCompat.postOnAnimation(RecyclerView.this, mUpdateChildViewsRunnable);
            } else {
                mAdapterUpdateDuringMeasure = true;
                requestLayout();
            }
        }

會(huì)根據(jù)mHasFixedSize這個(gè)值來(lái)判斷需要不需要requestLayout()筑煮;

  • 當(dāng)調(diào)用Adapter的notifyDataSetChanged() 最后調(diào)用了onChanged,調(diào)用了requestLayout()粤蝎,會(huì)去重新測(cè)量寬高真仲。
   public void onChanged() {
            assertNotInLayoutOrScroll(null);
            mState.mStructureChanged = true;

            processDataSetCompletelyChanged(true);
            if (!mAdapterHelper.hasPendingUpdates()) {
                requestLayout();
            }
        }

3.2、setHasStableIds

Adapter.setHasStablesId(true)開(kāi)啟固定ID

DemoAdapter  mAdapter=new DemoAdapter();
mAdapter.setHasStablesId(true);

在 Adapter 類(lèi)中重寫(xiě)getItemId來(lái)給每個(gè) Item 一個(gè)唯一的ID初澎。

@Override
public long getItemId(int position){
    return items.get(position).getId();
}

setHasStableIds(true)之后秸应,數(shù)據(jù)為發(fā)生變化情況下,滾動(dòng)recycleView

ViewHolder會(huì)被緩存到mAttachedScrap中,復(fù)用時(shí)通過(guò)position 從mAttachedScrap直接取出顯示碑宴,不需要重新createViewHolder软啼、bindViewHolder

用空間換時(shí)間,
從而規(guī)避滑動(dòng)recyelveView過(guò)程中出現(xiàn)的閃爍問(wèn)題。

3.3延柠、recycleView 圖片列表快速刷新

recyleView 中顯示圖片列表,快速滑動(dòng)容易出現(xiàn)卡頓祸挪。一個(gè)優(yōu)化思路,可以設(shè)置在滑動(dòng)過(guò)程中暫停正在加載的圖片,滑動(dòng)停止之后再恢復(fù)圖片的加載贞间。

recyceView?.addOnScrollListener(object : RecyclerView.OnScrollListener() {
            override fun onScrollStateChanged(recyclerView: RecyclerView, newState: Int) {
                super.onScrollStateChanged(recyclerView, newState)
                 if (newState == RecyclerView.SCROLL_STATE_IDLE) {
                    Glide.with(requireActivity()).resumeRequests()
                } else {
                    Glide.with(requireActivity()).pauseRequests()
                }
            }
        })

參考文章:

http://www.reibang.com/p/1d2213f303fc
https://blog.csdn.net/weixin_43130724/article/details/90068112
http://www.reibang.com/p/4a2b18135447

https://zhuanlan.zhihu.com/p/80475040

http://www.reibang.com/p/aeb9ccf6a5a4

圖片閃爍問(wèn)題分析:
http://www.reibang.com/p/29352def27e6

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末贿条,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子增热,更是在濱河造成了極大的恐慌整以,老刑警劉巖,帶你破解...
    沈念sama閱讀 219,427評(píng)論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件峻仇,死亡現(xiàn)場(chǎng)離奇詭異公黑,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,551評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門(mén)凡蚜,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)奠骄,“玉大人,你說(shuō)我怎么就攤上這事番刊『郏” “怎么了?”我有些...
    開(kāi)封第一講書(shū)人閱讀 165,747評(píng)論 0 356
  • 文/不壞的土叔 我叫張陵芹务,是天一觀的道長(zhǎng)蝉绷。 經(jīng)常有香客問(wèn)我,道長(zhǎng)枣抱,這世上最難降的妖魔是什么熔吗? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 58,939評(píng)論 1 295
  • 正文 為了忘掉前任,我火速辦了婚禮佳晶,結(jié)果婚禮上桅狠,老公的妹妹穿的比我還像新娘。我一直安慰自己轿秧,他們只是感情好中跌,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,955評(píng)論 6 392
  • 文/花漫 我一把揭開(kāi)白布。 她就那樣靜靜地躺著菇篡,像睡著了一般漩符。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上驱还,一...
    開(kāi)封第一講書(shū)人閱讀 51,737評(píng)論 1 305
  • 那天嗜暴,我揣著相機(jī)與錄音,去河邊找鬼议蟆。 笑死闷沥,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的咐容。 我是一名探鬼主播舆逃,決...
    沈念sama閱讀 40,448評(píng)論 3 420
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼疟丙!你這毒婦竟也來(lái)了颖侄?” 一聲冷哼從身側(cè)響起,我...
    開(kāi)封第一講書(shū)人閱讀 39,352評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤享郊,失蹤者是張志新(化名)和其女友劉穎览祖,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體炊琉,經(jīng)...
    沈念sama閱讀 45,834評(píng)論 1 317
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡展蒂,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,992評(píng)論 3 338
  • 正文 我和宋清朗相戀三年又活,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片锰悼。...
    茶點(diǎn)故事閱讀 40,133評(píng)論 1 351
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡柳骄,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出箕般,到底是詐尸還是另有隱情耐薯,我是刑警寧澤,帶...
    沈念sama閱讀 35,815評(píng)論 5 346
  • 正文 年R本政府宣布丝里,位于F島的核電站曲初,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏杯聚。R本人自食惡果不足惜臼婆,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,477評(píng)論 3 331
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望幌绍。 院中可真熱鬧颁褂,春花似錦、人聲如沸傀广。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 32,022評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)主儡。三九已至奖唯,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間糜值,已是汗流浹背。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 33,147評(píng)論 1 272
  • 我被黑心中介騙來(lái)泰國(guó)打工坯墨, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留寂汇,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 48,398評(píng)論 3 373
  • 正文 我出身青樓捣染,卻偏偏與公主長(zhǎng)得像骄瓣,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子耍攘,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,077評(píng)論 2 355

推薦閱讀更多精彩內(nèi)容