Small knowledge, big challenge! This article is participating in the creation activity of “Essential Tips for Programmers”


This article has participated in the “Digitalstar Project” and won a creative gift package to challenge the creative incentive money

preface

Study source code, study source code programming ideas, is the only way for developers to advance

We all know that RecyclerView has recycling reuse mechanism, then recycling reuse mechanism is how to function?

Today we will use the source code to explain, learn together

Android Advanced: Fully embrace the Activity Results API instead of onActivityResult

Android source code advanced in-depth understanding of SharedPreference principle mechanism

Study should “combine work and rest”

Android advanced desktop tasks and scheduling services and abandoned AlarmManager to fully embrace WorkManager

Android transition animation depth analysis

Java advanced in-depth understanding of load balancing algorithm implementation principle of 5

Advanced Coil for Android – Full explanation of kotlin’s photo gallery

History recommendation, mutual encouragement

Happy National Day: study and progress together

1. Recycler

RecyclerView is managed by internal Recycler caching. What is Recycler caching? We know that RecyclerView can slide like silk even when there is a lot of data, and RecyclerView itself is a ViewGroup, If you need to create a child View every time you use it, it will affect the flow of the sliding process. RecyclerView can cache Recycler by using ViewHolder(internal child views) so that you can recycle child views when sliding or, in some cases, recycle data bound to child views. So essentially caching is to reduce the time spent repeatedly drawing views and binding data, which improves performance when sliding

public final class Recycler {

final ArrayList<ViewHolder> mAttachedScrap = new ArrayList<>();

ArrayList<ViewHolder> mChangedScrap = null;

final ArrayList<ViewHolder> mCachedViews = new ArrayList<ViewHolder>();

private final List<ViewHolder>

mUnmodifiableAttachedScrap = Collections.unmodifiableList(mAttachedScrap);

private int mRequestedCacheMax = DEFAULT_CACHE_SIZE;

int mViewCacheMax = DEFAULT_CACHE_SIZE;

RecycledViewPool mRecyclerPool;

private ViewCacheExtension mViewCacheExtension;

static final int DEFAULT_CACHE_SIZE = 2;
Copy the code

Recycler cache ViewHolder has four levels of Recycler cache, from highest priority to lowest:

ArrayList mAttachedScrap – ViewHolder of the visible range in the cache screen

2, ArrayList mCachedViews —- Cache slide ViewHolder to be separated from RecyclerView, according to the position or ID of the child View cache, the default maximum storage 2

ViewCacheExtension mViewCacheExtension — developer implemented cache

RecycledViewPool mRecyclerPool — ViewHolder cache pool, essentially a SparseArray, where key is ViewType(int type), Value holds ArrayList< ViewHolder>. By default, each ArrayList can hold up to five ViewHolder.

Second, cache mechanism analysis and detailed explanation

When RecyclerView slides, it triggers onTouchEvent#onMove, and the recycle and reuse ViewHolder will start there. We know that set RecyclerView need to set up the LayoutManager, LayoutManager is responsible for the layout of the RecyclerView, contains and reuse of ItemView. Take the LinearLayoutManager as an example. When RecyclerView is reconfigured, the following methods will be executed successively:

OnLayoutChildren () : Entry method for RecyclerView layout

Fill (): is responsible for continuously filling the remaining space. The method called is layoutChunk().

LayoutChunk () : Is responsible for filling views that are eventually recycled by finding suitable views in the cache class Recycler

The entire call chain above: OnLayoutChildren ()-> Fill ()->layoutChunk()-> Next ()->getViewForPosition(),getViewForPosition() is the Recy in RecyclerView Get the appropriate View in clER,

Recycler#getViewForPosition();

@NonNull public View getViewForPosition(int position) { return getViewForPosition(position, false); } View getViewForPosition(int position, boolean dryRun) { return tryGetViewHolderForPositionByDeadline(position, dryRun, FOREVER_NS).itemView; } they will execute tryGetViewHolderForPositionByDeadline function, continue to go in: / / according to the position of the incoming get ViewHolder ViewHolder tryGetViewHolderForPositionByDeadline (int position, Boolean dryRun. long deadlineNs) { ... Omit Boolean fromScrapOrHiddenOrCache = false; ViewHolder holder = null; / / layout Belongs to the special situation Obtained from the mChangedScrap ViewHolder if (mState isPreLayout ()) {holder = getChangedScrapViewForPosition (position); fromScrapOrHiddenOrCache = holder ! = null; } if (holder == null) {// Select ViewHolder from mAttachedScrap. Continue to get ViewHolder from mCachedViews try holder = getScrapOrHiddenOrCachedHolderForPosition (position, dryRun); . Omit} the if (holder = = null) {final int offsetPosition = mAdapterHelper. FindPositionOffset (position); . Omit final int type = adapter.getitemViewType (offsetPosition); // If the Adapter has an Id declared, try to get it from the Id, Here does not belong to cache the if (mAdapter hasStableIds ()) = {holder getScrapOrCachedViewForId (mAdapter. GetItemId (offsetPosition), type, dryRun); } if (holder == null && mViewCacheExtension ! {mViewCacheExtension = ViewHolder;} The cache requires developers to realize the final View View. = mViewCacheExtension getViewForPositionAndType (this, the position, type); if (view ! = null) { holder = getChildViewHolder(view); }} if (holder == null) {// fallback to pool // getRecycledViewPool().getRecycledView(type); if (holder ! = null) {// the ViewHolder state will be reset if it is successfully retrieved, so we need to re-execute Adapter#onBindViewHolder binding holder.resetinternal (); if (FORCE_INVALIDATE_DISPLAY_LIST) { invalidateDisplayListInt(holder); } } } if (holder == null) { ... // If no ViewHolder is found in the cache above, Will call in the Adapter onCreateViewHolder create a holder = mAdapter. CreateViewHolder (RecyclerView. This type); } } boolean bound = false; if (mState.isPreLayout() && holder.isBound()) { holder.mPreLayoutPosition = position; } else if (! holder.isBound() || holder.needsUpdate() || holder.isInvalid()) { final int offsetPosition = mAdapterHelper.findPositionOffset(position); // if you need to bind data, Invokes the Adapter# onBindViewHolder to bind data bound = tryBindViewHolderByDeadline (holder, offsetPosition, position, deadlineNs); }... Omit the return holder; }Copy the code

ViewHolder obtained with mAttachedScrap, mCachedViews, and mViewCacheExtension does not need to recreate the layout and bind data; ViewHolder retrieved from the cache pool mRecyclerPool does not need to recreate the layout, but rebind the data; If the target ViewHolder is not retrieved from either of the caches, then Adapter#onCreateViewHolder is called to create the layout and Adapter#onBindViewHolder is called to bind the data

conclusion

Recycle reuse under the sliding scene of RecyclerView involves two structures:

  • MCachedViews and RecyclerViewPool
  • The priority of mCachedViews is higher than RecyclerViewPool. When recycled, the latest ViewHolder is put into mCachedViews. If it is full, Remove one and throw it into the ViewPool to make room to cache the latest ViewHolder.
  • When reusing, the ViewHolder should be found in mCachedViews first, but various matching conditions are required. To sum up, only the card position in the original position can be reused in the ViewHolder in mCachedViews. If not, So I’m going to look in the ViewPool.
  • A ViewHolder in a ViewPool is the same as a new ViewHolder. If the ViewHolder type is the same, it can be reused and rebound.

\

\