Do you understand how recyclerView reuse, want to explore together, stripping the mystery of recyclerView?

1. One swipe

In order to find out how recyclerView can be reused, start with the most common scenario, finger swiping screen.

1.1. Start at the beginning of the event

When the finger touches the screen, it first triggers onTouchEvet(), calls scrollByInternal() in the ACTION_MOVE event, and then continues passing into scrollStep().

In scrollStep (), the scrolling delegate is dispatched directly to the LayoutManger handling.

class RecyclerView { LayoutManager mLayout; .public boolean onTouchEvent(MotionEvent e) {...switch (action) {
            ...
            case MotionEvent.ACTION_MOVE: {
                ...
                int dx = mLastTouchX - x;
                int dy = mLastTouchY - y;
                if (scrollByInternal(
                        canScrollHorizontally ? dx : 0,
                        canScrollVertically ? dy : 0,
                        e)) {
                    getParent().requestDisallowInterceptTouchEvent(true); }}break; }}boolean scrollByInternal(int x, int y, MotionEvent ev) {...if(mAdapter ! =null) {... scrollStep(x, y, mReusableIntPair); }}void scrollStep(int dx, int dy, @Nullable int[] consumed) {...if(dx ! =0) {
            consumedX = mLayout.scrollHorizontallyBy(dx, mRecycler, mState);
        }
        if(dy ! =0) { consumedY = mLayout.scrollVerticallyBy(dy, mRecycler, mState); }}}Copy the code

1.2. Handle scrolling

Let’s take a LinearLayoutManager that is vertical, sliding up. So let’s go down and see how it works.

First LinearLayoutManager. ScrollVerticallyBy (), judgment is vertical, then back to the scrollBy ().

public int scrollVerticallyBy(int dy, RecyclerView.Recycler recycler,
                              RecyclerView.State state) {
    if (mOrientation == HORIZONTAL) {
        return 0;
    }
    return scrollBy(dy, recycler, state);
}
Copy the code

Pass to scrollBy() to finally handle scrolling:

  1. First, dy is decomposed into direction and offset values, where direction corresponds to two values, namely LAYOUT_END=1 and LAYOUT_START=-1. So we said that the slide up that we’re looking at so far, dy > 0, is LAYOUT_END.

  2. Call updateLayoutState(), which assigns values to the parameters in LayoutState, which is the configuration parameter for how to populate.

  3. Calculate consumed: calculates the rolling distance supported by RecyclerView. Fill () is used to control the creation, recycling, and reuse of entries.

  4. Finally is the Children doing migration, mOrientationHelper offsetChildren () will eventually returned to the RecyclerView do displacement item for all tables. It consumed in RecyclerView. The actual distance is the smaller of the two. It consumed in RecyclerView.

int scrollBy(int delta, RecyclerView.Recycler recycler, RecyclerView.State state) {...final int layoutDirection = delta > 0 ? LayoutState.LAYOUT_END : LayoutState.LAYOUT_START;
    final int absDelta = Math.abs(delta);
    
    updateLayoutState(layoutDirection, absDelta, true, state);
    
    final int consumed = mLayoutState.mScrollingOffset
            + fill(recycler, mLayoutState, state, false);
    
    final int scrolled = absDelta > consumed ? layoutDirection * consumed : delta;
    mOrientationHelper.offsetChildren(-scrolled);
}
Copy the code

Go back to updateLayoutState(), compute a few key values, and assign values to the parameters in LayoutState.

  1. layoutDirection: The direction of the scroll, calculated previously, assigned directly.
  2. scrollingOffset: The distance to scroll without adding a new subitem. Now we’re looking at swiping up, and this is the off-screen height of the lowest subitem. That’s the bottom position of the bottom subtermRecyclerViewThe difference at the bottom.
  3. available: Height to be filled. Sliding distance andscrollingOffset(may be negative).
private void updateLayoutState(int layoutDirection, int requiredSpace,
                               boolean canUseExistingSpace, RecyclerView.State state) {... mLayoutState.mLayoutDirection = layoutDirection;boolean layoutToEnd = layoutDirection == LayoutState.LAYOUT_END;
    int scrollingOffset;
    if (layoutToEnd) {
        ...
        final View child = getChildClosestToEnd();
        mLayoutState.mOffset = mOrientationHelper.getDecoratedEnd(child);
        scrollingOffset = mOrientationHelper.getDecoratedEnd(child)
                - mOrientationHelper.getEndAfterPadding();

    }
    mLayoutState.mAvailable = requiredSpace;
    if (canUseExistingSpace) {
        mLayoutState.mAvailable -= scrollingOffset;
    }
    mLayoutState.mScrollingOffset = scrollingOffset;
}
Copy the code

We go back to fill(), which controls entry creation, recycling, reuse, and so on.

The recycleByLayoutState() and layoutChunk() functions are called recycleByLayoutState() and layoutChunk().

As you can see, we first call the collection method once, and then we enter a while loop, which is conditional on the height to be filled and the control to be filled. The process in the loop can be divided into three steps:

  1. Add the next control,layoutChunk().
  2. recalculateavailable.
  3. Recycling,recycleByLayoutState().

The scrollingOffset can be rolled without adding a new subitem. The scrollingOffset can be rolled without adding a new subitem. Need to update my understanding here, scrollingOffset is loop every time I add item to calculate the actual rolling distance, also is a time when every add item, sliding distance and support the rolling distance of the minimum value.

Finally, the return value of the function is converted to the sum of the heights of all the controls added to the loop, which is the height of the fill. Pass to the outside to calculate the distance to support scrolling.

int fill(RecyclerView.Recycler recycler, LayoutState layoutState,
         RecyclerView.State state, boolean stopOnFocusable) {...final int start = layoutState.mAvailable;
    if(layoutState.mScrollingOffset ! = LayoutState.SCROLLING_OFFSET_NaN) {if (layoutState.mAvailable < 0) {
            layoutState.mScrollingOffset += layoutState.mAvailable;
        }
        recycleByLayoutState(recycler, layoutState);
    }
    int remainingSpace = layoutState.mAvailable + layoutState.mExtraFillSpace;
    LayoutChunkResult layoutChunkResult = mLayoutChunkResult;
    while ((layoutState.mInfinite || remainingSpace > 0) && layoutState.hasMore(state)) 
        ...
        //step0
        layoutChunk(recycler, state, layoutState, layoutChunkResult);
        //step1
        layoutState.mAvailable -= layoutChunkResult.mConsumed;
        remainingSpace -= layoutChunkResult.mConsumed;
        //step2
        if(layoutState.mScrollingOffset ! = LayoutState.SCROLLING_OFFSET_NaN) { layoutState.mScrollingOffset += layoutChunkResult.mConsumed;if (layoutState.mAvailable < 0) { layoutState.mScrollingOffset += layoutState.mAvailable; } recycleByLayoutState(recycler, layoutState); }}return start - layoutState.mAvailable;
}
Copy the code

1.3. Recycling

RecycleByLayoutState (); recycleByLayoutState(); recycleByLayoutState();

  1. recycleByLayoutState()According to the direction of the scroll, distributed to different methods, the analysis mentioned above is the upward slide, the corresponding method isrecycleViewsFromStart(). The actual rolling distance of each time analyzed above is also included herescrollingOffsetThe value of is passed on.
  2. recycleViewsFromStart()Iterate over the child controls from the beginning, comparing the bottom position of the control to the actual scroll length, and find the first control whose bottom position is larger than the scroll length, indicating that these controls will roll off the screen and jump to the next methodrecycleChildren().
  3. recycleChildren()To iterate over the control selected in the previous step. In this case, we use the position of the control found in the previous step minus one. In other words, we recycle the control whose bottom position is less than the scroll length.
  4. And finally it’s calledremoveAndRecycleViewAt()Which are called separatelyremoveMethod to remove the control, and the callRecyclerThe control on the line recycling. And you see here,RecyclerViewThe collection operation is delegated to the inner classRecyclerView.RecyclerThe next section will delve into this class.
private void recycleByLayoutState(RecyclerView.Recycler recycler, LayoutState layoutState) {...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) {...final int limit = scrollingOffset - noRecycleSpace;
    final int childCount = getChildCount();
    for (int i = 0; i < childCount; i++) {
        View child = getChildAt(i);
        if(mOrientationHelper.getDecoratedEnd(child) > limit || mOrientationHelper.getTransformedEndWithDecoration(child) > limit)  { recycleChildren(recycler,0, i);
            return; }}}private void recycleChildren(RecyclerView.Recycler recycler, int startIndex, int endIndex) {
    if (startIndex == endIndex) {
        return; }...if (endIndex > startIndex) {
        for (int i = endIndex - 1; i >= startIndex; i--) { removeAndRecycleViewAt(i, recycler); }}}public void removeAndRecycleViewAt(int index, @NonNull Recycler recycler) {
    final View view = getChildAt(index);
    removeViewAt(index);
    recycler.recycleView(view);
}
Copy the code

Add 1.4.

Step back and take a final look at how layoutChunk() adds controls. We need to have RecyclerView. RecyclerView. Recyclerer. Once the entry is obtained, it is added and the measure and Layout processes are performed.

void layoutChunk(RecyclerView.Recycler recycler, RecyclerView.State state, LayoutState layoutState, LayoutChunkResult result) {... View view = layoutState.next(recycler);//add
    addView(view);
    //measure
    measureChildWithMargins(view, 0.0);
    result.mConsumed = mOrientationHelper.getDecoratedMeasurement(view);
    //layout
    layoutDecoratedWithMargins(view, left, top, right, bottom);
}

/ / LayoutState method
View next(RecyclerView.Recycler recycler) {...final View view = recycler.getViewForPosition(mCurrentPosition);
    return view;
}
Copy the code

Think 1.5.

Source read here, the heart can not help but draw a huge question mark, this is not in the scroll? Why start recycling before you start rolling? After that, the scrolling operation will have a part of the blank display??

To answer that question, we first have to answer another question, how do we do continuous operations like scrolling or animating? The continuities, in the computer world, are discrete values sampled from continuous values. In other words, the scrolling is not continuous, from the display is a frame of a rapid transformation of the picture composition, from the operation is a fast scan position composition.

Going back to the original question, is this really rolling? The dy that we get is the displacement of the finger that we get over a sampling period, and what we’re going to do is instead of continuously playing the scroll animation from the first position to the next position, we’re going to switch directly from the first position to the second position based on the scroll distance dy.

So the operation of scrolling is actually a simple one by one offset operation, which reduces the continuous action to a transformation of two transient styles.

This explains why, before scrolling, the top control is recycled according to the scrolling distance. This is just preparation for the next transient pattern.

At this point, the whole process of a scroll is over. But that leaves a bigger problem, where you end up going into RecyclerView. Recyclerer, and then you go into it and see how it works.

2. RecyclerView.Recycler

We have RecyclerView. We have RecyclerView. We have recyclerer.

To help you read more smoothly, here are some conclusions from the analysis.

Firstly, the caches used in the REcycler are divided into scrap, cache, pool and extension. These four levels of cache are stored in these five objects:

  • attachedScrap: Recycles and reuses screen entries during the layout. During the layout, all entries are reclaimed before being added. There is no upper limit on the number of entries. Generally, the number of entries is all visible entries on the screen. Will add (notifyItemInserted()), delete (notifyItemRemoved()), move (notifyItemMoved()) and modify (notifyItemChanged()) table entry.
  • changedScrap: During the layout process, the screen is visible and changed. Will only store changes (notifyItemChanged()). It will also be cleared at the end of the layout.
  • cachedViews: The maximum number of entries is 2 by default. In the first-in, first-out (FIFO) queue structure, when new entries need to be saved but the number reaches the upper limit, the earliest entry will be savedrecyclerPool. The equivalent ofrecyclerPoolOf the standby queue.
  • recyclerPool: according to theviewTypeThe maximum number of storage types is 5 by default.
  • viewCacheExtension: Cache extension. Users can customize the cache policy.

When adding, deleting, and modifying entries, the RecyclerView is laid out twice, the first time for all entries and the second time for the changed state. Determine how to animate based on the difference between the two layouts. The first layout here, preLayout.

With that in mind, let’s first take a look at reuse methods.

2.1. Reuse

Reuse method according to the previous section recycler. GetViewForPosition (), check the call chain will go tryGetViewHolderForPositionByDeadline (), again turn head to see, all the creation method of the last call to this method. Let’s just start with this method.

Six attempts are made in this method to get the entry used.

  1. getChangedScrapViewForPosition()To try andchangeScrapIn the search.
  2. getScrapOrHiddenOrCachedHolderForPosition(), try to passpositioninattachedScrap, hidden entries, andcachedViewIn the search.
  3. getScrapOrCachedViewForId(), try to passidinattachedScrap,cachedViewIn the search.
  4. Try to throughviewCacheExtensionThe callback searches from the custom cache.
  5. Try to throughrecyclerPoolGets the entry.
  6. Create an entry directly.

Ignore custom caches and direct creation here, explore the rest, and move on.

ViewHolder tryGetViewHolderForPositionByDeadline(int position, boolean dryRun, long deadlineNs) {...boolean fromScrapOrHiddenOrCache = false;
    ViewHolder holder = null;
    // 0) Try searching from changedScrap
    if(mState.isPreLayout()) { holder = getChangedScrapViewForPosition(position); fromScrapOrHiddenOrCache = holder ! =null;
    }
    // 1) Search attachedScrap, hidden table entries, and cachedView by position
    if (holder == null) {
        holder = getScrapOrHiddenOrCachedHolderForPosition(position, dryRun);
        if(holder ! =null) {
            if(! validateViewHolderForOffsetPosition(holder)) { ... holder =null;
            } else {
                fromScrapOrHiddenOrCache = true; }}}if (holder == null) {
        final int offsetPosition = mAdapterHelper.findPositionOffset(position);
        final int type = mAdapter.getItemViewType(offsetPosition);
        // 2) Try searching attachedScrap, cachedView by ID
        if (mAdapter.hasStableIds()) {
            holder = getScrapOrCachedViewForId(mAdapter.getItemId(offsetPosition),
                    type, dryRun);
            if(holder ! =null) {
                // update position
                holder.mPosition = offsetPosition;
                fromScrapOrHiddenOrCache = true; }}// 3) Try searching in viewCacheExtension
        if (holder == null&& mViewCacheExtension ! =null) {
            final View view = mViewCacheExtension
                    .getViewForPositionAndType(this, position, type);
            if(view ! =null) { holder = getChildViewHolder(view); }}// 4) Try searching in recyclerPool
        if (holder == null) {
            holder = getRecycledViewPool().getRecycledView(type);
            if(holder ! =null) { holder.resetInternal(); }}// 5) Create directly
        if (holder == null) {
            holder = mAdapter.createViewHolder(this, type); }}...return holder;
}
Copy the code

2.1.1. getChangedScrapViewForPosition()

Try searching in changeScrap.

As you can see from the previous step, entering this method must be preLayout. In combination with changeScrap, only modified entries are stored, so this entry can only reuse modified entries during preLayout.

The logic is to search through changeScrap by position and then id.

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);returnholder; }}// find by id
    if (mAdapter.hasStableIds()) {
        ...
        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);returnholder; }}}return null;
}
Copy the code

2.1.2. getScrapOrHiddenOrCachedHolderForPosition()

First search attchedScrap by position. Then check to see if there are any hidden entries in the current position. Finally, search the cachedView by position.

ViewHolder getScrapOrHiddenOrCachedHolderForPosition(int position, boolean dryRun) {

    // Try first for an exact, non-invalid match from scrap.
    final int scrapCount = mAttachedScrap.size();
    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);returnholder; }}if(! dryRun) { View view = mChildHelper.findHiddenNonRemovedView(position);if(view ! =null) {... vh.addFlags(ViewHolder.FLAG_RETURNED_FROM_SCRAP | ViewHolder.FLAG_BOUNCED_FROM_HIDDEN_LIST);returnvh; }}// 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);
        if(! holder.isInvalid() && holder.getLayoutPosition() == position && ! holder.isAttachedToTransitionOverlay()) { ...returnholder; }}return null;
}
Copy the code

2.1.3. getScrapOrCachedViewForId()

This method is very similar to the last one, replacing position with ID, and eliminating the need to look up hidden entries.

ViewHolder getScrapOrCachedViewForId(long id, int type, boolean dryRun) {
    // Look in our attached views first
    final int count = mAttachedScrap.size();
    for (int i = count - 1; i >= 0; i--) {
        final ViewHolder holder = mAttachedScrap.get(i);
        if(holder.getItemId() == id && ! holder.wasReturnedFromScrap()) {if (type == holder.getItemViewType()) {
                ...
                holder.addFlags(ViewHolder.FLAG_RETURNED_FROM_SCRAP);
                returnholder; }}}// Search the first-level cache
    final int cacheSize = mCachedViews.size();
    for (int i = cacheSize - 1; i >= 0; i--) {
        final ViewHolder holder = mCachedViews.get(i);
        if(holder.getItemId() == id && ! holder.isAttachedToTransitionOverlay()) {if (type == holder.getItemViewType()) {
                ...
                returnholder; }}}return null;
}
Copy the code

2.1.4. recyclerPool

In a reuse pool, different types are stored in different scrapData objects, and the maximum number of caches per scrapData is 5 by default.

Get a reused component from the reuse pool as long as the type matches and the component is not currently bound to the current RecyclerView.

public static class RecycledViewPool {
    private static final int DEFAULT_MAX_SCRAP = 5;
    
    static class ScrapData {
        final ArrayList<ViewHolder> mScrapHeap = new ArrayList<>();
        int mMaxScrap = DEFAULT_MAX_SCRAP;
        long mCreateRunningAverageNs = 0;
        long mBindRunningAverageNs = 0;
    }

    SparseArray<ScrapData> mScrap = new SparseArray<>();

    public ViewHolder getRecycledView(int viewType) {
        final RecycledViewPool.ScrapData scrapData = mScrap.get(viewType);
        if(scrapData ! =null && !scrapData.mScrapHeap.isEmpty()) {
            final ArrayList<ViewHolder> scrapHeap = scrapData.mScrapHeap;
            for (int i = scrapHeap.size() - 1; i >= 0; i--) {
                if(! scrapHeap.get(i).isAttachedToTransitionOverlay()) {returnscrapHeap.remove(i); }}}return null; }}Copy the code

2.2. Reuse

Analyze the previous section that several cache entries are there.

2.2.1. scrap

If you look at the call chains for attachScrap and changedScrap, you’ll see that both methods have only one place to call the add() method, and both are in the scrapView() method. In this method, you can see that in the judgment, there is a holder-.isupdated (). As mentioned earlier, changedScrap only stores the modified entries.

void scrapView(View view) {
    final ViewHolder holder = getChildViewHolderInt(view);
    if(holder.hasAnyOfTheFlags(ViewHolder.FLAG_REMOVED | ViewHolder.FLAG_INVALID) || ! holder.isUpdated() || canReuseUpdatedViewHolder(holder)) { ... mAttachedScrap.add(holder); }else {
        if (mChangedScrap == null) {
            mChangedScrap = newArrayList<ViewHolder>(); } mChangedScrap.add(holder); }}Copy the code

Continue to view the call up scrapView (), will go to LayoutManger. OnLayoutChildren (), in which all tables first recycling, and then call the fill () added, the fill () is to analyze the rolling time that the fill () method.

Then find a call up, onLayoutChildren () this method namely RecyclerView will call to the method in the process of layout, namely scrap cache data is added in the process of layout.

private void scrapOrRecycleView(Recycler recycler, int index, View view) {
    final ViewHolder viewHolder = getChildViewHolderInt(view);
    if(viewHolder.isInvalid() && ! viewHolder.isRemoved() && ! mRecyclerView.mAdapter.hasStableIds()) { ... }else{ detachViewAt(index); recycler.scrapView(view); }}public void detachAndScrapAttachedViews(@NonNull Recycler recycler) {
    final int childCount = getChildCount();
    for (int i = childCount - 1; i >= 0; i--) {
        finalView v = getChildAt(i); scrapOrRecycleView(recycler, i, v); }}public voidLinearLayoutManager.onLayoutChildren(Recycler recycler, State state) { ... detachAndScrapAttachedViews(recycler); . fill(); }Copy the code

Take a look at the chain of calls to attachScrap and changedScrap’s clear() methods. Except for some calls at initialization, the calls are only made at the end of the layout, which in combination determines that the life of the Scrap cache exists only during the layout process.

void clearScrap(a) {
    mAttachedScrap.clear();
    if(mChangedScrap ! =null) { mChangedScrap.clear(); }}void removeAndRecycleScrapInt(Recycler recycler) {... recycler.clearScrap(); }private void dispatchLayoutStep3(a) {... mLayout.removeAndRecycleScrapInt(mRecycler); }Copy the code

2.2.2. cach

Then see where cach cache is added, it is only an add () call, in recyclerViewHolderInternal () method, in this way can see before adding to determine the number, if you have more than the limit, The first entry is recycled into the recyclerPool.

void recycleViewHolderInternal(ViewHolder holder) {...boolean cached = false;
    boolean recycled = false;
    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) {
                recycleCachedViewAt(0); cachedViewSize--; }... mCachedViews.add(targetCacheIndex, holder); cached =true;
        }
        if(! cached) { addViewHolderToRecycledViewPool(holder,true);
            recycled = true; }}}void recycleCachedViewAt(int cachedViewIndex) {... ViewHolder viewHolder = mCachedViews.get(cachedViewIndex); addViewHolderToRecycledViewPool(viewHolder,true);
    mCachedViews.remove(cachedViewIndex);
}
Copy the code

Keep looking up the call chain and you’ll find the recyclerView() method mentioned in the analysis scroll.

public void recycleView(@NonNull View view) {... ViewHolder holder = getChildViewHolderInt(view); recycleViewHolderInternal(holder); }Copy the code

2.2.3. recyclerPool

On the analysis of the above cach cache, see recycling method to recyclerPool addViewHolderToRecycledViewPool (), call to the recyclerPool. PutRecycledView () method, Store entries in the collection of the corresponding type. If the number of entries has reached the upper limit, it is abandoned directly.

void addViewHolderToRecycledViewPool(@NonNull ViewHolder holder, boolean dispatchRecycled) {... getRecycledViewPool().putRecycledView(holder); }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;
    }
    scrap.resetInternal();
    scrapHeap.add(scrap);
}
Copy the code

Think 2.3.

  1. changeScrapIt’s a special case. It has to bepreLayoutThe steps, as well as the controls that must be changed, I understand are just rightattachedScrapI’m going to ignore that.
  2. attachedScrapThe life cycle is to recycle all visible components during the layout process and then add them again. If the location and data of the component are not changed at all during the process of adding the component, it will be reused. In this case, there is no need to reload the data of the component. There are no displayed components in the first layout, so only when you add, remove, move, or modify some components, and request a new layout, will all displayed components not be discarded.
  3. inscrapandcatchThere are two ways to search. One is bypositionandid.idIn my understanding is another unique identifier, such as tabular data and databaseidYou don’t normally use it, you just use itpositionSearch.
  4. cachedViewAs arecyclerPoolAnd their use is still much worse,cachedViewThe use of andattachedScrapIt’s similar. It’s a matchpositionAnd there are no updates to the data that can be reused, which iscachedViewAs arecyclerPoolThe main difference between the two,cachedViewReuse does not require rebinding data,recyclerPoolReuse requires rebinding the data.
  5. Thinking more deeply about the last one,cachedViewandrecyclerPoolIt is usually recycled during scrolling, and it stores entries that are rolled off the screencachedViewCan only pass throughpositionMatch, that is to saycachedViewIn this case, the entry that is rolled off the screen is rolled back to the screen again.

3. Refer to the article

  1. RecyclerView caching mechanism | how to reuse table?
  2. RecyclerView interview questions | rolling table when the item is being filled or recycled?
  3. RecyclerView animation principle | pre – layout, post – the relationship between the layout and scrap the cache
  4. RecyclerView caching mechanism | scrap the view of life cycle
  5. RecyclerView reuse caching mechanism
  6. RecyclerView cache analysis