RecyclerView 的回收和复用

来源:互联网 发布:网络推手阿建推网红 编辑:程序博客网 时间:2024/06/05 11:12

RecyclerView 的回收和复用

前段时间在面试的时候这个问题被问到过,可惜自己在用的时候只知道 RecyclerView 可以通过回收和复用 view 来达到减少创建视图的优化。单内部是怎么缓存的?缓存多少?怎么区分不同的 View?

趁这个周清闲,把这个问题解决一下,源码之下无密码,那我们就从源码入手!

View 的回收

/*** 使用给定的回收机制删除子视图并回收*/public void removeAndRecycleView(View child, Recycler recycler) {    removeView(child);    recycler.recycleView(child);}

这个方法就是我们要研究的入口,这个方法很简单,第一行是删除子视图,第二行是回收器回收视图,我们跟进第二行的代码。

 public void recycleView(View view) {    // This public recycle method tries to make view recycle-able since layout manager    // intended to recycle this view (e.g. even if it is in scrap or change cache)    ViewHolder holder = getChildViewHolderInt(view);    if (holder.isTmpDetached()) {        removeDetachedView(view, false);    }    if (holder.isScrap()) {        holder.unScrap();    } else if (holder.wasReturnedFromScrap()) {        holder.clearReturnedFromScrapFlag();    }    recycleViewHolderInternal(holder);}

这段代码主要是做了一些判断,不是我们要关注的部分,在最后一行调用了 recyclerViewHolderInternal ,我们继续跟进去。

这个方法的代码比较多,我们之截取中间一段感兴趣的代码。
Markdown

if (cachedViewSize >= mViewCacheMax && cachedViewSize > 0)

int mViewCacheMax = DEFAULT_CACHE_SIZE

static final int DEFAULT_CACHE_SIZE = 2

从这里可以看到,并不是不用的 View 就会立即被回收的,而是先存储在 mCachedViews 中,当 mCachedViews 大于 2 时会回收位于第 0 个位置的视图。

/** * 回收缓存的视图,并从列表(mCachedViews)中删除 */ void recycleCachedViewAt(int cachedViewIndex) {    ViewHolder viewHolder = mCachedViews.get(cachedViewIndex);    addViewHolderToRecycledViewPool(viewHolder, true);    mCachedViews.remove(cachedViewIndex);}

其中 * addViewHolderToRecycledViewPool * 方法中比较重要的是获得了 * RecycledViewPool * 对象,并向其中添加传入的 viewHolder。

public void putRecycledView(ViewHolder scrap) {    final int viewType = scrap.getItemViewType();    final ArrayList scrapHeap = getScrapDataForType(viewType).mScrapHeap;    if (mScrap.get(viewType).mMaxScrap <= scrapHeap.size()) {        return;    }    scrap.resetInternal();    scrapHeap.add(scrap);}

这里再加上 scrap 的一些代码就非常好理解了

static class ScrapData {    ArrayList<ViewHolder> mScrapHeap = new ArrayList<>();    int mMaxScrap = DEFAULT_MAX_SCRAP;    long mCreateRunningAverageNs = 0;    long mBindRunningAverageNs = 0;}SparseArray<ScrapData> mScrap = new SparseArray<>();

可以看到在回收 View 的时候是按 ViewType 来存储在不同的 ScrapData 中,并且最大默认缓存数是 5.

SparseArray 相当于 HashMap

View 的复用

View 的复用我们可以从 getViewForPosition 方法开始分析,重点在 tryGetViewHolderForPositionByDeadline 方法中,这个方法很长,我们截取重要的部分。
Markdown

可以看到我们利用 RecycledViewPool 通过 viewType 获得缓存的 holder

/** * 返回缓存并删除 */public ViewHolder getRecycledView(int viewType) {    final ScrapData scrapData = mScrap.get(viewType);    if (scrapData != null && !scrapData.mScrapHeap.isEmpty()) {        final ArrayList<ViewHolder> scrapHeap = scrapData.mScrapHeap;        return scrapHeap.remove(scrapHeap.size() - 1);    }    return null;}

View 的复用过程相对还是比较好理解的,就是通过 ViewType 来获得 RecycledViewPool 中缓存的视图。

其他

在这次学习的过程中还注意到,可以通过给 RecyclerView 设置 RecycledViewPool 来实现多个 RecyclerView 之间的 view 共享,感觉这个挺不错。比如一些 等待View, 错误View,但 ViewType 一定要处理好。

原创粉丝点击