问小白 wenxiaobai
资讯
历史
科技
环境与自然
成长
游戏
财经
文学与艺术
美食
健康
家居
文化
情感
汽车
三农
军事
旅行
运动
教育
生活
星座命理

Android开发进阶:RecyclerView的四级缓存机制详解

创作时间:
作者:
@小白创作中心

Android开发进阶:RecyclerView的四级缓存机制详解

引用
CSDN
1.
https://blog.csdn.net/qq_32019367/article/details/125053015

RecyclerView的缓存机制是Android开发中一个非常重要但又容易被忽视的环节。良好的缓存策略不仅能提升应用的性能,还能优化用户体验。本文将从源码层面深入解析RecyclerView的四级缓存系统:Scrap、CachedViews、ViewCacheExtension和RecycledViewPool,帮助开发者更好地理解其工作原理和使用场景。

一.Scrap

Scrap是RecyclerView的第一级缓存,包括mAttachedScrap和mChangedScrap。这两个缓存主要在界面重新绘制时发挥作用,与滚动操作无关。

1.Scrap的缓存过程

缓存过程主要发生在LinearLayoutManager的scrapOrRecycleView方法中:

private void scrapOrRecycleView(Recycler recycler, int index, View view) {
    final ViewHolder viewHolder = getChildViewHolderInt(view);
    if (viewHolder.shouldIgnore()) {
        if (DEBUG) {
            Log.d(TAG, "ignoring view " + viewHolder);
        }
        return;
    }
    if (viewHolder.isInvalid() && !viewHolder.isRemoved()
            && !mRecyclerView.mAdapter.hasStableIds()) {
        removeViewAt(index);
        recycler.recycleViewHolderInternal(viewHolder);
    } else {
        detachViewAt(index);
        recycler.scrapView(view);
        mRecyclerView.mViewInfoStore.onViewDetached(viewHolder);
    }
}

缓存条件主要由以下三个判断决定:

  • viewHolder.isInvalid():判断ViewHolder是否为无效状态
  • viewHolder.isRemoved():判断ViewHolder是否被移除
  • mRecyclerView.mAdapter.hasStableIds():判断适配器是否使用稳定ID

通过调试可以得出以下结论:

  • 正常数据设置(如第一次设置数据或滚动时)不会触发mAttachedScrap和mChangedScrap的缓存
  • 调用适配器的notifyItemRemoved方法会触发缓存
  • 设置适配器的stableId为true也会触发缓存

具体缓存到哪个容器(mAttachedScrap或mChangedScrap)由以下条件决定:

void scrapView(View view) {
    final ViewHolder holder = getChildViewHolderInt(view);
    if (holder.hasAnyOfTheFlags(ViewHolder.FLAG_REMOVED | ViewHolder.FLAG_INVALID)
            || !holder.isUpdated() || canReuseUpdatedViewHolder(holder)) {
        if (holder.isInvalid() && !holder.isRemoved() && !mAdapter.hasStableIds()) {
            throw new IllegalArgumentException("Called scrap view with an invalid view."
                    + " Invalid views cannot be reused from scrap, they should rebound from"
                    + " recycler pool." + exceptionLabel());
        }
        holder.setScrapContainer(this, false);
        mAttachedScrap.add(holder);
    } else {
        if (mChangedScrap == null) {
            mChangedScrap = new ArrayList<ViewHolder>();
        }
        holder.setScrapContainer(this, true);
        mChangedScrap.add(holder);
    }
}

2.Scrap的复用

Scrap的复用过程主要发生在tryGetViewHolderForPositionByDeadline方法中:

需要注意的是,dispatchLayoutStep3方法中会清空Scrap缓存:

void removeAndRecycleScrapInt(Recycler recycler) {
    final int scrapCount = recycler.getScrapCount();
    for (int i = scrapCount - 1; i >= 0; i--) {
        final View scrap = recycler.getScrapViewAt(i);
        final ViewHolder vh = getChildViewHolderInt(scrap);
        if (vh.shouldIgnore()) {
            continue;
        }
        vh.setIsRecyclable(false);
        if (vh.isTmpDetached()) {
            mRecyclerView.removeDetachedView(scrap, false);
        }
        if (mRecyclerView.mItemAnimator != null) {
            mRecyclerView.mItemAnimator.endAnimation(vh);
        }
        vh.setIsRecyclable(true);
        recycler.quickRecycleScrapView(scrap);
    }
    recycler.clearScrap();
    if (scrapCount > 0) {
        mRecyclerView.invalidate();
    }
}

Scrap缓存的主要作用是应对轻量级刷新,如移除或更新单个条目,避免不必要的数据重设。

二、CachedViews

CachedViews是与滚动相关的缓存,主要用于存储被移出屏幕的View,容量默认为2。其复用过程主要发生在getScrapOrHiddenOrCachedHolderForPosition方法中:

final int cacheSize = mCachedViews.size();
for (int i = 0; i < cacheSize; i++) {
    final ViewHolder holder = mCachedViews.get(i);
    if (!holder.isInvalid() && holder.getLayoutPosition() == position
            && !holder.isAttachedToTransitionOverlay()) {
        if (!dryRun) {
            mCachedViews.remove(i);
        }
        if (DEBUG) {
            Log.d(TAG, "getScrapOrHiddenOrCachedHolderForPosition(" + position
                    + ") found match in cache: " + holder);
        }
        return holder;
    }
}

CachedViews主要用于处理来回滑动的情况,避免不必要的数据重设。

三、ViewCacheExtension

ViewCacheExtension是一个可扩展的缓存帮助类,开发者可以根据需要实现自定义的缓存逻辑。其主要作用是在Scrap和CachedViews之外提供额外的缓存层。

四、RecycledViewPool

RecycledViewPool是RecyclerView的终极回收站,以SparseArray嵌套ArrayList的形式保存ViewHolder,按itemType区分缓存。默认缓存大小为5,可以设置最大缓存数量。

缓存池定义了SparseArray mScrap,它是一个根据不同itemType来保存静态类ScrapData对象的SparseArray,ScrapData中包含了ArrayList mScrapHeap ,mScrapHeap是保存该itemType类型下ViewHolder的ArrayList。

RecycledViewPool的主要特点是会保存ViewHolder对象但不保留数据信息,在复用时需要重新调用onBindViewHolder方法绑定数据。

© 2023 北京元石科技有限公司 ◎ 京公网安备 11010802042949号