RecyclerView解析之缓存机制

很多文章都是先从获取viewholder来分析他的缓存机制的,这里打算翻过来,先讲解一下都在什么情况下,哪些viewholder会被放入哪些缓存中。

看本文之前最好先对rv的整体布局流程有一个简单的了解,但是不知道也没关系啦。

不过viewholder有很多标记位用来表示状态,我们还是要先看一下。

标记位 注释
FLAG_BOUND 在viewholder的被onbind时会添加此标记
FLAG_UPDATE 标记该ViewHolder显示的数据过时,需要重新onbind,但其position 和itemid还正确
FLAG_INVALID 不合法,需要全部重新绑定
FLAG_REMOVED 该viewholder表示的数据已经被移除了。该view还可能被用于执行移除动画
FLAG_NOT_RECYCLABLE 禁止回收
FLAG_RETURNED_FROM_SCRAP 此ViewHolder是从scrap中返回的,这意味着我们希望对此itemView进行addView调用。 从废料中返回时,ViewHolder会保留在scrap列表中,直到布局传递结束为止,如果未将其添加回RecyclerView,则由RecyclerView进行回收。
FLAG_IGNORE 该viewholder全权由layoutmanager管理,我们不对其执行废弃或者回收或者移除操作直到layoutmanager被替换。
FLAG_TMP_DETACHED 当View被detach时,我们设置此标志,以便在需要删除它或将其重新添加时可以采取正确的措施。
FLAG_ADAPTER_POSITION_UNKNOWN 此时无法确定viewholder的位置,直到被重新绑定一个position

其实还有一些标记位,不过暂时看不到,就先不了解了。其中有一些比如remove/update等的标记位,是在dispatchlayoutstep1中因为数据集更新而被添加上的,会在接下来的布局中对其进行相应的处理。具体的逻辑可以参考我对Rv数据更新的文章,这里就不赘述了。

回收

下面进入正题,在rv中有那么几层缓存,不过到底分成几层呢我觉得也不好区分,下面先一一列出。

mChangedScrap/mAttachedScrap

我们把这两个放在一起说。以使用linearlayoutmanager为例子,通过对他们俩的add方法跟踪,发现了两条调用链。其中一条涉及到从缓存中取数据的方法,我们暂时先不看。另外一条如下:

public void onLayoutChildren(RecyclerView.Recycler recycler, RecyclerView.State state) {
    ···
    detachAndScrapAttachedViews(recycler);
        ···
}
public void detachAndScrapAttachedViews(@NonNull Recycler recycler) {
    final int childCount = getChildCount();
    for (int i = childCount - 1; i >= 0; i--) {
        final View v = getChildAt(i);
        scrapOrRecycleView(recycler, i, v);
    }
}

当linearlayoutmanager开始布局child时,如果新的数据集合不为空,则会先将之前rx上所有的child回收。

这里我们再看一下scrapOnRecyclerView方法

private void scrapOrRecycleView(Recycler recycler, int index, View view) {
    final ViewHolder viewHolder = getChildViewHolderInt(view);
    ···
    if (viewHolder.isInvalid() && !viewHolder.isRemoved()//如果被标记了非法但没有remove标记且没有stableid,根据之前看到的标记位,说明这种view需要重新绑定才行,所以不能直接通过position重用。如果也没有stableId的话就只能分配给下一层缓存了
            && !mRecyclerView.mAdapter.hasStableIds()) {
        removeViewAt(index);//移除掉此view
        recycler.recycleViewHolderInternal(viewHolder);//通过recycleViewHolderInternal回收,这就涉及另一种回收了,暂时不看
    } else {//可以重用的情况
        detachViewAt(index);//暂时从child数组中移除,不用刷新页面
        recycler.scrapView(view);//将此view回收
        mRecyclerView.mViewInfoStore.onViewDetached(viewHolder);//通知Viewinfostore 这个涉及动画,先不管
    }
}
void scrapView(View view) {
    final ViewHolder holder = getChildViewHolderInt(view);
    if (holder.hasAnyOfTheFlags(ViewHolder.FLAG_REMOVED | ViewHolder.FLAG_INVALID)
            || !holder.isUpdated() || canReuseUpdatedViewHolder(holder)) {
        ···//如果是被移除的或者没有update标记或者可在执行动画是重用
        holder.setScrapContainer(this, false);
        mAttachedScrap.add(holder);//放入mAttachedScrap缓存中
    } else {//否则说明是被标记了更新的view
        if (mChangedScrap == null) {
            mChangedScrap = new ArrayList();
        }
        holder.setScrapContainer(this, true);
        mChangedScrap.add(holder);//放入mChangedScrap缓存中
    }
}

到这里,这一层缓存的回收就结束了。

HiddenChild

这一层这一层不能算是缓存。不过在获取viewholder的时候,也会来这里面查找,所以我就放了过来。

依然先追踪一下调用链。

首先在rv的addAnimatingView方法中

private void addAnimatingView(ViewHolder viewHolder) {
    final View view = viewHolder.itemView;
    final boolean alreadyParented = view.getParent() == this;
    mRecycler.unscrapView(getChildViewHolder(view));
    if (viewHolder.isTmpDetached()) {
        // re-attach
        mChildHelper.attachViewToParent(view, -1, view.getLayoutParams(), true);
    } else if (!alreadyParented) {
        mChildHelper.addView(view, true);//将view添加乳hidden
    } else {
        mChildHelper.hide(view);
    }
}

这里可以看出来,hidden是和动画相关的。有些时候,数据集已经发生了改变,某一条数据已经不存在了,不过因为动画的原因,这个view还在需要在屏幕上显示。此时他就会处于hidden的状态。rv使用childHelper来封装对viewgroup中view的添加移除等操作,来保证不会因为上面的原因导致发生误差。具体可以看我rv之childHelper,这里就不赘述了。

mCachedViews

当viewholder不满足mChangedScrap/mAttachedScrap的条件,或者在其他情况下,一般都是通过调用如下方法来回收view的,比如在滚动布局时,移出屏幕的view最终就是通过此方法回收。

void recycleViewHolderInternal(ViewHolder holder) {
    ···
    final boolean transientStatePreventsRecycling = holder
            .doesTransientStatePreventRecycling();
    final boolean forceRecycle = mAdapter != null
            && transientStatePreventsRecycling
            && mAdapter.onFailedToRecycleView(holder);
    boolean cached = false;
    boolean recycled = false;
    ···
    if (forceRecycle || holder.isRecyclable()) {
        if (mViewCacheMax > 0//默认为2
                && !holder.hasAnyOfTheFlags(ViewHolder.FLAG_INVALID
                | ViewHolder.FLAG_REMOVED
                | ViewHolder.FLAG_UPDATE
                | ViewHolder.FLAG_ADAPTER_POSITION_UNKNOWN)) {//如果view (合法|没有被remove|没有被更新|adapterPosition有效)(比如因为滚动被移出屏幕的viewholder)就会被放入cached中缓存
            // Retire oldest cached view
            int cachedViewSize = mCachedViews.size();
            if (cachedViewSize >= mViewCacheMax && cachedViewSize > 0) {//如果没有空间
                recycleCachedViewAt(0);//将队列头出队并放入RecycledViewPool
                cachedViewSize--;
            }

            int targetCacheIndex = cachedViewSize;
            if (ALLOW_THREAD_GAP_WORK
                    && cachedViewSize > 0
                    && !mPrefetchRegistry.lastPrefetchIncludedPosition(holder.mPosition)) {//设计到gapworker预读取 如果缓存的不是预读取的数据,则要把这个数据插到预读取的数据之前,以防缓存满的时候预读取的数据被先移除
                // when adding the view, skip past most recently prefetched views
                int cacheIndex = cachedViewSize - 1;
                while (cacheIndex >= 0) {
                    int cachedPos = mCachedViews.get(cacheIndex).mPosition;
                    if (!mPrefetchRegistry.lastPrefetchIncludedPosition(cachedPos)) {
                        break;
                    }
                    cacheIndex--;
                }
                targetCacheIndex = cacheIndex + 1;
            }
            mCachedViews.add(targetCacheIndex, holder);//加入缓存
            cached = true;//缓存成功
        }
        if (!cached) {//缓存失败,通过RecycledViewPool去存储
            addViewHolderToRecycledViewPool(holder, true);
            recycled = true;
        }
    }
    ···
    mViewInfoStore.removeViewHolder(holder);//被回收了,从中移除
    if (!cached && !recycled && transientStatePreventsRecycling) {
        holder.mOwnerRecyclerView = null;
    }
}

这里可以看到,预读取的数据就会被放到cached中。

最后

RecycledViewPool

条件不符合上面几层缓存或者被从cache中挤出来的viewholder就会来到RecycledViewPool中了。

void addViewHolderToRecycledViewPool(@NonNull ViewHolder holder, boolean dispatchRecycled) {
    clearNestedRecyclerViewIfNotNested(holder);
    if (holder.hasAnyOfTheFlags(ViewHolder.FLAG_SET_A11Y_ITEM_DELEGATE)) {
        holder.setFlags(0, ViewHolder.FLAG_SET_A11Y_ITEM_DELEGATE);//清除FLAG_SET_A11Y_ITEM_DELEGATE
        ViewCompat.setAccessibilityDelegate(holder.itemView, null);//移除辅助功能代理
    }
    if (dispatchRecycled) {
        dispatchViewRecycled(holder);//通知被回收了
    }
    holder.mOwnerRecyclerView = null;//
    getRecycledViewPool().putRecycledView(holder);//放入RecycledViewPool
}

这里的逻辑比较简单,我们主要分析一下RecycledViewPool的结构。

RecycledViewPool中持有一个以viewtype为key的SparseArray,其value的类型为

static class ScrapData {
    final ArrayList mScrapHeap = new ArrayList<>();
    int mMaxScrap = DEFAULT_MAX_SCRAP;//最大长度 默认为5
    long mCreateRunningAverageNs = 0;//平均创建时间
    long mBindRunningAverageNs = 0;//平均绑定时间
}

类似hashmap的结构。所以说默认每个类型最大能存放五个。

另外,这个RecycledViewPool提供了几个比较有意思的方法

void factorInCreateTime(int viewType, long createTimeNs) {
    ScrapData scrapData = getScrapDataForType(viewType);
    scrapData.mCreateRunningAverageNs = runningAverage(
            scrapData.mCreateRunningAverageNs, createTimeNs);
}

void factorInBindTime(int viewType, long bindTimeNs) {
    ScrapData scrapData = getScrapDataForType(viewType);
    scrapData.mBindRunningAverageNs = runningAverage(
            scrapData.mBindRunningAverageNs, bindTimeNs);
}

boolean willCreateInTime(int viewType, long approxCurrentNs, long deadlineNs) {
    long expectedDurationNs = getScrapDataForType(viewType).mCreateRunningAverageNs;
    return expectedDurationNs == 0 || (approxCurrentNs + expectedDurationNs < deadlineNs);
}

boolean willBindInTime(int viewType, long approxCurrentNs, long deadlineNs) {
    long expectedDurationNs = getScrapDataForType(viewType).mBindRunningAverageNs;
    return expectedDurationNs == 0 || (approxCurrentNs + expectedDurationNs < deadlineNs);
}

long runningAverage(long oldAverage, long newValue) {
     if (oldAverage == 0) {
          return newValue;
     }
     return (oldAverage / 4 * 3) + (newValue / 4);
}

上面四个方法,用于更新平均创建/绑定该类型holder的耗时,以及用平均时间作为基准判断在指定时间内是否能完成对该holder的创建/绑定。具体有什么用呢?暂时我们先放一放。

另外,可以通过多个rv公用一个RecycledViewPool来实现viewholder的回收共用。

ViewCacheExtension

这是一个开发者可以自行扩展的缓存,一般情况下用不到。暂时先不看。

到此为止,rv的三或者四级缓存的回收操作我们就全部分析完了。

下面我们就看一下取出数据的逻辑。

取出

linearlayout通过next方法获取一个viewholder并将它添加到rv上,具体细节可以看我对LinearLayoutManager的分析,这里不赘述了。

View next(RecyclerView.Recycler recycler) {
    if (mScrapList != null) {
        return nextViewFromScrapList();//在执行动画相关时不为null,我们暂时不考虑这种情况
    }
    final View view = recycler.getViewForPosition(mCurrentPosition);
    mCurrentPosition += mItemDirection;
    return view;
}

这里的mScrapList就是持有的recycler中对mAttachedScrap的包装。

private final List
        mUnmodifiableAttachedScrap = Collections.unmodifiableList(mAttachedScrap);

通过Collections.unmodifiableList包装成只可读,不可写的list。

通过getViewForPosition方法,最终调用了tryGetViewHolderForPositionByDeadline方法来获取viewholder。下面分析一下这个重量级的方法。

tryGetViewHolderForPositionByDeadline

因为这个方法比较长,会分段看一下。

ViewHolder tryGetViewHolderForPositionByDeadline(int position,
        boolean dryRun, long deadlineNs) {
    ···
    boolean fromScrapOrHiddenOrCache = false;
    ViewHolder holder = null;
    // 0) If there is a changed scrap, try to find from there
    if (mState.isPreLayout()) {
        holder = getChangedScrapViewForPosition(position);
        fromScrapOrHiddenOrCache = holder != null;
    }
    ···
}

首先,先通过getChangedScrapViewForPosition查找。

getChangedScrapViewForPosition
ViewHolder getChangedScrapViewForPosition(int position) {
    // If pre-layout, check the changed scrap for an exact match.
    final int changedScrapSize;
    if (mChangedScrap == null || (changedScrapSize = mChangedScrap.size()) == 0) {
        return null;
    }
    // find by position
    for (int i = 0; i < changedScrapSize; i++) {
        final ViewHolder holder = mChangedScrap.get(i);
        if (!holder.wasReturnedFromScrap() && holder.getLayoutPosition() == position) {
            holder.addFlags(ViewHolder.FLAG_RETURNED_FROM_SCRAP);
            return holder;
        }
    }
    // find by id
    if (mAdapter.hasStableIds()) {
        final int offsetPosition = mAdapterHelper.findPositionOffset(position);
        if (offsetPosition > 0 && offsetPosition < mAdapter.getItemCount()) {
            final long id = mAdapter.getItemId(offsetPosition);
            for (int i = 0; i < changedScrapSize; i++) {
                final ViewHolder holder = mChangedScrap.get(i);
                if (!holder.wasReturnedFromScrap() && holder.getItemId() == id) {
                    holder.addFlags(ViewHolder.FLAG_RETURNED_FROM_SCRAP);
                    return holder;
                }
            }
        }
    }
    return null;
}

继续tryGetViewHolderForPositionByDeadline。

ViewHolder tryGetViewHolderForPositionByDeadline(int position,
        boolean dryRun, long deadlineNs) {
    ···
    // 1) Find by position from scrap/hidden list/cache
    if (holder == null) {
        holder = getScrapOrHiddenOrCachedHolderForPosition(position, dryRun);
        if (holder != null) {
            ···
        }
    }
    ···
}

如果没有在mChangedScrap中找到缓存,则通过getScrapOrHiddenOrCachedHolderForPosition继续查找。

getScrapOrHiddenOrCachedHolderForPosition
ViewHolder getScrapOrHiddenOrCachedHolderForPosition(int position, boolean dryRun) {
    final int scrapCount = mAttachedScrap.size();

    // Try first for an exact, non-invalid match from scrap.
    for (int i = 0; i < scrapCount; i++) {
        final ViewHolder holder = mAttachedScrap.get(i);
        if (!holder.wasReturnedFromScrap() && holder.getLayoutPosition() == position
                && !holder.isInvalid() && (mState.mInPreLayout || !holder.isRemoved())) {
            holder.addFlags(ViewHolder.FLAG_RETURNED_FROM_SCRAP);
            return holder;
        }
    }
        ···
    return null;
}

此方法我们分成三段看。首先从mAttachedScrap中寻找。有几个判断条件:

  1. !holder.wasReturnedFromScrap() 我们之前看到这个标记位,设置了此标记说明别人此时已经抢先获取了此viewholder缓存了。
  2. holder.getLayoutPosition() == position 要查找的position。
  3. !holder.isInvalid() 设置了此标记为说明不合法了,需要全部重新绑定包括position,就没意义了。
  4. mState.mInPreLayout || !holder.isRemoved() 没有被移除或者处于预布局状态。

符合上述条件,则设置FLAG_RETURNED_FROM_SCRAP,返回。

当mAttachedScrap中没有找到时:

ViewHolder getScrapOrHiddenOrCachedHolderForPosition(int position, boolean dryRun) {
    ···
    if (!dryRun) {
        View view = mChildHelper.findHiddenNonRemovedView(position);
        if (view != null) {
            final ViewHolder vh = getChildViewHolderInt(view);
            mChildHelper.unhide(view);
            int layoutIndex = mChildHelper.indexOfChild(view);
            if (layoutIndex == RecyclerView.NO_POSITION) {
                throw new IllegalStateException("layout index should not be -1 after "
                        + "unhiding a view:" + vh + exceptionLabel());
            }
            mChildHelper.detachViewFromParent(layoutIndex);
            scrapView(view);
            vh.addFlags(ViewHolder.FLAG_RETURNED_FROM_SCRAP
                    | ViewHolder.FLAG_BOUNCED_FROM_HIDDEN_LIST);
            return vh;
        }
    }
        ···
}

下一步就是去hidden中查找了。如果找到了,把它放入scrap缓存中并添加flag。这里一般也是用于动画逻辑。

如果在hidden中也找不到呢?我们继续看。根据方法名也能猜出来,下一步是去cache中查找了。

ViewHolder getScrapOrHiddenOrCachedHolderForPosition(int position, boolean dryRun) {
        ···
    // Search in our first-level recycled view cache.
    final int cacheSize = mCachedViews.size();
    for (int i = 0; i < cacheSize; i++) {
        final ViewHolder holder = mCachedViews.get(i);
        // invalid view holders may be in cache if adapter has stable ids as they can be
        // retrieved via getScrapOrCachedViewForId
        if (!holder.isInvalid() && holder.getLayoutPosition() == position) {
            if (!dryRun) {
                mCachedViews.remove(i);
            }
            if (DEBUG) {
                Log.d(TAG, "getScrapOrHiddenOrCachedHolderForPosition(" + position
                        + ") found match in cache: " + holder);
            }
            return holder;
        }
    }
    return null;
}

遍历cacheView查找对应的,然后返回。

到这里getScrapOrHiddenOrCachedHolderForPosition方法就结束了,我们继续跟踪。

ViewHolder tryGetViewHolderForPositionByDeadline(int position,
        boolean dryRun, long deadlineNs) {
    ···
    // 1) Find by position from scrap/hidden list/cache
    if (holder == null) {
        holder = getScrapOrHiddenOrCachedHolderForPosition(position, dryRun);
        if (holder != null) {
            if (!validateViewHolderForOffsetPosition(holder)) {//看到了这里
                if (!dryRun) {
                    holder.addFlags(ViewHolder.FLAG_INVALID);
                    if (holder.isScrap()) {//
                        removeDetachedView(holder.itemView, false);
                        holder.unScrap();//从scrap中移除
                    } else if (holder.wasReturnedFromScrap()) {
                        holder.clearReturnedFromScrapFlag();//清理标记位
                    }
                    recycleViewHolderInternal(holder);
                }
                holder = null;
            } else {
                fromScrapOrHiddenOrCache = true;
            }
        }
    }
    ···
}

从这三级缓存中查找到holder之后,首先要验证这个holder的合法性,也就是是不是可用。

boolean validateViewHolderForOffsetPosition(ViewHolder holder) {
    // if it is a removed holder, nothing to verify since we cannot ask adapter anymore
    // if it is not removed, verify the type and id.
    if (holder.isRemoved()) {
        if (DEBUG && !mState.isPreLayout()) {
            throw new IllegalStateException("should not receive a removed view unless it"
                    + " is pre layout" + exceptionLabel());
        }
        return mState.isPreLayout();
    }
    if (holder.mPosition < 0 || holder.mPosition >= mAdapter.getItemCount()) {
        throw new IndexOutOfBoundsException("Inconsistency detected. Invalid view holder "
                + "adapter position" + holder + exceptionLabel());
    }
    if (!mState.isPreLayout()) {
        // don't check type if it is pre-layout.
        final int type = mAdapter.getItemViewType(holder.mPosition);
        if (type != holder.getItemViewType()) {
            return false;
        }
    }
    if (mAdapter.hasStableIds()) {
        return holder.getItemId() == mAdapter.getItemId(holder.mPosition);
    }
    return true;
}

如果是被remove掉的viewholder,则无需验证,直接判断当前绘制阶段就可以了。至于为什么呢,暂时先不管。

如果position不合法,抛出异常。

如果当前不是preLayout阶段,则判断itemViewType。

如果有stableId,判断ItemId。

经过合法性验证,如果合法,则直接设置fromScrapOrHiddenOrCache true。否则这个viewholder就不能用,我们给她添加不合法的标签,然后回收掉。

下面我们继续看tryGetViewHolderForPositionByDeadline

ViewHolder tryGetViewHolderForPositionByDeadline(int position,
        boolean dryRun, long deadlineNs) {
    ···
    if (holder == null) {
        final int offsetPosition = mAdapterHelper.findPositionOffset(position);//找到执行改变之后的position
        ···

        final int type = mAdapter.getItemViewType(offsetPosition);//获取目标type
        // 2) Find from scrap/cache via stable ids, if exists
        if (mAdapter.hasStableIds()) {//如果存在stableid,再次根据stableid   去缓存中查找
            holder = getScrapOrCachedViewForId(mAdapter.getItemId(offsetPosition),
                    type, dryRun);
            if (holder != null) {
                // update position
                holder.mPosition = offsetPosition;
                fromScrapOrHiddenOrCache = true;
            }
        }
        if (holder == null && mViewCacheExtension != null) {//去ViewCacheExtension中查找
            // We are NOT sending the offsetPosition because LayoutManager does not
            // know it.
            final View view = mViewCacheExtension
                    .getViewForPositionAndType(this, position, type);
            if (view != null) {
                holder = getChildViewHolder(view);
                if (holder == null) {
                    throw new IllegalArgumentException("getViewForPositionAndType returned"
                            + " a view which does not have a ViewHolder"
                            + exceptionLabel());
                } else if (holder.shouldIgnore()) {
                    throw new IllegalArgumentException("getViewForPositionAndType returned"
                            + " a view that is ignored. You must call stopIgnoring before"
                            + " returning this view." + exceptionLabel());
                }
            }
        }
        if (holder == null) { // fallback to pool
            if (DEBUG) {
                Log.d(TAG, "tryGetViewHolderForPositionByDeadline("
                        + position + ") fetching from shared pool");
            }
            holder = getRecycledViewPool().getRecycledView(type);//去RecyclerViewPool中查找
            if (holder != null) {
                holder.resetInternal();//重置状态
                if (FORCE_INVALIDATE_DISPLAY_LIST) {
                    invalidateDisplayListInt(holder);//
                }
            }
        }
        if (holder == null) {//还是没有找到
            long start = getNanoTime();
            if (deadlineNs != FOREVER_NS
                    && !mRecyclerPool.willCreateInTime(type, start, deadlineNs)) {//预读取的情况下,剩下的时间不够加载的了,为了不占用主线程时间
                return null;//直接返回null
            }
            holder = mAdapter.createViewHolder(RecyclerView.this, type);//createViewHolder
            if (ALLOW_THREAD_GAP_WORK) {
                // only bother finding nested RV if prefetching
                RecyclerView innerView = findNestedRecyclerView(holder.itemView);
                if (innerView != null) {
                    holder.mNestedRecyclerView = new WeakReference<>(innerView);
                }
            }

            long end = getNanoTime();
            mRecyclerPool.factorInCreateTime(type, end - start);//更新创建的时间
            if (DEBUG) {
                Log.d(TAG, "tryGetViewHolderForPositionByDeadline created new ViewHolder");
            }
        }
    }
        ···
}

上面的代码 首先获取目标viewtype

  1. 如果有stableId,先通过stableId进行查找。
  2. mViewCacheExtension中查找。
  3. RecycledViewPool中查找
  4. 都找不到,通过createViewHolder进行创建。

到目前为止,已经结束了获取或者创建holder的操作。继续看下去

ViewHolder tryGetViewHolderForPositionByDeadline(int position,
        boolean dryRun, long deadlineNs) {
    ···
    if (fromScrapOrHiddenOrCache && !mState.isPreLayout() && holder
            .hasAnyOfTheFlags(ViewHolder.FLAG_BOUNCED_FROM_HIDDEN_LIST)) {
        holder.setFlags(0, ViewHolder.FLAG_BOUNCED_FROM_HIDDEN_LIST);//清理此标志位
        if (mState.mRunSimpleAnimations) {//动画相关,我们暂时不看
            ···
        }
    }

    boolean bound = false;
    if (mState.isPreLayout() && holder.isBound()) {
        // do not update unless we absolutely have to.
        holder.mPreLayoutPosition = position;//无需再次绑定
    } else if (!holder.isBound() || holder.needsUpdate() || holder.isInvalid()) {
        ···
        final int offsetPosition = mAdapterHelper.findPositionOffset(position);
        bound = tryBindViewHolderByDeadline(holder, offsetPosition, position, deadlineNs);//再次绑定viewholder
    }

    final ViewGroup.LayoutParams lp = holder.itemView.getLayoutParams();
    final LayoutParams rvLayoutParams;
    if (lp == null) {
        rvLayoutParams = (LayoutParams) generateDefaultLayoutParams();
        holder.itemView.setLayoutParams(rvLayoutParams);
    } else if (!checkLayoutParams(lp)) {
        rvLayoutParams = (LayoutParams) generateLayoutParams(lp);
        holder.itemView.setLayoutParams(rvLayoutParams);
    } else {
        rvLayoutParams = (LayoutParams) lp;
    }
    rvLayoutParams.mViewHolder = holder;//将view和viewholder绑定
    rvLayoutParams.mPendingInvalidate = fromScrapOrHiddenOrCache && bound;//是否需要在将此view add到parent上时刷新。如果数据绑定成功且此holder是通过ScrapOrHiddenOrCache回收来的,则需要刷新
    return holder;
}

到此为止,获取viewholder的整个流程我们就看完了。

你可能感兴趣的:(RecyclerView解析之缓存机制)