ListView 和 RecyclerView 复用机制区别

ListView 和 RecyclerView 的复用机制还是有很大的差异, ListView 的复用是两级缓存的,而 RecyclerView 是四级缓存,在这里参考了腾讯Bugly干货分享里面详细比较了这两者缓存机制的区别。这里我想自己总结一下,方便记忆

介绍 ListView 和 RecyclerView 每一级的复用

ListView 的两级复用

是否 createView 是否 bindView 备注
mActiveViews 用于屏幕内快速复用
mScrapeViews
  • mActiveViews mActiveViews 是一个保存可以屏幕内可见,可以直接复用的 View 的数组。这里的备注说是用于屏幕内快速复用,在一开始还是很懵逼的,但是经过我的一番资料的查询终于搞清楚了,感谢代码如风的的博客,借用下它的配图(在下面)来分析下什么叫屏幕内快速复用,屏幕内快速复用其实也很好理解,重点来了,假如我们分析下面这个场景,屏幕里面可以完整的容纳六个 View,向下滑动,第0个位置的 View (从0开始计数)被滑出了屏幕,但是 mActiveViews 数组里面还有第1到第5个 View 可以直接复用。这就是备注里面所说的屏幕内快速复用,很合清理也不难理解。

  • mScrapeViews mScrapeViews 是一个保存移除屏幕的 View 的数组,移到这里的 View 都会恢复成一个空白的 View,同时也恢复所有的状态(意思就是把 View 恢复到刚创建时的状态,以后再进行数据绑定的时候可以直接使用),关于具体和 mActiveViews 在复用的时候谁先被调用等问题后面解释。

image.png

RecyclerView 的四级复用

是否 createView 是否 bindView 备注
mAttachedScrap 用于屏幕内快速复用
mCacheViews 默认2个
mViewCashExtension 不研究 不研究
mRecyclerPool 默认5个
  • mAttachedScrap 这个可以说和 ListView 的 mActiveViews 十分地相似,但是这里唯一要强调的就是 ListView 和 RecyclerView 复用的东西不同,ListView 复用的是 View,也就是在实现 ListView 的 Adapter 的时候实现的 getView 方法里的参数 convertView,但是 RecyclerView 复用的对象是 ViewHolder,当然 ListView 也可以由自己来实现 ViewHolder,关于 ViewHolder 的知识下面会讲的。

  • mCacheViews + mRecyclerPool 这两个加起来实现的效果和 ListView 的 mScrapeViews 实现的效果差不多,但是内部的细节却显得更加精妙,都是缓存的离开屏幕的 ViewHolder ,相当于对 mScrapeViews 进行了一个分级缓存,当 RecyclerView 对离开的屏幕的 ViewHolder 进行回收时先将 ViewHolder 回收进 mCacheViews,此时 ViewHolder 里面 View 还是被回收时的样子,没有变成初始形态,但当 mCacheViews 里面的个数超过规定的数量时,会把 mCacheViews 里最老的的 ViewHolder 转进 mRecyclerPool 里去,进入到这里就像进入到 ListView 的mScrapeViews,ViewHolder 所有的属性和状态都会被还原,当然在理论上实现多个 RecyclerView 公用同一个 mRecyclerPool ,这样子当我们在使用 ViewPager 的时候就会将 ViewHolder 存进一个共同的 mRecyclerPool 里面,可以一定程度上节省内存。

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;
}

每次调用 getView 的时候会 findViewById ,这无疑是很浪费时间的,有什么方法可以避免每次都去 findViewById 呢?没错就是通过实现 ViewHolder,接下来就是 ViewHolder 的实现。

@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 的时候,当滑动发生的时候会有一种状态是有11个 item 显示在屏幕当中,当然这时有两个 item 是不完全显示的,这种情况下 ListView 和 RecyclerView 不可能先把第1个 item 回收了,再去复用第11 个 item,那这势必不是一种很好的体验(一位会出现改显示出来的还没有显示,但是不该回收的却被回收了),所以正确的姿势绝对是先复用 item ,然后当第1个 item 完全看不见的时候,再去把它回收掉。

ListView 和 RecyclerView 的复用过程

ListView 的复用过程

我觉得 腾讯Bugly 里的流程图解释的很准确,所以这里借用下它的流程图,再加上一些自己的解读,废话不多说,先上图。

image.png

在这上面所有的步骤都很好解释,我这里就想说下其中的一个步骤就是 从mActiviViews 中通过匹配 pos 读取 view,读取成功的时候就会直接跳过 getView() 这个步骤,之前在我没搞懂 mActiviViews 的功能时,还不是很清楚这一步的含义,但是当我懂了 mActiviViews 是什么以后就彻底懂了。因为 mActiviViews 是屏幕上面可见的 View,所以我们并且此时 mActiviViews 上面还未还原,所以就可以直接通过在数据源中的位置来从 mActiviViews 找出对应的 View ,进而直接进行复用。

RecyclerView 的复用

同样借用 腾讯Bugly 俩面 RecyclerView 复用过程的流程图。

image.png

这里值得注意的是处于 mChacheViews 中的 ViewHolder 和 ListView 中的 mActiviViews 一样都是通过 pos 来确定是否使用其中的 ViewHolder(或者View)复用,mRecyclerPool 在找寻 ViewHolder 缓存的时候会根据 View 的类型来找寻不同的 mRecyclerPool,找到后重新 bindView(不同的类型是通过重写 RecyclerView 的 getViewType() 来实现的 )。

ListView 和 RecyclerView 的回收过程

ListView 的回收过程

ListView 的回收过程十分简单,就是完全滑出屏幕后就把 View 回收到 mScrapeViews 当中去,并把 View 还原成初始状态,所以说 ListView 中所有进行复用的 View 的数量加起来一定是一个定值,其大小和屏幕所能容纳下的 item 的个数有关

RecyclerView 的回收过程

RecyclerView 的回收过程就是一个标准的二级缓存,滑出屏幕的 ViewHolder 先缓存进 mCacheViews ,此时并不还原视图,当 mCacheViews 中的数量超过一定的限制以后(默认是2个,这个是可以由自己来决定的),将最先放入 mCacheViews 的 ViewHolder 放入到 mRecyclerPool 当中去,并且是根据 View 的 type 不同,放入不同的 mRecyclerPool 当中去,同时 mRecyclerPool 也有大小的限制(默认是 5 个),但是这种回收机制好处就在于可以保证 mCacheViews 和 mRecyclerPool 是最新的放到前面。

ListView 和 RecyclerView 的优缺点

我个人倾向是使用 RecyclerView 的,RecyclerView 的优点很多很多的,它的自由度更高,提供一种插拔式的体验(特别是体现在想快速的改变 RecyclerView 列表的表现形式的时候,默认实现了 ViewHolder,只用替换相应的 LayoutManager就可以实现),同时在实现一些多种类复杂的列表的时,想要自定义列表切换的动画的时候,优势也十分明显,但是 RecyclerView 也不是说就可以代替 ListView,它们在性能上面的差异不是很明显,各自都有自己的一套回收/复用机制,关键是在实现 RecyclerView 的时候 RecyclerView 要自己重新写 Adpater,这样子很麻烦,特别是项目里面列表出现的次数很多的时候,但是面对简单的需求的时候 ListView 就显得更加的轻松,它可以直接用默认的 Adapter 来实现。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,547评论 6 477
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,399评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,428评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,599评论 1 274
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,612评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,577评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,941评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,603评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,852评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,605评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,693评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,375评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,955评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,936评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,172评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 43,970评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,414评论 2 342

推荐阅读更多精彩内容