ListView 和 RecyclerView 復用機制區(qū)別

ListView 和 RecyclerView 的復用機制還是有很大的差異, ListView 的復用是兩級緩存的限番,而 RecyclerView 是四級緩存,在這里參考了騰訊Bugly干貨分享里面詳細比較了這兩者緩存機制的區(qū)別呀舔。這里我想自己總結一下弥虐,方便記憶

介紹 ListView 和 RecyclerView 每一級的復用

ListView 的兩級復用

是否 createView 是否 bindView 備注
mActiveViews 用于屏幕內(nèi)快速復用
mScrapeViews
  • mActiveViews mActiveViews 是一個保存可以屏幕內(nèi)可見扩灯,可以直接復用的 View 的數(shù)組。這里的備注說是用于屏幕內(nèi)快速復用霜瘪,在一開始還是很懵逼的珠插,但是經(jīng)過我的一番資料的查詢終于搞清楚了,感謝代碼如風的的博客颖对,借用下它的配圖(在下面)來分析下什么叫屏幕內(nèi)快速復用捻撑,屏幕內(nèi)快速復用其實也很好理解,重點來了缤底,假如我們分析下面這個場景顾患,屏幕里面可以完整的容納六個 View,向下滑動个唧,第0個位置的 View (從0開始計數(shù))被滑出了屏幕江解,但是 mActiveViews 數(shù)組里面還有第1到第5個 View 可以直接復用。這就是備注里面所說的屏幕內(nèi)快速復用徙歼,很合清理也不難理解膘流。

  • mScrapeViews mScrapeViews 是一個保存移除屏幕的 View 的數(shù)組,移到這里的 View 都會恢復成一個空白的 View鲁沥,同時也恢復所有的狀態(tài)(意思就是把 View 恢復到剛創(chuàng)建時的狀態(tài)呼股,以后再進行數(shù)據(jù)綁定的時候可以直接使用),關于具體和 mActiveViews 在復用的時候誰先被調(diào)用等問題后面解釋画恰。

image.png

RecyclerView 的四級復用

是否 createView 是否 bindView 備注
mAttachedScrap 用于屏幕內(nèi)快速復用
mCacheViews 默認2個
mViewCashExtension 不研究 不研究
mRecyclerPool 默認5個
  • mAttachedScrap 這個可以說和 ListView 的 mActiveViews 十分地相似画侣,但是這里唯一要強調(diào)的就是 ListView 和 RecyclerView 復用的東西不同拇勃,ListView 復用的是 View,也就是在實現(xiàn) ListView 的 Adapter 的時候實現(xiàn)的 getView 方法里的參數(shù) convertView,但是 RecyclerView 復用的對象是 ViewHolder擅编,當然 ListView 也可以由自己來實現(xiàn) ViewHolder,關于 ViewHolder 的知識下面會講的棺禾。

  • mCacheViews + mRecyclerPool 這兩個加起來實現(xiàn)的效果和 ListView 的 mScrapeViews 實現(xiàn)的效果差不多,但是內(nèi)部的細節(jié)卻顯得更加精妙糊治,都是緩存的離開屏幕的 ViewHolder 唱矛,相當于對 mScrapeViews 進行了一個分級緩存,當 RecyclerView 對離開的屏幕的 ViewHolder 進行回收時先將 ViewHolder 回收進 mCacheViews井辜,此時 ViewHolder 里面 View 還是被回收時的樣子绎谦,沒有變成初始形態(tài),但當 mCacheViews 里面的個數(shù)超過規(guī)定的數(shù)量時粥脚,會把 mCacheViews 里最老的的 ViewHolder 轉進 mRecyclerPool 里去窃肠,進入到這里就像進入到 ListView 的mScrapeViews,ViewHolder 所有的屬性和狀態(tài)都會被還原刷允,當然在理論上實現(xiàn)多個 RecyclerView 公用同一個 mRecyclerPool 冤留,這樣子當我們在使用 ViewPager 的時候就會將 ViewHolder 存進一個共同的 mRecyclerPool 里面碧囊,可以一定程度上節(jié)省內(nèi)存。

ViewHolder 的介紹

ViewHolder 和復用機制沒有什么直接的關系纤怒,但是有時候會存在混淆糯而,所以還是另外介紹下 ViewHolder,這里可以結合我們平時寫的代碼來解釋它肪跋。
如果我們沒有使用 ViewHolder歧蒋,我們的代碼就是下面這種情況。

@Override
public View getView(int position, View convertView, ViewGroup parent) {
        
        ImageView userImg;
        TextView userName;
        TextView userComment;        
        ListViewItem itemData = items.get(position);
        if(convertView == null){
            convertView = View.inflate(context, R.layout.list_item_layout, null);
        }        
        userImg = (ImageView) convertView.findViewById(R.id.user_header_img);
        userName = (TextView) convertView.findViewById(R.id.user_name);
        userComment = (TextView) convertView.findViewById(R.id.user_coomment);
        userImg.setImageResource(itemData.getUserImg());
        userName.setText(itemData.getUserName());
        userComment.setText(itemData.getUserComment());
       
        return convertView;
}

每次調(diào)用 getView 的時候會 findViewById 州既,這無疑是很浪費時間的谜洽,有什么方法可以避免每次都去 findViewById 呢?沒錯就是通過實現(xiàn) ViewHolder吴叶,接下來就是 ViewHolder 的實現(xiàn)阐虚。

@Override
public View getView(int position, View convertView, ViewGroup parent) {
        final ViewHolder holder;
        ListViewItem itemData = items.get(position);
        if(convertView == null){
            convertView = View.inflate(context, R.layout.list_item_layout, null);
            holder = new ViewHolder();
            holder.userImg = (ImageView) convertView.findViewById(R.id.user_header_img);
            holder.userName = (TextView) convertView.findViewById(R.id.user_name);
            holder.userComment = (TextView) convertView.findViewById(R.id.user_coomment);
            convertView.setTag(holder);
        }else{
            holder = (ViewHolder) convertView.getTag();
        }
        holder.userImg.setImageResource(itemData.getUserImg());
        holder.userName.setText(itemData.getUserName());
        holder.userComment.setText(itemData.getUserComment());
        return convertView;
}

static class ViewHolder{
        ImageView userImg;
        TextView userName;
        TextView userComment;
}

說白了 ViewHolder 是用來保持著 convertView 對里面每個子 View 的引用,避免每次都要 findViewById蚌卤。

ListView 和 RecyclerView 是先回收還是先復用实束?

這里先給出答案,當然是先復用逊彭,因為我們還是來設想一下咸灿,如果屏幕剛好只能容納10個 item 的時候,當滑動發(fā)生的時候會有一種狀態(tài)是有11個 item 顯示在屏幕當中侮叮,當然這時有兩個 item 是不完全顯示的避矢,這種情況下 ListView 和 RecyclerView 不可能先把第1個 item 回收了,再去復用第11 個 item囊榜,那這勢必不是一種很好的體驗(一位會出現(xiàn)改顯示出來的還沒有顯示审胸,但是不該回收的卻被回收了),所以正確的姿勢絕對是先復用 item 卸勺,然后當?shù)?個 item 完全看不見的時候砂沛,再去把它回收掉。

ListView 和 RecyclerView 的復用過程

ListView 的復用過程

我覺得 騰訊Bugly 里的流程圖解釋的很準確曙求,所以這里借用下它的流程圖碍庵,再加上一些自己的解讀,廢話不多說圆到,先上圖怎抛。

image.png

在這上面所有的步驟都很好解釋,我這里就想說下其中的一個步驟就是 從mActiviViews 中通過匹配 pos 讀取 view芽淡,讀取成功的時候就會直接跳過 getView() 這個步驟,之前在我沒搞懂 mActiviViews 的功能時豆赏,還不是很清楚這一步的含義挣菲,但是當我懂了 mActiviViews 是什么以后就徹底懂了富稻。因為 mActiviViews 是屏幕上面可見的 View,所以我們并且此時 mActiviViews 上面還未還原白胀,所以就可以直接通過在數(shù)據(jù)源中的位置來從 mActiviViews 找出對應的 View 椭赋,進而直接進行復用。

RecyclerView 的復用

同樣借用 騰訊Bugly 倆面 RecyclerView 復用過程的流程圖或杠。

image.png

這里值得注意的是處于 mChacheViews 中的 ViewHolder 和 ListView 中的 mActiviViews 一樣都是通過 pos 來確定是否使用其中的 ViewHolder(或者View)復用哪怔,mRecyclerPool 在找尋 ViewHolder 緩存的時候會根據(jù) View 的類型來找尋不同的 mRecyclerPool,找到后重新 bindView(不同的類型是通過重寫 RecyclerView 的 getViewType() 來實現(xiàn)的 )向抢。

ListView 和 RecyclerView 的回收過程

ListView 的回收過程

ListView 的回收過程十分簡單认境,就是完全滑出屏幕后就把 View 回收到 mScrapeViews 當中去,并把 View 還原成初始狀態(tài)挟鸠,所以說 ListView 中所有進行復用的 View 的數(shù)量加起來一定是一個定值叉信,其大小和屏幕所能容納下的 item 的個數(shù)有關

RecyclerView 的回收過程

RecyclerView 的回收過程就是一個標準的二級緩存,滑出屏幕的 ViewHolder 先緩存進 mCacheViews 艘希,此時并不還原視圖硼身,當 mCacheViews 中的數(shù)量超過一定的限制以后(默認是2個,這個是可以由自己來決定的)覆享,將最先放入 mCacheViews 的 ViewHolder 放入到 mRecyclerPool 當中去佳遂,并且是根據(jù) View 的 type 不同,放入不同的 mRecyclerPool 當中去撒顿,同時 mRecyclerPool 也有大小的限制(默認是 5 個)丑罪,但是這種回收機制好處就在于可以保證 mCacheViews 和 mRecyclerPool 是最新的放到前面。

ListView 和 RecyclerView 的優(yōu)缺點

我個人傾向是使用 RecyclerView 的核蘸,RecyclerView 的優(yōu)點很多很多的巍糯,它的自由度更高,提供一種插拔式的體驗(特別是體現(xiàn)在想快速的改變 RecyclerView 列表的表現(xiàn)形式的時候客扎,默認實現(xiàn)了 ViewHolder祟峦,只用替換相應的 LayoutManager就可以實現(xiàn)),同時在實現(xiàn)一些多種類復雜的列表的時徙鱼,想要自定義列表切換的動畫的時候宅楞,優(yōu)勢也十分明顯,但是 RecyclerView 也不是說就可以代替 ListView袱吆,它們在性能上面的差異不是很明顯厌衙,各自都有自己的一套回收/復用機制,關鍵是在實現(xiàn) RecyclerView 的時候 RecyclerView 要自己重新寫 Adpater绞绒,這樣子很麻煩婶希,特別是項目里面列表出現(xiàn)的次數(shù)很多的時候,但是面對簡單的需求的時候 ListView 就顯得更加的輕松蓬衡,它可以直接用默認的 Adapter 來實現(xiàn)喻杈。

最后編輯于
?著作權歸作者所有,轉載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末彤枢,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子筒饰,更是在濱河造成了極大的恐慌缴啡,老刑警劉巖,帶你破解...
    沈念sama閱讀 206,214評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件瓷们,死亡現(xiàn)場離奇詭異业栅,居然都是意外死亡,警方通過查閱死者的電腦和手機谬晕,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,307評論 2 382
  • 文/潘曉璐 我一進店門碘裕,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人固蚤,你說我怎么就攤上這事娘汞。” “怎么了夕玩?”我有些...
    開封第一講書人閱讀 152,543評論 0 341
  • 文/不壞的土叔 我叫張陵你弦,是天一觀的道長。 經(jīng)常有香客問我燎孟,道長禽作,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 55,221評論 1 279
  • 正文 為了忘掉前任揩页,我火速辦了婚禮旷偿,結果婚禮上,老公的妹妹穿的比我還像新娘爆侣。我一直安慰自己萍程,他們只是感情好,可當我...
    茶點故事閱讀 64,224評論 5 371
  • 文/花漫 我一把揭開白布兔仰。 她就那樣靜靜地躺著茫负,像睡著了一般。 火紅的嫁衣襯著肌膚如雪乎赴。 梳的紋絲不亂的頭發(fā)上忍法,一...
    開封第一講書人閱讀 49,007評論 1 284
  • 那天,我揣著相機與錄音榕吼,去河邊找鬼饿序。 笑死,一個胖子當著我的面吹牛羹蚣,可吹牛的內(nèi)容都是我干的原探。 我是一名探鬼主播,決...
    沈念sama閱讀 38,313評論 3 399
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼踢匣!你這毒婦竟也來了告匠?” 一聲冷哼從身側響起戈抄,我...
    開封第一講書人閱讀 36,956評論 0 259
  • 序言:老撾萬榮一對情侶失蹤离唬,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后划鸽,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體输莺,經(jīng)...
    沈念sama閱讀 43,441評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 35,925評論 2 323
  • 正文 我和宋清朗相戀三年裸诽,在試婚紗的時候發(fā)現(xiàn)自己被綠了嫂用。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 38,018評論 1 333
  • 序言:一個原本活蹦亂跳的男人離奇死亡丈冬,死狀恐怖嘱函,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情埂蕊,我是刑警寧澤往弓,帶...
    沈念sama閱讀 33,685評論 4 322
  • 正文 年R本政府宣布,位于F島的核電站蓄氧,受9級特大地震影響函似,放射性物質發(fā)生泄漏。R本人自食惡果不足惜喉童,卻給世界環(huán)境...
    茶點故事閱讀 39,234評論 3 307
  • 文/蒙蒙 一撇寞、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧堂氯,春花似錦蔑担、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,240評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至局扶,卻和暖如春恨统,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背三妈。 一陣腳步聲響...
    開封第一講書人閱讀 31,464評論 1 261
  • 我被黑心中介騙來泰國打工畜埋, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人畴蒲。 一個月前我還...
    沈念sama閱讀 45,467評論 2 352
  • 正文 我出身青樓悠鞍,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子咖祭,可洞房花燭夜當晚...
    茶點故事閱讀 42,762評論 2 345

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