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;}以上就是查找缓存的地方 |