in? Why is it in 2021, you still don’t know the caching mechanism of RecyclerView

1 Introduction

There are many articles about RecyclerView caching on the Internet, so why write this article? Before writing this article, I also browsed some articles with relatively high hits on the Internet. The overall writing is not bad, but some knowledge points are inadequate, and they may not understand them. Some use wrong conclusions, and some give them a pass. In order to allow readers to quickly decide whether to watch this article, ask the following questions. If you can give the correct answer, then the knowledge points of this article are basically mastered.

  1. What does mAttachedScrap do? Does this level of caching have much to do with developers?
  2. What does mChangedScrap do? Does it have much to do with developers?
  3. In the dimension of the first level cache, why design two different caches, mAttachedScrap and mChangedScrap, at the same time?
  4. What are the design tips of mUnmodifiableAttachedScrap?
  5. The number of mCachedViews caches and what are the characteristics of the ViewHolder in the cache?
  6. Although mViewCacheExtension is to customize the caching strategy for developers, it has no soft use.
  7. RecyclerPool can share cache objects for multiple RecyclerViews, but if it is set improperly, will it cause serious performance problems?
  8. HasStableIds returns true, what is the use?
  9. How to use onBindViewHolder(VH holder, int position, List payloads)?

2. Articles about RecyclerView

I have written some articles about RecyclerView before, and reading them will help to connect the knowledge points of RecyclerView and understand them by analogy.

1. What does RecyclerView do when sliding

2. Detailed explanation of one of the principles of RecyclerView animation

3. Detailed explanation of RecyclerView animation principle 2

In the article What does RecyclerView do when sliding, it explains the sequence of reuse and recycling in the RecyclerView sliding process, but it does not explain the caching mechanism of RecyclerView in detail. This article can be used as a follow-up supplement to this article.

In the series of articles explaining the principle of RecyclerView animation, I came into contact with mAttachedScrap. In the onLayoutChildren method of LayoutManager, the View on RecyclerView will be stripped off and placed in mAttachedScrap. This article involves the first level cache of the caching mechanism. Read this article You can get a good understanding of what mAttachedScrap is for.

3. RecyclerView cache architecture diagram

RecyclerView's cache, a picture in a picture.

As can be seen from the figure, RecyclerView cache is a four-level cache architecture. Of course, from the code comments of RecyclerView, it is officially believed that there is only a three-level cache, that is, mCachedViews is a first-level cache, mViewCacheExtension is a second-level cache, and mRecyclerPool is a third-level cache. From the developer's point of view, mAttachedScrap and mChangedScrap are opaque to developers, and the officials have not exposed any methods that can change their behavior.

3.1 mCacheViews can change the size of the cache through the following methods

   public void setItemViewCacheSize(int size) {
       mRecycler.setViewCacheSize(size);
   }

3.2 ViewCacheExtension is an abstract class. You can customize a subclass and modify the strategy of obtaining cache. However, this class only provides an interface to obtain the cache, and does not provide an interface to save the cache, which is very demanding for developers, and the use of RecyclerPool can well achieve general cache requirements. Therefore, this interface is basically a designer's tastelessness, and there is no soft use.

   public abstract static class ViewCacheExtension {
     public abstract View getViewForPositionAndType(Recycler recycler, int position,
             int type);
     }

3.3 The RecyclerViewPool class provides the maximum number of caches for modifying different types of views, which is very transparent to developers

   public void setMaxRecycledViews(int viewType, int max) {
       ScrapData scrapData = getScrapDataForType(viewType);
       scrapData.mMaxScrap = max;
       final ArrayList<ViewHolder> scrapHeap = scrapData.mScrapHeap;
       while (scrapHeap.size() > max) {
           scrapHeap.remove(scrapHeap.size() - 1);
       }
   }

4. RecyclerView$Recycler source code

We know that the caching function of RecyclerView is defined in RecyclerView$Recycler.

     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;
   } 

Let's explain the different levels of caching in turn:

4.1 mAttachedScrap

The corresponding data structure of mAttachedScrap is ArrayList. In the LayoutManager#onLayoutChildren method, when the views are laid out, all the Views on the RecyclerView will be temporarily stored in this collection for subsequent use. The characteristic of the ViewHolder in the cache is if If it matches the position or itemId on the RV, it is considered a clean ViewHolder and can be used directly without calling the onBindViewHolder method. The size of the ArrayList is unlimited. How many Views there are on the screen will create a large collection. The scenario that triggers this level of caching is generally to call the notifyItemXXX method. Call the notifyDataSetChanged method, and only when the Adapter hasStableIds returns true, the cache usage of this level will be triggered.

4.2 mChangedScrap

mChangedScrap and mAttachedScrap are the same level of cache, they are equal. But the calling scene of mChangedScrap is notifyItemChanged and notifyItemRangeChanged. Only the changed ViewHolder will be put into mChangedScrap. The ViewHolder in the mChangedScrap cache needs to call the onBindViewHolder method to rebind the data. Then there is a question, why do two different caches need to be designed for the same level of cache? What's the role ? Students who have read the series of articles about RecyclerView animation principle and detailed RecyclerView animation principle two will remember that the layoutForPredictiveAnimations method will eventually be called in the dispatchLayoutStep2 phase LayoutManager onLayoutChildren method to fill the remaining ViewHolder in mAttachedScrap to the screen So their difference is that the ViewHolder in mChangedScrap will not be forcibly filled on the RV when the RV is full. So is there a way to make the changed ViewHolder enter the mAttachedScrap cache? of course can. Call the notifyItemChanged(int position, Object payload) method to realize the partial refresh function. If the payload is not empty, then the changed ViewHolder will be separated into mAttachedScrap.

4.3 mUnmodifiableAttachedScrap

mUnmodifiableAttachedScrap = Collections.unmodifiableList(mAttachedScrap) is an encapsulation of mAttachedScrap, which exposes mAttachedScrap to developers to call. Its characteristic is that it can only be read but not written.

4.4 mCachedViews

The data structure corresponding to mCachedViews is also an ArrayList, but the cache has a limit on the size of the collection, and the default is 2. The characteristics of ViewHolder in the cache are the same as those in mAttachedScrap. As long as the position or itemId corresponds to it, it is clean without rebinding data. Developers can call the setItemViewCacheSize(size) method to change the size of the cache. A common scenario triggered by this level of cache is sliding RV. Of course notifyXXX will also trigger the cache. The cache is as efficient as mAttachedScrap.

4.5 ViewCacheExtension

ViewCacheExtension developers have little meaning to implement by themselves. Basically everything you want to do can be achieved through RecyclerViewPool.

4.6 RecyclerViewPool

RecyclerViewPool cache can set the cache size for multiple ItemTypes. The default cache number for each ItemType is 5. And the cache can be shared by multiple RecyclerView. Since the default number of caches is 5, assuming that a certain news app can display 10 news per screen, it will inevitably cause cache hits to fail and frequently cause ViewHolder creation to affect performance. Therefore, the cache size needs to be expanded.

5. End

This article does not involve the explanation of the code. First, there are too many materials on the Internet, and the explanation is relatively complete. Second, this article focuses on explaining the functions and differences of caches at all levels. So here it is, go slowly and not send it.

and many more! Click a thumbs up and leave, kneel to the boss T_T

Guess you like

Origin blog.csdn.net/Androiddddd/article/details/112206852