A cache ViewHolder in RecyclerView is a cache ViewHolder in RecyclerView. Android source code analysis RecyclerView level four cache reuse mechanism a (cache ViewHolder) list view has been occupying an important position in native development, No matter before ListView or RecyclerView now, no matter the real performance or use function has a huge advantage, the most important one is actually for view reuse mechanism. From RecycleBin of ListView to Recycler of RecyclerView, Google’s design of cache for ListView has been very careful and worthy of our study and research. H5 and RN on web pages have poor rendering performance for complex list views. This article takes a look at the reuse of a ViewHolder. A cached ViewHolder must be reused to make sense of the cache.

1. General flow chart

Put a piece ofA blog post by BuglyRecyclerView of the cache process diagram (their own drawing is also about the same, directly used… If delete)

2. Invoke method tracing

When does recyclerView reuse the cached ViewHolder, no doubt while recyclerView is rolling. RecyclerView triggers reuse while scrolling.

// 1.RecyclerView scrollBy()
@Override
public void scrollBy(int x, int y) {
        / /...
        final boolean canScrollHorizontal = mLayout.canScrollHorizontally();
        final boolean canScrollVertical = mLayout.canScrollVertically();
        // Slide horizontally and vertically to enter if
        if (canScrollHorizontal || canScrollVertical) {
            scrollByInternal(canScrollHorizontal ? x : 0, canScrollVertical ? y : 0.null); }}// 2. ScrollByInternal () of RecyclerView
boolean scrollByInternal(int x, int y, MotionEvent ev) {
        int unconsumedX = 0, unconsumedY = 0;
        int consumedX = 0, consumedY = 0;

        consumePendingUpdateOperations();
        if(mAdapter ! =null) {
            eatRequestLayout();
            onEnterLayoutOrScroll();
            TraceCompat.beginSection(TRACE_SCROLL_TAG);
            if(x ! =0) {
                // Swipe horizontally to call scrollHorizontallyBy of LayoutManager
                consumedX = mLayout.scrollHorizontallyBy(x, mRecycler, mState);
                unconsumedX = x - consumedX;
            }
            if(y ! =0) {
                // Scroll vertically to call scrollVerticallyBy of LayoutManager
                consumedY = mLayout.scrollVerticallyBy(y, mRecycler, mState);
                unconsumedY = y - consumedY;
            }
            / /...
        }
        / /...
}

/ / 3. RecyclerView. LayoutManager scrollVerticallyBy () method in this paper, we analysis the longitudinal sliding just the same really
public int scrollVerticallyBy(int dy, Recycler recycler, State state) {
     return 0;
}

// 4.LinearLayoutManager overrides the scrollVerticallyBy() method of LayoutManager
@Override
public int scrollVerticallyBy(int dy, RecyclerView.Recycler recycler,RecyclerView.State state) {
    if (mOrientation == HORIZONTAL) {
        return 0;
    }
    return scrollBy(dy, recycler, state);
}

/ / 5. LinearLayoutManager scrollBy ()
int scrollBy(int dy, RecyclerView.Recycler recycler, RecyclerView.State state) {
        / /...
        // Call the fill method here
        final int consumed = mLayoutState.mScrollingOffset
                + fill(recycler, mLayoutState, state, false);
        / /...
        final int scrolled = absDy > consumed ? layoutDirection * consumed : dy;
        mOrientationHelper.offsetChildren(-scrolled);
        / /...
        mLayoutState.mLastScrollDelta = scrolled;
        return scrolled;
}
/ / 6. LinearLayoutManager the fill ()
int fill(RecyclerView.Recycler recycler, LayoutState layoutState,
            RecyclerView.State state, boolean stopOnFocusable) {
        / /...
        while ((layoutState.mInfinite || remainingSpace > 0) && layoutState.hasMore(state)) {
            / /...
            // Call the appropriate collection method based on the current layout direction
            layoutChunk(recycler, state, layoutState, layoutChunkResult);
            / /...
        }
        / /...
    }
 / / 7. LinearLayoutManager layoutChunk ()
 void layoutChunk(RecyclerView.Recycler recycler, RecyclerView.State state,LayoutState layoutState, LayoutChunkResult result) {
        // Pass recycler to retrieve views
        View view = layoutState.next(recycler);
        / /...
        if (layoutState.mScrapList == null) {
            if (mShouldReverseLayout == (layoutState.mLayoutDirection== LayoutState.LAYOUT_START)) {
                // Add the obtained view to recyclerView
                addView(view);
            } else {
                addView(view, 0); }}/ /...
    }
 / / 8. LinearLayoutManager next ()
 View next(RecyclerView.Recycler recycler) {
      if(mScrapList ! =null) {
           return nextViewFromScrapList();
      }
      // Get the view by position
      final View view = recycler.getViewForPosition(mCurrentPosition);
      mCurrentPosition += mItemDirection;
      return view;
}
/ / 9. RecyclerView. Recycler getViewForPosition ()
View getViewForPosition(int position, boolean dryRun) {
     return tryGetViewHolderForPositionByDeadline(position, dryRun, FOREVER_NS).itemView;
}   
Copy the code

Review the method invocation

Entry: Slide Move events - > scrollByInternal - > scrollStep - > mLayout. ScrollVerticallyBy - > scrollBy - > fill - > layoutChunk - > layoutState.next --> addView(view); layoutState.next--> getViewForPosition --> tryGetViewHolderForPositionByDeadlineCopy the code

After a series of method calls, we call the RecyclerView. Recycler of tryGetViewHolderForPositionByDeadline () method to get the level 4 in the cache ViewHolder and then take the view, It is then added to recyclerView in the addView(view) method in layoutChunk().

The following through the source code to verify 1

3. Core source code analysis

ViewHolder tryGetViewHolderForPositionByDeadline(int position,
                boolean dryRun, long deadlineNs) {
            / /... All kinds of judgment
            boolean fromScrapOrHiddenOrCache = false;
            ViewHolder holder = null;
            if (mState.isPreLayout()) {
                // 1. This corresponds to the level 1 cache -- mChangeScrap animation-related ViewHolderholder = getChangedScrapViewForPosition(position); fromScrapOrHiddenOrCache = holder ! =null;
            }
            // If level 1 cache does not have a corresponding holder
            if (holder == null) {
                //2. MAttachedScrap and mCachedViews are used to store ViewHolder off-screen
                holder = getScrapOrHiddenOrCachedHolderForPosition(position, dryRun);
                if(holder ! =null) {
                    if(! validateViewHolderForOffsetPosition(holder)) {if(! dryRun) {/ /... A pile of judgment
                            // Recycle method
                            recycleViewHolderInternal(holder);
                        }
                        holder = null;
                    } else {
                        fromScrapOrHiddenOrCache = true; }}}if (holder == null) {
                / /...
                
                final int type = mAdapter.getItemViewType(offsetPosition);
                if (mAdapter.hasStableIds()) {
                    // The ViewHolder obtained by mAttachedScrap and mCachedViews according to (ViewType, ItemID)
                    holder = getScrapOrCachedViewForId(mAdapter.getItemId(offsetPosition),
                            type, dryRun);
                    if(holder ! =null) {
                        holder.mPosition = offsetPosition;
                        fromScrapOrHiddenOrCache = true; }}if (holder == null&& mViewCacheExtension ! =null) {
                //4. The corresponding level of cache is the custom cache. The ViewHolder
                    final View view = mViewCacheExtension
                            .getViewForPositionAndType(this, position, type);
                    if(view ! =null) {
                        holder = getChildViewHolder(view);
                        / /...}}if (holder == null) { // fallback to pool
                    / /...
                    //5. The corresponding level 4 cache -- fetch the ViewHolder from the buffer pool
                    holder = getRecycledViewPool().getRecycledView(type);
                    if(holder ! =null) {
                        holder.resetInternal();
                        if(FORCE_INVALIDATE_DISPLAY_LIST) { invalidateDisplayListInt(holder); }}}if (holder == null) {
                    long start = getNanoTime();
                    / /...
                    CreateViewHolder () creates a ViewHolder
                    holder = mAdapter.createViewHolder(RecyclerView.this, type);
                    / /...
                }
                / /...
             return holder;
        }
Copy the code

So far the general source code analysis of the article is finished, if you want to further understand more recyclerView recycling more details, you can read the source code.