再也不用擔(dān)心面試問RecyclerView了

嗨,大家好红淡,最近去淘了一些關(guān)于RecyclerView的面試真題外莲,大家一起看看吧猪半,這次的問題如果都弄懂了,下次面試再遇到RecyclerView應(yīng)該就沒啥可擔(dān)心的了。

  • 講一下RecyclerView的緩存機(jī)制,滑動10個磨确,再滑回去沽甥,會有幾個執(zhí)行onBindView。緩存的是什么俐填?cachedView會執(zhí)行onBindView嗎?
  • RecyclerView預(yù)取機(jī)制
  • 如何實(shí)現(xiàn)RecyclerView的局部更新安接,用過payload嗎,notifyItemChange方法中的參數(shù)?
  • RecyclerView嵌套RecyclerView滑動沖突英融,NestScrollView嵌套RecyclerView。
  • 說說RecyclerView性能優(yōu)化歇式。

講一下RecyclerView的緩存機(jī)制,滑動10個驶悟,再滑回去,會有幾個執(zhí)行onBindView材失。緩存的是什么痕鳍?cachedView會執(zhí)行onBindView嗎?

RecyclerView預(yù)取機(jī)制

這兩個問題都是關(guān)于緩存的,我就一起說了龙巨。

1)首先說下RecyclerView的緩存結(jié)構(gòu):

Recyclerview有四級緩存笼呆,分別是mAttachedScrap(屏幕內(nèi)),mCacheViews(屏幕外)旨别,mViewCacheExtension(自定義緩存)诗赌,mRecyclerPool(緩存池)

  • mAttachedScrap(屏幕內(nèi)),用于屏幕內(nèi)itemview快速重用秸弛,不需要重新createView和bindView
  • mCacheViews(屏幕外)铭若,保存最近移出屏幕的ViewHolder,包含數(shù)據(jù)和position信息递览,復(fù)用時必須是相同位置的ViewHolder才能復(fù)用叼屠,應(yīng)用場景在那些需要來回滑動的列表中,當(dāng)往回滑動時绞铃,能直接復(fù)用ViewHolder數(shù)據(jù)镜雨,不需要重新bindView。
  • mViewCacheExtension(自定義緩存)儿捧,不直接使用荚坞,需要用戶自定義實(shí)現(xiàn),默認(rèn)不實(shí)現(xiàn)纯命。
  • mRecyclerPool(緩存池)西剥,當(dāng)cacheView滿了后或者adapter被更換,將cacheView中移出的ViewHolder放到Pool中亿汞,放之前會把ViewHolder數(shù)據(jù)清除掉瞭空,所以復(fù)用時需要重新bindView。

2)四級緩存按照順序需要依次讀取。所以完整緩存流程是:

  1. 保存緩存流程:
  • 插入或是刪除itemView時咆畏,先把屏幕內(nèi)的ViewHolder保存至AttachedScrap
  • 滑動屏幕的時候南捂,先消失的itemview會保存到CacheView,CacheView大小默認(rèn)是2旧找,超過數(shù)量的話按照先入先出原則溺健,移出頭部的itemview保存到RecyclerPool緩存池(如果有自定義緩存就會保存到自定義緩存里),RecyclerPool緩存池會按照itemview的itemtype進(jìn)行保存钮蛛,每個itemType緩存?zhèn)€數(shù)為5個鞭缭,超過就會被回收。
  1. 獲取緩存流程:
  • AttachedScrap中獲取魏颓,通過pos匹配holder——>獲取失敗岭辣,從CacheView中獲取,也是通過pos獲取holder緩存
    ——>獲取失敗甸饱,從自定義緩存中獲取緩存——>獲取失敗沦童,從mRecyclerPool中獲取
    ——>獲取失敗,重新創(chuàng)建viewholder——createViewHolder并bindview叹话。

3)了解了緩存結(jié)構(gòu)和緩存流程偷遗,我們再來看看具體的問題
滑動10個,再滑回去驼壶,會有幾個執(zhí)行onBindView氏豌?

  • 由之前的緩存結(jié)構(gòu)可知,需要重新執(zhí)行onBindView的只有一種緩存區(qū)辅柴,就是緩存池mRecyclerPool箩溃。

所以我們假設(shè)從加載RecyclView開始盤的話(頁面假設(shè)可以容納7條數(shù)據(jù)):

  • 首先,7條數(shù)據(jù)會依次調(diào)用onCreateViewHolderonBindViewHolder碌嘀。
  • 往下滑一條(position=7)涣旨,那么會把position=0的數(shù)據(jù)放到mCacheViews中。此時mCacheViews緩存區(qū)數(shù)量為1股冗,mRecyclerPool數(shù)量為0霹陡。然后新出現(xiàn)的position=7的數(shù)據(jù)通過postion在mCacheViews中找不到對應(yīng)的ViewHolder,通過itemtype也在mRecyclerPool中找不到對應(yīng)的數(shù)據(jù)止状,所以會調(diào)用onCreateViewHolderonBindViewHolder方法烹棉。
  • 再往下滑一條數(shù)據(jù)(position=8),如上怯疤。
  • 再往下滑一條數(shù)據(jù)(position=9)浆洗,position=2的數(shù)據(jù)會放到mCacheViews中,但是由于mCacheViews緩存區(qū)默認(rèn)容量為2集峦,所以position=0的數(shù)據(jù)會被清空數(shù)據(jù)然后放到mRecyclerPool緩存池中伏社。而新出現(xiàn)的position=9數(shù)據(jù)由于在mRecyclerPool中還是找不到相應(yīng)type的ViewHolder抠刺,所以還是會走onCreateViewHolderonBindViewHolder方法。所以此時mCacheViews緩存區(qū)數(shù)量為2摘昌,mRecyclerPool數(shù)量為1速妖。
  • 再往下滑一條數(shù)據(jù)(position=10),這時候由于可以在mRecyclerPool中找到相同viewtype的ViewHolder了聪黎。所以就直接復(fù)用了罕容,并調(diào)用onBindViewHolder方法綁定數(shù)據(jù)。
  • 后面依次類推稿饰,剛消失的兩條數(shù)據(jù)會被放到mCacheViews中锦秒,再出現(xiàn)的時候是不會調(diào)用onBindViewHolder方法,而復(fù)用的第三條數(shù)據(jù)是從mRecyclerPool中取得湘纵,就會調(diào)用onBindViewHolder方法了脂崔。

4)所以這個問題就得出結(jié)論了(假設(shè)mCacheViews容量為默認(rèn)值2):

  • 如果一開始滑動的是新數(shù)據(jù),那么滑動10個梧喷,就會走10個bindview方法。然后滑回去脖咐,會走10-2個bindview方法铺敌。一共18次調(diào)用。

  • 如果一開始滑動的是老數(shù)據(jù)屁擅,那么滑動10-2個偿凭,就會走8個bindview方法。然后滑回去派歌,會走10-2個bindview方法弯囊。一共16次調(diào)用。

但是但是胶果,實(shí)際情況又有點(diǎn)不一樣匾嘱。因?yàn)?code>Recyclerview在v25版本引入了一個新的機(jī)制,預(yù)取機(jī)制早抠。

預(yù)取機(jī)制霎烙,就是在滑動過程中,會把將要展示的一個元素提前緩存到mCachedViews中蕊连,所以滑動10個元素的時候悬垃,第11個元素也會被創(chuàng)建,也就多走了一次bindview方法甘苍。但是滑回去的時候不影響尝蠕,因?yàn)榫退闾崆叭×艘粋€緩存數(shù)據(jù),只是把bindview方法提前了载庭,并不影響總的綁定item數(shù)量看彼。

所以滑動的是新數(shù)據(jù)的情況下就會多一次調(diào)用bindview方法廊佩。

5)總結(jié),問題怎么答呢闲昭?

  • 四級緩存和流程說一下罐寨。
  • 滑動10個,再滑回去序矩,bindview可以是19次調(diào)用鸯绿,可以是16次調(diào)用。
  • 緩存的其實(shí)就是緩存item的view簸淀,在Recyclerview中就是viewholder瓶蝴。
  • cachedView就是mCacheViews緩存區(qū)中的view,是不需要重新綁定數(shù)據(jù)的租幕。

如何實(shí)現(xiàn)RecyclerView的局部更新舷手,用過payload嗎,notifyItemChange方法中的參數(shù)?

關(guān)于RecyclerView的數(shù)據(jù)更新劲绪,主要有以下幾個方法:

  • notifyDataSetChanged()男窟,刷新全部可見的item。
    *notifyItemChanged(int)贾富,刷新指定item歉眷。
  • notifyItemRangeChanged(int,int),從指定位置開始刷新指定個item颤枪。
  • notifyItemInserted(int)汗捡、notifyItemMoved(int)、notifyItemRemoved(int)畏纲。插入扇住、移動一個并自動刷新。
  • notifyItemChanged(int, Object)盗胀,局部刷新艘蹋。

可以看到,關(guān)于view的局部刷新就是notifyItemChanged(int, Object)方法读整,下面具體說說:

notifyItemChange有兩個構(gòu)造方法:

  • notifyItemChanged(int position, @Nullable Object payload)
  • notifyItemChanged(int position)

其中payload參數(shù)可以認(rèn)為是你要刷新的一個標(biāo)示簿训,比如我有時候只想刷新itemView中的textview,有時候只想刷新imageview?又或者我只想某一個view的文字顏色進(jìn)行高亮設(shè)置米间?那么我就可以通過payload參數(shù)來標(biāo)示這個特殊的需求了强品。

具體怎么做呢?比如我調(diào)用了notifyItemChanged(14,"changeColor"),那么在onBindViewHolder回調(diào)方法中做下判斷即可:

    @Override
    public void onBindViewHolder(ViewHolderholder, int position, List<Object> payloads) {
        if (payloads.isEmpty()) {
            // payloads為空屈糊,說明是更新整個ViewHolder
            onBindViewHolder(holder, position);
        } else {
            // payloads不為空的榛,這只更新需要更新的View即可。
            String payload = payloads.get(0).toString();
            if ("changeColor".equals(payload)) {
                holder.textView.setTextColor("");
            }
        }
    }

RecyclerView嵌套RecyclerView滑動沖突逻锐,NestScrollView嵌套RecyclerView夫晌。

1)RecyclerView嵌套RecyclerView的情況下雕薪,如果兩者都要上下滑動,那么就會引起滑動沖突晓淀。默認(rèn)情況下外層的RecyclerView可滑所袁,內(nèi)層不可滑。

之前說過解決滑動沖突的辦法有兩種:內(nèi)部攔截法和外部攔截法凶掰。
這里我提供一種內(nèi)部攔截法燥爷,還有一些其他的辦法大家可以自己思考下。

   holder.recyclerView.setOnTouchListener { v, event ->
            when(event.action){
                //當(dāng)按下操作的時候懦窘,就通知父view不要攔截前翎,拿起操作就設(shè)置可以攔截,正常走父view的滑動畅涂。
                MotionEvent.ACTION_DOWN,MotionEvent.ACTION_MOVE -> v.parent.requestDisallowInterceptTouchEvent(true)
                MotionEvent.ACTION_UP -> v.parent.requestDisallowInterceptTouchEvent(false)
            }
            false}

2)關(guān)于ScrclerView的滑動沖突還是同樣的解決辦法港华,就是進(jìn)行事件攔截。
還有一個辦法就是用Nestedscrollview代替ScrollView午衰,Nestedscrollview是官方為了解決滑動沖突問題而設(shè)計的新的View立宜。它的定義就是支持嵌套滑動的ScrollView。

所以直接替換成Nestedscrollview就能保證兩者都能正畴叮滑動了赘理。但是要注意設(shè)置RecyclerView.setNestedScrollingEnabled(false)這個方法,用來取消RecyclerView本身的滑動效果扇单。

這是因?yàn)镽ecyclerView默認(rèn)是setNestedScrollingEnabled(true),這個方法的含義是支持嵌套滾動的奠旺。也就是說當(dāng)它嵌套在NestedScrollView中時,默認(rèn)會隨著NestedScrollView滾動而滾動,放棄了自己的滾動蜘澜。所以給我們的感覺就是滯留、卡頓响疚。所以我們將它設(shè)置為false就解決了卡頓問題鄙信,讓他正常的滑動,不受外部影響装诡。

說說RecyclerView性能優(yōu)化鸦采。

  • bindViewHolder方法是在UI線程進(jìn)行的渔伯,此方法不能耗時操作锣吼,不然將會影響滑動流暢性玄叠。比如進(jìn)行日期的格式化读恃。
  • 對于新增或刪除的時候狐粱,可以使用diffutil進(jìn)行局部刷新肌蜻,少用全局刷新
  • 對于itemVIew進(jìn)行布局優(yōu)化蒋搜,比如少嵌套等育谬。
  • 25.1.0 (>=21)及以上使用Prefetch 功能膛檀,也就是預(yù)取功能咖刃,嵌套時且使用的是LinearLayoutManager嚎杨,子RecyclerView可通過setInitialPrefatchItemCount設(shè)置預(yù)取個數(shù)
  • 加大RecyclerView緩存,比如cacheview大小默認(rèn)為2箩帚,可以設(shè)置大點(diǎn)膏潮,用空間來換取時間焕参,提高流暢度
  • 如果高度固定刻帚,可以設(shè)置setHasFixedSize(true)來避免requestLayout浪費(fèi)資源崇众,否則每次更新數(shù)據(jù)都會重新測量高度顷歌。
void onItemsInsertedOrRemoved() {
   if (hasFixedSize) layoutChildren();
   else requestLayout();
}
  • 如果多個RecycledView 的 Adapter 是一樣的,比如嵌套的 RecyclerView 中存在一樣的 Adapter麻顶,可以通過設(shè)置 RecyclerView.setRecycledViewPool(pool);來共用一個 RecycledViewPool队萤。這樣就減少了創(chuàng)建VIewholder的開銷要尔。
  • 在RecyclerView的元素比較高,一屏只能顯示一個元素的時候,第一次滑動到第二個元素會卡頓旁钧。這種情況就可以通過設(shè)置額外的緩存空間,重寫getExtraLayoutSpace方法即可寄猩。
new LinearLayoutManager(this) {
    @Override
    protected int getExtraLayoutSpace(RecyclerView.State state) {
        return size;
    }
};
  • 設(shè)置RecyclerView.addOnScrollListener();來在滑動過程中停止加載的操作替废。
  • 減少對象的創(chuàng)建,比如設(shè)置監(jiān)聽事件状答,可以全局創(chuàng)建一個惊科,所有view公用一個listener,并且放到CreateView里面去創(chuàng)建監(jiān)聽孙咪,因?yàn)镃reateView調(diào)用要少于bindview。這樣就減少了對象創(chuàng)建所造成的消耗
  • notifyDataSetChange時荤堪,適配器不知道整個數(shù)據(jù)集中的那些內(nèi)容以及存在,再重新匹配ViewHolder時會花生閃爍。設(shè)置adapter.setHasStableIds(true)肮塞,并重寫getItemId()來給每個Item一個唯一的ID,也就是唯一標(biāo)識拷窜,就使itemview的焦點(diǎn)固定赋荆,解決了閃爍問題。

拜拜

今天聊了不少,關(guān)于RecyclerView重要的知識點(diǎn)應(yīng)該都涉及到了均抽,其中bindview的問題下次有機(jī)會我會再配合圖片日志詳細(xì)的說一下款熬。

有一起學(xué)習(xí)的小伙伴可以關(guān)注下我的公眾號——碼上積木????
每日三問知識點(diǎn)/面試題惋鹅,積少成多。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末蝠检,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖藤违,帶你破解...
    沈念sama閱讀 218,386評論 6 506
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件璧榄,死亡現(xiàn)場離奇詭異涂身,居然都是意外死亡妒潭,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,142評論 3 394
  • 文/潘曉璐 我一進(jìn)店門团驱,熙熙樓的掌柜王于貴愁眉苦臉地迎上來摸吠,“玉大人,你說我怎么就攤上這事嚎花〈缌。” “怎么了?”我有些...
    開封第一講書人閱讀 164,704評論 0 353
  • 文/不壞的土叔 我叫張陵紊选,是天一觀的道長啼止。 經(jīng)常有香客問我,道長兵罢,這世上最難降的妖魔是什么献烦? 我笑而不...
    開封第一講書人閱讀 58,702評論 1 294
  • 正文 為了忘掉前任,我火速辦了婚禮卖词,結(jié)果婚禮上稚瘾,老公的妹妹穿的比我還像新娘。我一直安慰自己鳖悠,他們只是感情好瓮增,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,716評論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上跺嗽,一...
    開封第一講書人閱讀 51,573評論 1 305
  • 那天,我揣著相機(jī)與錄音页藻,去河邊找鬼桨嫁。 笑死,一個胖子當(dāng)著我的面吹牛惕橙,可吹牛的內(nèi)容都是我干的瞧甩。 我是一名探鬼主播,決...
    沈念sama閱讀 40,314評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼弥鹦,長吁一口氣:“原來是場噩夢啊……” “哼肚逸!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起彬坏,我...
    開封第一講書人閱讀 39,230評論 0 276
  • 序言:老撾萬榮一對情侶失蹤朦促,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后栓始,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體务冕,經(jīng)...
    沈念sama閱讀 45,680評論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,873評論 3 336
  • 正文 我和宋清朗相戀三年幻赚,在試婚紗的時候發(fā)現(xiàn)自己被綠了禀忆。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 39,991評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡落恼,死狀恐怖箩退,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情佳谦,我是刑警寧澤戴涝,帶...
    沈念sama閱讀 35,706評論 5 346
  • 正文 年R本政府宣布,位于F島的核電站钻蔑,受9級特大地震影響啥刻,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜咪笑,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,329評論 3 330
  • 文/蒙蒙 一可帽、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧窗怒,春花似錦映跟、人聲如沸钝满。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,910評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至孔轴,卻和暖如春剃法,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背路鹰。 一陣腳步聲響...
    開封第一講書人閱讀 33,038評論 1 270
  • 我被黑心中介騙來泰國打工贷洲, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人晋柱。 一個月前我還...
    沈念sama閱讀 48,158評論 3 370
  • 正文 我出身青樓优构,卻偏偏與公主長得像,于是被迫代替她去往敵國和親雁竞。 傳聞我的和親對象是個殘疾皇子钦椭,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,941評論 2 355

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

  • 為什么提出來RecycleView? RecyclerView提供了一種插拔式的體驗(yàn)碑诉,高度解耦彪腔,使用靈活。Recy...
    9283856ddec1閱讀 333評論 0 0
  • 前言 嗨进栽,大家好德挣,好久不見。一個月沒寫過文章了快毛,這里跟大家侃侃這中間發(fā)生了什么格嗅。 一個月前呢,想準(zhǔn)備面試唠帝,就網(wǎng)上隨...
    積木zz閱讀 6,155評論 1 36
  • 目錄介紹 25.0.0.0 請說一下RecyclerView屯掖?adapter的作用是什么,幾個方法是做什么用的没隘?如...
    楊充211閱讀 1,020評論 1 10
  • 【Android 控件 RecyclerView】 概述 RecyclerView是什么 從Android 5.0...
    Rtia閱讀 307,514評論 27 439
  • 久違的晴天懂扼,家長會。 家長大會開好到教室時右蒲,離放學(xué)已經(jīng)沒多少時間了阀湿。班主任說已經(jīng)安排了三個家長分享經(jīng)驗(yàn)。 放學(xué)鈴聲...
    飄雪兒5閱讀 7,523評論 16 22