美文网首页
RecyclerView(二)

RecyclerView(二)

作者: 涛涛123759 | 来源:发表于2021-10-14 18:12 被阅读0次

    Android知识总结

    一、缓存

    缓存流程图

    二、缓存 mCachedViews

    2.1、第一种进入缓存方式

    执行LinearLayoutManager.onLayoutChildren方法

    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);
        }
    }
    
    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()) {
            //将index的view从mHiddenViews移除的动作
            removeViewAt(index);
            //向 mCachedViews 或 Pool 中添加
            recycler.recycleViewHolderInternal(viewHolder);
        } else {
            //设置RecyclerView这个位置的view的parent为null, 并标记ViewHolder为FLAG_TMP_DETACHED
            detachViewAt(index);
            //添加到mAttachedScrap 或 mCachedViews集合中
            recycler.scrapView(view);
            mRecyclerView.mViewInfoStore.onViewDetached(viewHolder);
        }
    }
    

    2.2、第二种进入缓存方式

    进入RecyclerView内部类Recycler#scrapView

    --》 fill 
    --》 recycleByLayoutState
    --》 recycleViewsFromStart
    --》 recycleChildren
    --》 removeAndRecycleViewAt
    --》 recycler.recycleView
    --》 recycleViewHolderInternal // 关键代码,(cacheView、pool)
    

    我们直接以LinearLayoutManager#recycleViewsFromStart开始

    private void recycleByLayoutState(RecyclerView.Recycler recycler, LayoutState layoutState) {
        if (!layoutState.mRecycle || layoutState.mInfinite) {
            return;
        }
        int scrollingOffset = layoutState.mScrollingOffset;
        int noRecycleSpace = layoutState.mNoRecycleSpace;
        if (layoutState.mLayoutDirection == LayoutState.LAYOUT_START) {
            recycleViewsFromEnd(recycler, scrollingOffset, noRecycleSpace);
        } else {
            recycleViewsFromStart(recycler, scrollingOffset, noRecycleSpace);
        }
    }
    
    private void recycleViewsFromStart(RecyclerView.Recycler recycler, int scrollingOffset,
            int noRecycleSpace) {
        if (scrollingOffset < 0) {
            if (DEBUG) {
                Log.d(TAG, "Called recycle from start with a negative value. This might happen"
                        + " during layout changes but may be sign of a bug");
            }
            return;
        }
        // ignore padding, ViewGroup may not clip children.
        final int limit = scrollingOffset - noRecycleSpace;
        final int childCount = getChildCount();
        if (mShouldReverseLayout) {
            for (int i = childCount - 1; i >= 0; i--) {
                View child = getChildAt(i);
                if (mOrientationHelper.getDecoratedEnd(child) > limit
                        || mOrientationHelper.getTransformedEndWithDecoration(child) > limit) {
                    // stop here
                    recycleChildren(recycler, childCount - 1, i);
                    return;
                }
            }
        } else {
            for (int i = 0; i < childCount; i++) {
                View child = getChildAt(i);
                if (mOrientationHelper.getDecoratedEnd(child) > limit
                        || mOrientationHelper.getTransformedEndWithDecoration(child) > limit) {
                    // stop here
                    recycleChildren(recycler, 0, i);
                    return;
                }
            }
        }
    }
    
    private void recycleChildren(RecyclerView.Recycler recycler, int startIndex, int endIndex) {
        if (startIndex == endIndex) {
            return;
        }
        if (DEBUG) {
            Log.d(TAG, "Recycling " + Math.abs(startIndex - endIndex) + " items");
        }
        if (endIndex > startIndex) {
            for (int i = endIndex - 1; i >= startIndex; i--) {
                removeAndRecycleViewAt(i, recycler);
            }
        } else {
            for (int i = startIndex; i > endIndex; i--) {
                removeAndRecycleViewAt(i, recycler);
            }
        }
    }
    

    执行RecyclerView内部类LayoutManager#removeAndRecycleViewAt

    public void removeAndRecycleViewAt(int index, @NonNull Recycler recycler) {
        final View view = getChildAt(index);
        removeViewAt(index);
        recycler.recycleView(view);
    }
    

    执行RecyclerView内部类Recycler#recycleView

    public void recycleView(@NonNull View view) {
        ViewHolder holder = getChildViewHolderInt(view);
        if (holder.isTmpDetached()) {
            removeDetachedView(view, false);
        }
        if (holder.isScrap()) {
            holder.unScrap();
        } else if (holder.wasReturnedFromScrap()) {
            holder.clearReturnedFromScrapFlag();
        }
        //向 mCachedViews 或 Pool 中添加
        recycleViewHolderInternal(holder);
        if (mItemAnimator != null && !holder.isRecyclable()) {
            mItemAnimator.endAnimation(holder);
        }
    }
    

    2.1、 mCachedViews 或 Pool 缓存

    RecyclerView的集合中缓存

    void recycleViewHolderInternal(RecyclerView.ViewHolder holder) {
        final boolean transientStatePreventsRecycling = holder
                .doesTransientStatePreventRecycling();
        @SuppressWarnings("unchecked")
        final boolean forceRecycle = mAdapter != null
                && transientStatePreventsRecycling
                && mAdapter.onFailedToRecycleView(holder);
        boolean cached = false;
        boolean recycled = false;
        if (DEBUG && mCachedViews.contains(holder)) {
            throw new IllegalArgumentException("cached view received recycle internal? "
                    + holder + exceptionLabel());
        }
        if (forceRecycle || holder.isRecyclable()) {
            if (mViewCacheMax > 0
                    && !holder.hasAnyOfTheFlags(RecyclerView.ViewHolder.FLAG_INVALID
                    | RecyclerView.ViewHolder.FLAG_REMOVED
                    | RecyclerView.ViewHolder.FLAG_UPDATE
                    | RecyclerView.ViewHolder.FLAG_ADAPTER_POSITION_UNKNOWN)) {
                // Retire oldest cached view
                int cachedViewSize = mCachedViews.size();
                //大于缓存的最大值 mViewCacheMax = 2, CacheView 把老的移除集合,放入 pool 中
                if (cachedViewSize >= mViewCacheMax && cachedViewSize > 0) {
                    //取出集合的第一个元素放入 pool 中
                    recycleCachedViewAt(0);
                    //缓存的个数减一
                    cachedViewSize--;
                }
    
                int targetCacheIndex = cachedViewSize;
                if (ALLOW_THREAD_GAP_WORK
                        && cachedViewSize > 0
                        && !mPrefetchRegistry.lastPrefetchIncludedPosition(holder.mPosition)) {
                    // 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 集合
                mCachedViews.add(targetCacheIndex, holder);
                cached = true;
            }
            if (!cached) {
                // 添加到Pool中
                addViewHolderToRecycledViewPool(holder, true);
                recycled = true;
            }
        } else {
            if (DEBUG) {
                Log.d(TAG, "trying to recycle a non-recycleable holder. Hopefully, it will "
                        + "re-visit here. We are still removing it from animation lists"
                        + exceptionLabel());
            }
        }
        mViewInfoStore.removeViewHolder(holder);
        if (!cached && !recycled && transientStatePreventsRecycling) {
            holder.mOwnerRecyclerView = null;
        }
    }
    
    • 接下来看向 pool 中添加的步骤
    void addViewHolderToRecycledViewPool(@NonNull RecyclerView.ViewHolder holder, boolean dispatchRecycled) {
        clearNestedRecyclerViewIfNotNested(holder);
        View itemView = holder.itemView;
        if (mAccessibilityDelegate != null) {
            AccessibilityDelegateCompat itemDelegate = mAccessibilityDelegate.getItemDelegate();
            AccessibilityDelegateCompat originalDelegate = null;
            if (itemDelegate instanceof RecyclerViewAccessibilityDelegate.ItemDelegate) {
                originalDelegate =
                        ((RecyclerViewAccessibilityDelegate.ItemDelegate) itemDelegate)
                                .getAndRemoveOriginalDelegateForItem(itemView);
            }
            // Set the a11y delegate back to whatever the original delegate was.
            ViewCompat.setAccessibilityDelegate(itemView, originalDelegate);
        }
        if (dispatchRecycled) {
            dispatchViewRecycled(holder);
        }
        holder.mOwnerRecyclerView = null;
        getRecycledViewPool().putRecycledView(holder);
    }
    
    public void putRecycledView(RecyclerView.ViewHolder scrap) {
        final int viewType = scrap.getItemViewType();
        final ArrayList<RecyclerView.ViewHolder> scrapHeap = getScrapDataForType(viewType).mScrapHeap;
        if (mScrap.get(viewType).mMaxScrap <= scrapHeap.size()) {
            return;
        }
        if (DEBUG && scrapHeap.contains(scrap)) {
            throw new IllegalArgumentException("this scrap item already exists");
        }
            //进行漂白,即清除一些标志信息个数据
        scrap.resetInternal();
            //保存 ViewHolder
        scrapHeap.add(scrap);
    }
    

    调用RecyclerView.RecycledViewPool中的方法getScrapDataForType

    private ScrapData getScrapDataForType(int viewType) {
        ScrapData scrapData = mScrap.get(viewType);
        if (scrapData == null) {
            scrapData = new ScrapData();
            mScrap.put(viewType, scrapData);
        }
        return scrapData;
    }
    
    • RecyclerView#ScrapData
    static class ScrapData {
        @UnsupportedAppUsage
            //pool 的实际缓存对象,一种 ViewType 对应一个 ScrapData 
        ArrayList<ViewHolder> mScrapHeap = new ArrayList<>();
        int mMaxScrap = DEFAULT_MAX_SCRAP;
        long mCreateRunningAverageNs = 0;
        long mBindRunningAverageNs = 0;
    }
    SparseArray<ScrapData> mScrap = new SparseArray<>();
    
    • resetInternal 重置数据
    void resetInternal() {
        mFlags = 0;
        mPosition = NO_POSITION;
        mOldPosition = NO_POSITION;
        mItemId = NO_ID;
        mPreLayoutPosition = NO_POSITION;
        mIsRecyclableCount = 0;
        mShadowedHolder = null;
        mShadowingHolder = null;
        clearPayload();
        mWasImportantForAccessibilityBeforeHidden = View.IMPORTANT_FOR_ACCESSIBILITY_AUTO;
        mPendingAccessibilityState = PENDING_ACCESSIBILITY_STATE_NOT_SET;
        clearNestedRecyclerViewIfNotNested(this);
    }
    

    2.2、mAttachedScrap 或 mCachedViews 缓存

    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);
        }
    }
    

    二、总结

    • 1、从缓存池(RecycledViewPool)复用 ViewHolder 需要调用 onBindViewHodler。

    • 2、从屏幕外缓存(mCachedViews)复用 ViewHolder 不需要调用 onBindViewHodler。

    • 3、从 mCachedViews 中没有拿到 ViewHolder,需要调用 onCreateViewHolder 和 onBindViewHodler。

    相关文章

      网友评论

          本文标题:RecyclerView(二)

          本文链接:https://www.haomeiwen.com/subject/feeibltx.html