RecycleView缓存机制

手机软件开发 2024-9-22 12:13:08 67 0 来自 中国
recycleview代码一万三千多行,内部类26个,看起来感觉无从动手
然而,作为一个控件来讲,将onMeasure()和onLayout() 作为入口方法比力符合。其缓存复用机制,定会蕴藏此中。
先来看onMeasure方法
protected void onMeasure(int widthSpec, int heightSpec) {    if (mLayout == null) {        defaultOnMeasure(widthSpec, heightSpec);        return;    }    // 以上代码表明了为什么我们忘记设置layoutManger时,页面什么也展示不出来        if (mLayout.isAutoMeasureEnabled()) {       ...        final boolean measureSpecModeIsExactly =            widthMode == MeasureSpec.EXACTLY && heightMode == MeasureSpec.EXACTLY;        if (measureSpecModeIsExactly || mAdapter == null) {         // 假如丈量的是绝对值,则跳过onMeasure过程            return;        }        ...        if (mState.mLayoutStep == State.STEP_START) {            //mState.mLayoutStep的默认值为State.STEP_START            dispatchLayoutStep1();        }        ...        dispatchLayoutStep2();                //开始丈量条目        mLayout.setMeasuredDimensionFromChildren(widthSpec, heightSpec);        if (mLayout.shouldMeasureTwice()) {            ...            //假如RecycleView没有确定的宽高,大概至少有一个条目没有确切的宽高,则须要二次丈量            dispatchLayoutStep2();            ...        }    } else {        if (mHasFixedSize) {            mLayout.onMeasure(mRecycler, mState, widthSpec, heightSpec);            return;        }        ...        mLayout.onMeasure(mRecycler, mState, widthSpec, heightSpec);        ...        mState.mInPreLayout = false; // clear    }}mState.mLayoutStep的默认值为State.STEP_START,以是,步伐会先实行dispatchLayoutStep1()方法,然后实行dispatchLayoutStep2(), 假如RecycleView没有确定的宽高,大概至少有一个条目没有确切的宽高,则再实行一次dispatchLayoutStep2()方法。
先来看dispatchLayoutStep1(),这个方法表明很明白,重要举行一些和动画相干的操纵,以及举行一些预布局。当该方法实行完成后,会将状态改为State.STEP_LAYOUT
/** * The first step of a layout where we; * - process adapter updates * - decide which animation should run * - save information about current views * - If necessary, run predictive layout and save its information */private void dispatchLayoutStep1() {    mState.assertLayoutStep(State.STEP_START);    fillRemainingScrollValues(mState);    mState.mIsMeasuring = false;    startInterceptRequestLayout();    mViewInfoStore.clear();    onEnterLayoutOrScroll();    processAdapterUpdatesAndSetAnimationFlags();    saveFocusInfo();    mState.mTrackOldChangeHolders = mState.mRunSimpleAnimations && mItemsChanged;    mItemsAddedOrRemoved = mItemsChanged = false;    mState.mInPreLayout = mState.mRunPredictiveAnimations;    mState.mItemCount = mAdapter.getItemCount();    findMinMaxChildLayoutPositions(mMinMaxLayoutPositions);    if (mState.mRunSimpleAnimations) {        ...         // 找到没有被remove的ItemView,生存OldViewHolder信息,预备预布局    }    if (mState.mRunPredictiveAnimations) {        ...        // 举行预布局    } else {        clearOldPositions();    }    onExitLayoutOrScroll();    stopInterceptRequestLayout(false);    mState.mLayoutStep = State.STEP_LAYOUT;}可以看出,dispatchLayoutStep1()方法,并没有真正举行条目的丈量任务。
再来看dispatchLayoutStep2()方法
/** * The second layout step where we do the actual layout of the views for the final state. * This step might be run multiple times if necessary (e.g. measure). */private void dispatchLayoutStep2() {    startInterceptRequestLayout();    onEnterLayoutOrScroll();    mState.assertLayoutStep(State.STEP_LAYOUT | State.STEP_ANIMATIONS);    mAdapterHelper.consumeUpdatesInOnePass();    mState.mItemCount = mAdapter.getItemCount();    mState.mDeletedInvisibleItemCountSincePreviousLayout = 0;    // Step 2: Run layout    mState.mInPreLayout = false;    mLayout.onLayoutChildren(mRecycler, mState);    mState.mStructureChanged = false;    mPendingSavedState = null;    // onLayoutChildren may have caused client code to disable item animations; re-check    mState.mRunSimpleAnimations = mState.mRunSimpleAnimations && mItemAnimator != null;    mState.mLayoutStep = State.STEP_ANIMATIONS;    onExitLayoutOrScroll();    stopInterceptRequestLayout(false);}在这个方法内里,重要丈量方法为mLayout.onLayoutChildren(mRecycler, mState),实际丈量条目的布局,可以看出,实际丈量方法委托给了LayoutManger,以LinearLayoutManger为例来看一下详细实现:
public void onLayoutChildren(RecyclerView.Recycler recycler, RecyclerView.State state) {    ...    // 省略探求锚点和判定添补方向    if (mAnchorInfo.mLayoutFromEnd) {        firstLayoutDirection = mShouldReverseLayout ? LayoutState.ITEM_DIRECTION_TAIL                : LayoutState.ITEM_DIRECTION_HEAD;    } else {        firstLayoutDirection = mShouldReverseLayout ? LayoutState.ITEM_DIRECTION_HEAD                : LayoutState.ITEM_DIRECTION_TAIL;    }    ...    mLayoutState.mNoRecycleSpace = 0;    if (mAnchorInfo.mLayoutFromEnd) {        // 从下往上添补        ...        fill(recycler, mLayoutState, state, false);        endOffset = mLayoutState.mOffset;        if (mLayoutState.mAvailable > 0) {            // end could not consume all. add more items towards start            extraForStart = mLayoutState.mAvailable;            updateLayoutStateToFillStart(firstElement, startOffset);            mLayoutState.mExtraFillSpace = extraForStart;            fill(recycler, mLayoutState, state, false);            startOffset = mLayoutState.mOffset;        }    } else {        // 从上往下添补,逻辑与上类似        ...    }    // changes may cause gaps on the UI, try to fix them.    ...    mLastStackFromEnd = mStackFromEnd;}这里实际添补条目的为fill()函数,看一下fill函数的实现。fill函数界说在详细的LayoutManger中,这里展示的为LinearLayoutManger中的fill函数。
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;    if (layoutState.mScrollingOffset != LayoutState.SCROLLING_OFFSET_NaN) {        // 接纳不可见条目        recycleByLayoutState(recycler, layoutState);    }    int remainingSpace = layoutState.mAvailable + layoutState.mExtraFillSpace;    LayoutChunkResult layoutChunkResult = mLayoutChunkResult;    while ((layoutState.mInfinite || remainingSpace > 0) && layoutState.hasMore(state)) {        //recycleview另有剩余空间,并且另有条目的时间,盘算条目的巨细以及间距信息,并且更新剩余空间        layoutChunkResult.resetInternal();        ...        layoutState.mOffset += layoutChunkResult.mConsumed * layoutState.mLayoutDirection;        if (!layoutChunkResult.mIgnoreConsumed || layoutState.mScrapList != null                || !state.isPreLayout()) {            layoutState.mAvailable -= layoutChunkResult.mConsumed;            remainingSpace -= layoutChunkResult.mConsumed;        }        if (layoutState.mScrollingOffset != LayoutState.SCROLLING_OFFSET_NaN) {            layoutState.mScrollingOffset += layoutChunkResult.mConsumed;            recycleByLayoutState(recycler, layoutState);        }    }    return start - layoutState.mAvailable;}可以看出,fill函数除了丈量空间巨细之外,还举行了条目控件的接纳工作,看一下recycleByLayoutState的实现
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);    }}以recycleViewsFromEnd为例看一下
private void recycleViewsFromEnd(RecyclerView.Recycler recycler, int scrollingOffset,        int noRecycleSpace) {    final int childCount = getChildCount();        final int limit = mOrientationHelper.getEnd() - scrollingOffset + noRecycleSpace;    if (mShouldReverseLayout) {        for (int i = 0; i < childCount; i++) {            View child = getChildAt(i);            if (mOrientationHelper.getDecoratedStart(child) < limit                    || mOrientationHelper.getTransformedStartWithDecoration(child) < limit) {                // stop here                recycleChildren(recycler, 0, i);                return;            }        }    } else {        for (int i = childCount - 1; i >= 0; i--) {            View child = getChildAt(i);            if (mOrientationHelper.getDecoratedStart(child) < limit                    || mOrientationHelper.getTransformedStartWithDecoration(child) < limit) {                // stop here                recycleChildren(recycler, childCount - 1, i);                return;            }        }    }}接纳工作焦点调用了recycleChildren方法
public void removeAndRecycleViewAt(int index, @NonNull Recycler recycler) {    final View view = getChildAt(index);    removeViewAt(index);    recycler.recycleView(view);}终于到了缓存接纳的焦点代码recycler.recycleView(view),这里调用的是Recycler中的recycleView()方法,缓存的服用与接纳,都是在RecycleView的内部类Recycler中控制的,接纳的时间,会扫除view的动画,标志等。
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();    }    recycleViewHolderInternal(holder);    }看下面这段代码
if (holder.isScrap()) {    holder.unScrap();}void unScrap() {    mScrapContainer.unscrapView(this);}终极调用
void unscrapView(ViewHolder holder) {    if (holder.mInChangeScrap) {        mChangedScrap.remove(holder);    } else {        mAttachedScrap.remove(holder);    }    holder.mScrapContainer = null;    holder.mInChangeScrap = false;    holder.clearReturnedFromScrapFlag();}mAttachedScrap为第一级缓存,移除的时间,先从mAttachedScrap中移除
二级缓存的焦点代码为recycleViewHolderInternal(),Recycler中的缓存的接纳与复用,都是以ViewHolder为单元的,看下recycleViewHolderInternal函数的实现。
void recycleViewHolderInternal(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(ViewHolder.FLAG_INVALID                | ViewHolder.FLAG_REMOVED                | ViewHolder.FLAG_UPDATE                | ViewHolder.FLAG_ADAPTER_POSITION_UNKNOWN)) {            // Retire oldest cached view            int cachedViewSize = mCachedViews.size();            if (cachedViewSize >= mViewCacheMax && cachedViewSize > 0) {                //mViewCacheMax 默认值为2                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.add(targetCacheIndex, holder);            cached = true;        }        if (!cached) {            addViewHolderToRecycledViewPool(holder, true);            recycled = true;        }    } else {        ...    }    // even if the holder is not removed, we still call this method so that it is removed    // from view holder lists.    mViewInfoStore.removeViewHolder(holder);    if (!cached && !recycled && transientStatePreventsRecycling) {        holder.mOwnerRecyclerView = null;    }}第二级缓存 final ArrayList<ViewHolder> mCachedViews = new ArrayList<ViewHolder>();
        if (cachedViewSize >= mViewCacheMax && cachedViewSize > 0) {            //mViewCacheMax 默认值为2            recycleCachedViewAt(0);            cachedViewSize--;        }这段代码展示,假如mCachedViews缓存的是2的话,就把最早的一个移除,mCachedViews最大缓存为2
recycleCachedViewAt方法实现
void recycleCachedViewAt(int cachedViewIndex) {    ViewHolder viewHolder = mCachedViews.get(cachedViewIndex);    addViewHolderToRecycledViewPool(viewHolder, true);    mCachedViews.remove(cachedViewIndex);}mCachedViews移除的,会被添加到四级缓存中
void addViewHolderToRecycledViewPool(@NonNull 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);}RecycledViewPool mRecyclerPool;为四级缓存,为什么直接进入第四级缓存,由于,第三极缓存为用户自界说的缓存,一样平常情况下,客户端不会利用
public void putRecycledView(ViewHolder scrap) {    final int viewType = scrap.getItemViewType();    final ArrayList<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();    scrapHeap.add(scrap);}scrapHeap的add方法
public boolean add(E e) {    ensureCapacityInternal(size + 1);  // Increments modCount!!    elementData[size++] = e;    return true;}ensureCapacityInternal方法为
private void ensureCapacityInternal(int minCapacity) {    if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {        minCapacity = Math.max(DEFAULT_CAPACITY, minCapacity);    }    ensureExplicitCapacity(minCapacity);}这里默认的最大缓存数为10
到这里,添加缓存的方法就完成了,那什么时间利用呢?
回到LinearLayoutManger的fill方法中
int fill(RecyclerView.Recycler recycler, LayoutState layoutState,        RecyclerView.State state, boolean stopOnFocusable) {    ...    LayoutChunkResult layoutChunkResult = mLayoutChunkResult;    while ((layoutState.mInfinite || remainingSpace > 0) && layoutState.hasMore(state)) {        ...        layoutChunk(recycler, state, layoutState, layoutChunkResult);        ...    }}利用的地方在layoutChunk方法中
void layoutChunk(RecyclerView.Recycler recycler, RecyclerView.State state,        LayoutState layoutState, LayoutChunkResult result) {    View view = layoutState.next(recycler);    if (view == null) {        ...        return;    }        RecyclerView.LayoutParams params = (RecyclerView.LayoutParams) view.getLayoutParams();    if (layoutState.mScrapList == null) {        if (mShouldReverseLayout == (layoutState.mLayoutDirection                == LayoutState.LAYOUT_START)) {            addView(view);        } else {            addView(view, 0);        }    } else {        if (mShouldReverseLayout == (layoutState.mLayoutDirection                == LayoutState.LAYOUT_START)) {            addDisappearingView(view);        } else {            addDisappearingView(view, 0);        }    }    //以下为实际丈量子条目    measureChildWithMargins(view, 0, 0);    result.mConsumed = mOrientationHelper.getDecoratedMeasurement(view);    int left, top, right, bottom;    if (mOrientation == VERTICAL) {        if (isLayoutRTL()) {            right = getWidth() - getPaddingRight();            left = right - mOrientationHelper.getDecoratedMeasurementInOther(view);        } else {            left = getPaddingLeft();            right = left + mOrientationHelper.getDecoratedMeasurementInOther(view);        }        if (layoutState.mLayoutDirection == LayoutState.LAYOUT_START) {            bottom = layoutState.mOffset;            top = layoutState.mOffset - result.mConsumed;        } else {            top = layoutState.mOffset;            bottom = layoutState.mOffset + result.mConsumed;        }    } else {        top = getPaddingTop();        bottom = top + mOrientationHelper.getDecoratedMeasurementInOther(view);        if (layoutState.mLayoutDirection == LayoutState.LAYOUT_START) {            right = layoutState.mOffset;            left = layoutState.mOffset - result.mConsumed;        } else {            left = layoutState.mOffset;            right = layoutState.mOffset + result.mConsumed;        }    }    // We calculate everything with View's bounding box (which includes decor and margins)    // To calculate correct layout position, we subtract margins.    layoutDecoratedWithMargins(view, left, top, right, bottom);    if (DEBUG) {        Log.d(TAG, "laid out child at position " + getPosition(view) + ", with l:"                + (left + params.leftMargin) + ", t:" + (top + params.topMargin) + ", r:"                + (right - params.rightMargin) + ", b:" + (bottom - params.bottomMargin));    }    // Consume the available space if the view is not removed OR changed    if (params.isItemRemoved() || params.isItemChanged()) {        result.mIgnoreConsumed = true;    }    result.mFocusable = view.hasFocusable();}获取缓存的焦点代码为
View next(RecyclerView.Recycler recycler) {    if (mScrapList != null) {        return nextViewFromScrapList();    }    final View view = recycler.getViewForPosition(mCurrentPosition);    mCurrentPosition += mItemDirection;    return view;}实际获取地方为
final View view = recycler.getViewForPosition(mCurrentPosition);
终极调用了tryGetViewHolderForPositionByDeadline 方法
ViewHolder tryGetViewHolderForPositionByDeadline(int position,        boolean dryRun, long deadlineNs) {    ViewHolder holder = null;    // 0)     if (mState.isPreLayout()) {        holder = getChangedScrapViewForPosition(position);        fromScrapOrHiddenOrCache = holder != null;    }    // 1) Find by position from scrap/hidden list/cache    if (holder == null) {        holder = getScrapOrHiddenOrCachedHolderForPosition(position, dryRun);        if (holder != null) {            if (!validateViewHolderForOffsetPosition(holder)) {                // recycle holder (and unscrap if relevant) since it can't be used                if (!dryRun) {                    ...                    recycleViewHolderInternal(holder);                }                holder = null;            } else {                fromScrapOrHiddenOrCache = true;            }        }    }    if (holder == null) {        final int offsetPosition = mAdapterHelper.findPositionOffset(position);               final int type = mAdapter.getItemViewType(offsetPosition);        // 2) Find from scrap/cache via stable ids, if exi        ...        }        if (holder == null) { // fallback to pool            holder = getRecycledViewPool().getRecycledView(type);            ...        }        ...    }    ...    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);    }    ...    return holder;}以上就是查找缓存的地方
您需要登录后才可以回帖 登录 | 立即注册

Powered by CangBaoKu v1.0 小黑屋藏宝库It社区( 冀ICP备14008649号 )

GMT+8, 2024-10-18 20:26, Processed in 0.159559 second(s), 32 queries.© 2003-2025 cbk Team.

快速回复 返回顶部 返回列表