文章详情

短信预约-IT技能 免费直播动态提醒

请输入下面的图形验证码

提交验证

短信预约提醒成功

RecyclerView的缓存机制及使用策略

2024-11-29 21:15

关注

缓存层级

负责回收和复用ViewHolder的类是Recycler,负责缓存的主要就是这个类的几个成员变量。

public final class Recycler {

    // 存放可见范围内的 ViewHolder (但是在 onLayoutChildren 的时候,会将所有 View 都会缓存到这), 从这里复用的 ViewHolder 如果 position 或者 id 对应的上,则不需要重新绑定数据。
    final ArrayList mAttachedScrap = new ArrayList<>();
    // 存放可见范围内并且数据发生了变化的 ViewHolder,从这里复用的 ViewHolder 需要重新绑定数据。
    ArrayList mChangedScrap = null;
    // 存放 remove 掉的 ViewHolder,从这里复用的 ViewHolder 如果 position 或者 id 对应的上,则不需要重新绑定数据。
    final ArrayList mCachedViews = new ArrayList(); 
    // 默认值是 2
    private int mRequestedCacheMax = DEFAULT_CACHE_SIZE; 
    // 默认值是 2
    int mViewCacheMax = DEFAULT_CACHE_SIZE; 

    // 存放 remove 掉,并且重置了数据的 ViewHolder,从这里复用的 ViewHolder 需要重新绑定数据。 // 默认值大小是 5 
    RecycledViewPool mRecyclerPool; 
    // 自定义的缓存
    private ViewCacheExtension mViewCacheExtension; 
    }

RecyclerView的缓存机制主要由四个部分组成,它们按照从高到低的优先级排列:

  1. 「Scrap缓存(Scrap Heap)」

包括mAttachedScrap和mChangedScrap,也称为屏内缓存,因为它们主要用于保存屏幕内当前可见或者即将可见的ViewHolder。

mAttachedScrap:存放的是已添加到RecyclerView但与RecyclerView临时分离(例如在滚动或布局调整过程中)的ViewHolder。

mChangedScrap:存放的是数据已改变但尚未重新绑定数据的ViewHolder,通常用于动画播放等场景。

  1. 「Cache缓存(mCachedViews)」

又称离屏缓存,用于保存最新被移除(remove)但尚未被回收的ViewHolder。

缓存的大小是有限制的,默认最大数量为2(由DEFAULT_CACHE_SIZE定义)。

当需要展示新视图时,会首先检查Cache缓存中是否有可用的ViewHolder。

  1. 「ViewCacheExtension」

为开发者预留的缓存池,允许开发者自定义缓存策略,存储更多的或特定类型的ViewHolder。

开发者可以通过实现ViewCacheExtension接口来扩展缓存功能。

  1. 「RecycledViewPool(mRecyclerPool)」

终极的回收缓存池,用于存放被标识为废弃(即其他缓存池不再需要的)的ViewHolder。

这些ViewHolder已经被抹除了数据,需要重新绑定数据才能使用。

RecycledViewPool会根据不同的item类型创建不同的List来存储ViewHolder。

缓存使用策略

int fill(RecyclerView.Recycler recycler, LayoutState layoutState,
        RecyclerView.State state, boolean stopOnFocusable) {
    // max offset we should set is mFastScroll + available
    final int start = layoutState.mAvailable;
    //首选该语句块的判断,判断当前状态是否为滚动状态,如果是的话,则触发 recycleByLayoutState 方法
    if (layoutState.mScrollingOffset != LayoutState.SCROLLING_OFFSET_NaN) {
        // TODO ugly bug fix. should not happen
        if (layoutState.mAvailable < 0) {
            layoutState.mScrollingOffset += layoutState.mAvailable;
        }
        // 分析1----回收
        recycleByLayoutState(recycler, layoutState);
        }
    while ((layoutState.mInfinite || remainingSpace > 0) && layoutState.hasMore(state)) {
        //分析2----复用
        layoutChunk(recycler, state, layoutState, layoutChunkResult);
    }
}

// 分析1----回收 
// 通过一步步追踪,我们发现最后调用的是 removeAndRecycleViewAt() 
public void removeAndRecycleViewAt(int index, @NonNull Recycler recycler) {
    final View view = getChildAt(index);
    //分析1-1
    removeViewAt(index);
    //分析1-2
    recycler.recycleView(view);
}
// 分析1-1
// 从 RecyclerView 移除一个 View 
public void removeViewAt(int index) {
    final View child = getChildAt(index);
    if (child != null) {
        mChildHelper.removeViewAt(index);
    }
}
//分析1-2 
// recycler.recycleView(view) 最终调用的是 recycleViewHolderInternal(holder) 进行回收 VH (ViewHolder)
void recycleViewHolderInternal(ViewHolder holder) {
    if (forceRecycle || holder.isRecyclable()) {
        //判断是否满足放进 mCachedViews 
        if (mViewCacheMax > 0 && !holder.hasAnyOfTheFlags(ViewHolder.FLAG_INVALID| ViewHolder.FLAG_REMOVED| ViewHolder.FLAG_UPDATE| ViewHolder.FLAG_ADAPTER_POSITION_UNKNOWN)){
            // 判断 mCachedViews 是否已满
            if (cachedViewSize >= mViewCacheMax && cachedViewSize > 0) {
                // 如果满了就将下标为0(即最早加入的)移除,同时将其加入到 RecyclerPool 中
                recycleCachedViewAt(0);
                cachedViewSize--;
                }  
            mCachedViews.add(targetCacheIndex, holder);
            cached = true;
            }
        //如果没有满足上面的条件,则直接存进 RecyclerPool 中    
        if (!cached) {
            addViewHolderToRecycledViewPool(holder, true);
            recycled = true;
         } 
     }
}

//分析2
void layoutChunk(RecyclerView.Recycler recycler, RecyclerView.State state,
        LayoutState layoutState, LayoutChunkResult result) {
    //分析2-1
    View view = layoutState.next(recycler);
    if (layoutState.mScrapList == null) {
        if (mShouldReverseLayout == (layoutState.mLayoutDirection
                == LayoutState.LAYOUT_START)) {
            //添加到 RecyclerView 上
            addView(view);
        } else {
            addView(view, 0);
        }
    }
}
//分析2-1
//layoutState.next(recycler) 最后调用的是 tryGetViewHolderForPositionByDeadline() 这个方法正是 复用 核心的方法
ViewHolder tryGetViewHolderForPositionByDeadline(int position,
        boolean dryRun, long deadlineNs) {
    // 0) If there is a changed scrap, try to find from there
    // 例如:我们调用 notifyItemChanged 方法时
    if (mState.isPreLayout()) {
        // 如果是 changed 的 ViewHolder 那么就先从 mChangedScrap 中找
        holder = getChangedScrapViewForPosition(position);
        fromScrapOrHiddenOrCache = holder != null;
    }
    // 1) Find by position from scrap/hidden list/cache
    if (holder == null) {
        //如果在上面没有找到(holder == null),那就尝试从通过 pos 在 mAttachedScrap/ mHiddenViews / mCachedViews 中获取
        holder = getScrapOrHiddenOrCachedHolderForPosition(position, dryRun);
    }
    if (holder == null) {
        // 2) Find from scrap/cache via stable ids, if exists
        if (mAdapter.hasStableIds()) {
            //如果在上面没有找到(holder == null),那就尝试从通过 id 在 mAttachedScrap/ mCachedViews 中获取
            holder = getScrapOrCachedViewForId(mAdapter.getItemId(offsetPosition),
        }
        if (holder == null && mViewCacheExtension != null) {
            //这里是通过自定义缓存中获取,忽略
        }
        //如果在上面都没有找到(holder == null),那就尝试在 RecycledViewPool 中获取
        if (holder == null) { // fallback to pool
            holder = getRecycledViewPool().getRecycledView(type);
            if (holder != null) {
                //这里拿的是,要清空数据的
                holder.resetInternal();
            }
        }
        //如果在 Scrap / Hidden / Cache / RecycledViewPool 都没有找到,那就只能创建一个了。
        if (holder == null) {
            holder = mAdapter.createViewHolder(RecyclerView.this, type);
        }
    }
    return holder;
}

「注意」:当RecyclerView不再需要某个ViewHolder时(例如,当列表项被完全移出屏幕并且缓存已满时),ViewHolder会被放入RecycledViewPool并最终可能被系统回收。

缓存机制的好处

来源:沐雨花飞蝶内容投诉

免责声明:

① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。

② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341

软考中级精品资料免费领

  • 历年真题答案解析
  • 备考技巧名师总结
  • 高频考点精准押题
  • 2024年上半年信息系统项目管理师第二批次真题及答案解析(完整版)

    难度     813人已做
    查看
  • 【考后总结】2024年5月26日信息系统项目管理师第2批次考情分析

    难度     354人已做
    查看
  • 【考后总结】2024年5月25日信息系统项目管理师第1批次考情分析

    难度     318人已做
    查看
  • 2024年上半年软考高项第一、二批次真题考点汇总(完整版)

    难度     435人已做
    查看
  • 2024年上半年系统架构设计师考试综合知识真题

    难度     224人已做
    查看

相关文章

发现更多好内容

猜你喜欢

AI推送时光机
位置:首页-资讯-后端开发
咦!没有更多了?去看看其它编程学习网 内容吧
首页课程
资料下载
问答资讯