当前位置: 首页 > article >正文

RecyclerView详解——(四)缓存复用机制

稍微看了下源码和部分文章,在此做个小小的总结

RecyclerView,意思为可回收的view,那么相对于listview,他的缓存复用肯定是一大优化。

具体而言,当一个列表项被移出屏幕后,RecyclerView并不会销毁其视图,而是会缓存起来,以提供给新进入屏幕的列表项重用,这种重用不但可以避免重复创建不必要的视图,还可以避免重复执行昂贵的findViewById从而达到的改善性能、提升应用响应能力、降低功耗的效果。

一、核心类

Recycler,这个类是 RecycelerView 缓存复用机制的核心。

在我们常规使用RecyclerView时,与RecyclerView构建动态列表相关联的几个重要类中,AdapterViewHolder负责配合使用,共同定义RecyclerView列表项数据的展示方式,其中:

  • ViewHolder是一个包含列表项视图(itemView)的封装容器,同时也是RecyclerView缓存复用的主要对象。

  • Adapter则提供了数据和视图间的“绑定”关系,其包含以下几个关键方法:

    • onCreateViewHolder:负责创建并初始化ViewHolder及其关联的视图,但不会填充视图内容。
    • onBindViewHolder:负责提取适当的数据,填充ViewHolder的视图内容。

而RecyclerView的缓存机制则可以减少onCreateViewHolder和onBindViewHolder的调用,从而达到减少开销,提高性能的效果。

最优解就是两个方法都不调用,直接复用,次解是只需要调用onBindViewHolder进行数据的绑定,而不需要去重新绑定View,最差解肯定是两个都需要去调用了。

public final class Recycler {
        final ArrayList<RecyclerView.ViewHolder> mAttachedScrap = new ArrayList<>();
        ArrayList<RecyclerView.ViewHolder> mChangedScrap = null;

        final ArrayList<RecyclerView.ViewHolder> mCachedViews = new ArrayList<RecyclerView.ViewHolder>();

        RecyclerView.RecycledViewPool mRecyclerPool;

        private RecyclerView.ViewCacheExtension mViewCacheExtension;
    }

从上述代码可以看出,缓存复用的对象是 ViewHolder。上面五个成员变量就是我们常说的 RecyclerView 的四级缓存,分别是:

  1. mChangedScrap/mAttachedScrap主要用于临时存放仍在当前屏幕可见、但被标记为「移除」或「重用」的列表项其均以ArrayList的形式持有着每个列表项的ViewHolder对象,大小无明确限制,但一般来讲,其最大数就是屏幕内总的可见列表项数。

    • 其中,mChangedScrap主要是为列表项数据发生变化时的动画效果服务的。mChangedScrap缓存中的ViewHolder是需要调用onBindViewHolder方法重新绑定数据的。

    • mAttachedScrap应对的则是剩下的绝大部分场景,比如:像notifyItemMoved、notifyItemRemoved这种列表项发生移动,但列表项数据本身没有发生变化的场景。如果和RecyclerView上的position或者itemId匹配上了,那么认为是干净的ViewHolder,是可以直接拿出来使用的,无需调用onBindViewHolder方法。

  2. mCachedViews,用来缓存移出屏幕之外的 ViewHolder(但有可能很快重新进入屏幕的列表项)。只要position或者itemId对应上了,就无需重新绑定数据,默认大小为2,可以通过  public void setItemViewCacheSize(int size) {
        mRecycler.setViewCacheSize(size);
    }   修改缓存大小
  3. mViewCacheExtension,这一层的创建和缓存完全由开发者自己控制,初始值为 null。这一层很少会用到。
  4. mRecyclerPool,ViewHolder 缓存池。mRecyclerPool主要用于按不同的itemType分别存放超出mCachedViews限制的、被移出屏幕的列表项,其会先以SparseArray区分不同的itemType,然后每种itemType对应的值又以ArrayList的形式持有着每个列表项的ViewHolder对象,每种itemType的ArrayList大小限制默认为5。可通过setMaxRecycledViews(itemType,size)修改大小.

二、Recycler的源码调用

    public final class Recycler {
        ...
        /**
         * 尝试通过从Recycler scrap缓存、RecycledViewPool查找或直接创建的形式来获取指定位置的ViewHolder。
         */
        @Nullable
        ViewHolder tryGetViewHolderForPositionByDeadline(int position,
                boolean dryRun, long deadlineNs) {
            if (mState.isPreLayout()) {
                // 0 尝试从mChangedScrap中获取ViewHolder对象
                holder = getChangedScrapViewForPosition(position);
                ...
            }
            if (holder == null) {
                // 1.1 尝试根据position从mAttachedScrap或mCachedViews中获取ViewHolder对象
                holder = getScrapOrHiddenOrCachedHolderForPosition(position, dryRun);
                ...
            }
            if (holder == null) {
                ...
                final int type = mAdapter.getItemViewType(offsetPosition);
                if (mAdapter.hasStableIds()) {
                    // 1.2 尝试根据id从mAttachedScrap或mCachedViews中获取ViewHolder对象
                    holder = getScrapOrCachedViewForId(mAdapter.getItemId(offsetPosition),
                            type, dryRun);
                    ...
                }
                if (holder == null && mViewCacheExtension != null) {
                    // 2 尝试从mViewCacheExtension中获取ViewHolder对象
                    final View view = mViewCacheExtension
                            .getViewForPositionAndType(this, position, type);
                    if (view != null) {
                        holder = getChildViewHolder(view);
                        ...
                    }
                }
                if (holder == null) { // fallback to pool
                    // 3 尝试从mRecycledViewPool中获取ViewHolder对象
                    holder = getRecycledViewPool().getRecycledView(type);
                    ...
                }
                if (holder == null) {
                    // 4.1 回调createViewHolder方法创建ViewHolder对象及其关联的视图
                    holder = mAdapter.createViewHolder(RecyclerView.this, type);
                    ...
                }
            }
    
            if (mState.isPreLayout() && holder.isBound()) {
                ...
            } else if (!holder.isBound() || holder.needsUpdate() || holder.isInvalid()) {
                ...
                // 4.1 回调bindViewHolder方法提取数据填充ViewHolder的视图内容
                bound = tryBindViewHolderByDeadline(holder, offsetPosition, position, deadlineNs);
            }
    
            ...
    
            return holder;
        }
        ...
    }    

从上述代码中,可以看到他们的调用顺序为依次从mChangedScrap/mAttachedScrap、mCachedViews、mViewCacheExtension、mRecyclerPool中尝试获取指定位置或ID的ViewHolder对象以供重用,如果全都获取不到则直接重新创建。

参考文章:

https://juejin.cn/post/7344941254236749851?searchId=202411182234361EC6BF29AC9F2C8D6DB5icon-default.png?t=O83Ahttps://juejin.cn/post/7344941254236749851?searchId=202411182234361EC6BF29AC9F2C8D6DB5关于RecyclerView和ListView的缓存对比

https://juejin.cn/post/6844903448974983181?searchId=20241118230728641CF838198392957185icon-default.png?t=O83Ahttps://juejin.cn/post/6844903448974983181?searchId=20241118230728641CF838198392957185 


http://www.kler.cn/a/406363.html

相关文章:

  • uniapp发布android上架应用商店权限
  • VSCode汉化教程【简洁易懂】
  • 核间通信-Linux下RPMsg使用与源码框架分析
  • 数字化那点事:一文读懂物联网
  • 泷羽sec-星河飞雪-shell-7
  • 在Excel中处理不规范的日期格式数据并判断格式是否正确
  • 论文阅读——Intrusion detection systems using longshort‑term memory (LSTM)
  • 儿童玩具安全检测GB6675标准详细介绍
  • PHP 8.4 重磅发布了
  • 如何创建你的第一个 Telegram 机器人:一步步教程
  • 【Python TensorFlow】进阶指南(续篇三)
  • STM32(hal库)中,为什么DMA没有MSP函数?
  • C# 中Timer的三种用法
  • 代码随想录1016-Day17
  • 【bug】python常见的错误以及解决办法
  • 大数据环境下的高效数据清洗策略
  • 【信息系统项目管理师】第2章:信息技术发展 考点梳理
  • 泥石流灾害风险评估与模拟丨AI与R语言、ArcGIS、HECRAS融合,提升泥石流灾害风险预测的精度和准确性
  • CSS遮罩:mask
  • 使用minio cllient(mc)完成不同服务器的minio的数据迁移和mc基本操作
  • stm32 指定变量存储地址
  • 利用Python爬虫获取1688搜索词推荐:技术与实践
  • P1308 [NOIP2011 普及组] 统计单词数题解
  • [开源重构]Search(Elasticsearch/OpenSearch) Sync Tool
  • c++基础语法
  • shell脚本(三)