1. RecyclerView cache mechanism and performance optimization
RecyclerView to do performance optimization to say complex complex, such as layout optimization, cache, preloading and so on. There are many optimized points. Between these seemingly independent points, there is actually a hub: Adapter. Because all ViewHolder creation and content binding are done through the two Adaper functions onCreateViewHolder and onBindViewHolder.
So the essence of our performance optimization is to reduce the time and number of calls to these two functions. If we want to make performance optimization of RecyclerView, we must clearly know how many times onCreateViewHolder and onBindViewHolder are called behind each step of operation. Therefore, understanding the cache mechanism of RecyclerView is the basis of RecyclerView performance optimization.
In order to understand the application scenario of cache, this paper will first briefly introduce the drawing principle of RecyclerView, and then analyze the implementation principle of cache.
2. Brief introduction of drawing principle
2.1 assuming that
To simplify the problem, the drawing principle introduction provides the following assumptions:
- RecyclerView
- LinearLayoutManager, for example
- Ignore ItemDecoration
- Ignore ItemAnimator
- Ignore Measure process
- Assume that the width and height of RecyclerView are fixed
- Recycler
- Ignore mViewCacheExtension
2.2 Drawing Process
(1) Introduction of class responsibilities
LayoutManager: take over the process of RecyclerView Measure, Layout and Draw
Recycler: Cache pool
Adapter: Generator and content binder for ViewHolder.
(2) Brief introduction to the drawing process
- RecyclerView. RequestLayout began drawing, ignore the process of Measure
- In the process of Layout, the RecyclerView will be filled by LayoutManager. Fill
- LayoutManager. The fill will call LayoutManager. LayoutChunk to generate a specific ViewHolder
- Then LayoutManager is called Recycler. GetViewForPosition ViewHolder to the Recycler
- Recycler first checks the level1 Cache to see if it is recyclable and returns it. If the level 1 cache is not found, the level 3 cache is searched, and if the level 3 cache is found, the adapter.bindViewholder is called to bind the content, and return. If the third level cache is not Recycler, create a ViewHolder with Adapter.createViewholder, bind the contents with adapter.bindViewholder and return Recycler. [See later: 2. Caching mechanism]
- Repeat steps 3-5 until the ViewHolder created fills the entire RecyclerView.
3. Caching mechanism
3.1 Brief analysis of source code
RecyclerView Recyler can recycle ViewHolder. Recyer can recycle by the following 5 objects:
- ArrayList mAttachedScrap: ViewHolder lists that are not separated from RecyclerView and that still depend on RecyclerView (for example, itemViews that have been slid out of sight but not removed) but have been marked removed are added to mAttachedScrap
- Find the ViewHolder by ID and position
- ArrayList mChangedScrap: A list of viewHolder whose data has changed. The viewHolder that needs to be changed when storing notifXXX is matched by position and ID
- ArrayList mCachedViews: Cache ViewHolder, mainly used to solve the situation of RecyclerView sliding jitter, and ViewHoder for storing Prefetch
- Maximum quantity: mViewCacheMax = mRequestedCacheMax + extraCache (extraCache is calculated from prefetch)
- ViewCacheExtension mViewCacheExtension: A developer-customizable layer of caching, an instance of the virtual class ViewCacheExtension, Developers can implement methods getViewForPositionAndType (Recycler Recycler, int position, int type) to implement its own cache.
- Applicable scenario: android. Jlelse. Eu/anatomy – of -…
- Fixed position
- The content remains the same
- The number of co., LTD.
- MRecyclerPool ViewHolder Cache pool. If the ViewHolder cannot be stored in the limited mCachedViews, the ViewHolder will be stored in the RecyclerViewPool.
- Find the ViewHolder by Type
- A maximum of five types are cached by default
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
3.2 Diagram of caching mechanism
RecyclerView in the design of the above five cache object is divided into three levels. Each ViewHolder is created by querying the cache in order of priority. Every time the ViewHolder cache is recycled, the Recycler cache is recycled in order of priority. The three levels of cache are:
- Level 1 cache: Returns a ViewHolder where both layout and content are valid
- Matches by position or ID
Hitting the level 1 cache does not require onCreateViewHolder and onBindViewHolder
- MAttachScrap is used when adapter.notifyxxx is used
- MChanedScarp is used every time the View is drawn, because getViewHolderForPosition is not called multiple times
- MCachedView: used to resolve sliding jitter. The default value is 2
- Level 2 cache: Returns the View
- Matches by position and type
- So let’s go back to View
- You need to inherit the ViewCacheExtension implementation yourself
- If the position is fixed and the content does not change, for example Header if the content is fixed, you can use it
- Level 3 cache: Returns ViewHolder with valid layout but invalid content
- Matches by type, with each type cache value default =5
- Layout is valid, but content is invalid
Multiple RecycleView can be shared
, can be used for multiple RecyclerView optimization
3.3 Example Explanation
Example explanation:
(1) Since ViewCacheExtension is rarely used in practice, the secondary cache is ignored in this example
(2) mChangedScrap and mAttchScrap are the cache of RecyclerView internal control, which are temporarily ignored in this example.
(3) In order to simplify the problem, the case of PreFetch is not considered for the time being
(4) Picture Explanation:
- RecyclerView consists of three parts: already out of the screen, inside the screen, about to enter the screen, we swipe up
- RecyclerView contains three types: 1,2,3. All the stuff on the screen is Type=3
- The red line represents interactions between ViewHoder and Recycler that have come out of the screen
- The green line represents interactions between ViewHolder and Recycler when ViewHoder is coming into the screen
- The purple line represents mCacheView entering the cache pool
Off-screen conditions -mCacheViews is not full
- When ViewHolder (position=0, type=1) is out of the screen, the ViewHolder is placed directly inside mCacheViews (0-N from old to new) because mCacheViews is empty. At this point, the ViewHolder layout and content within mCacheViews are valid and can therefore be reused directly.
- ViewHolder (position=1, type=2) repeat step 1
Screen time -mCacheViews is full
- When ViewHolder (position=2, type=1) exits the screen, the level-1 cache mCacheViews is already full. Remove the oldest ViewHolder(Position =0,type=1) from mCacheViews into RecyclePool and store ViewHolder(Position =2,type=1) into mCacheViews. At this time, the ViewHolder content removed to RecyclePool will be marked as invalid. When it is reused, it needs to bind the content through Adapter. BindViewHolder again.
- ViewHolder (position=3, type=3) repeat Step 3
What happens when you get on the screen
- When ViewHolder (Position =3, Type =3) enters the screen to draw, RecyclerPool cannot find View matching position in mCacheViews or RecyclerPool cannot find View matching type. It can only create a ViewHolder with adapter.createViewholder, and then bind the content with adapter.bindViewholder.
- When ViewHolder (position=11, type=1) enters the screen, the ReccylerPool cache with type=1 will be used. Since the content is invalid, you also need to call bindViewHolder to bind the layout. At the same time, ViewHolder (Position =4, type=3) needs to go out of the screen, which is reclaimed in Step 3
- ViewHolder (position=12, type=3) repeat Step 6
Screen down ViewHoder (Position =1) to enter the screen
- Since the ViewHolder in mCacheView with position=1 matches it, return. Because the content is valid, there is no need to bind the content again
- ViewHolder (position=0) repeat Step 8