preface
This paper mainly explains RecyclerView cache mechanism through the following aspects:
- ViewHolder
- Cache architecture
- Recycler.recycleViewHolderInternal(ViewHolder holder)
- Recycler.tryGetViewHolderForPositionByDeadline(int position, boolean dryRun, long deadlineNs)
1. ViewHolder
1.1 role
ViewHolder is the wrapper of ItemView on RecyclerView, which is the carrier of RecyclerView cache. It encapsulates the following attributes:
-
View itemView: a child of RecyclerView
-
Int mPosition: position of View data in the data source
-
Int mOldPosition: View last bound data position in the data source
-
Long mItemId: Determines whether the ViewHolder needs to rebind data
-
Int mItemViewType: Specifies the type of itemView
-
Int mPreLayoutPosition: Position of the ViewHolder data in the data source during the prelayout phase
-
Int mFlags: the flag bit corresponding to the ViewHolder
-
List<Ojbect> mPayloads: local refresh
-
Recycler mScrapContainer: If not empty, ViewHolder is stored in the Scrap cache
1.2 flag
-
FLAG_BOUND: The View corresponding to the ViewHolder is already bound and does not need to be rebound
-
FLAG_UPDATE: Data has changed and View needs to be rebound
-
FLAG_INVALID: Data is invalid and View needs to be rebound
-
FLAG_REMOVED: Data is removed from the data source, and the View is still useful in vanishing animations
-
FLAG_NOT_RECYCLABLE: ViewHolder cannot be recycled. Ensure that the ViewHolder cannot be recycled when animating an ItemView
-
FLAG_RETURNED_FROM_SCRAP: ViewHolder retrieved from the scrap cache
-
FLAG_IGNORE: An error is reported if a ViewHolder of this type is recalled
-
FLAG_TMP_DETACHED: DETACHED from RecyclerView DETACHED from RecyclerView detach and remove detach will remove the View from the children array of the ViewGroup and refresh the ViewGroup detach will delete and not refresh the ViewGroup
-
FLAG_ADAPTER_FULLUPDATE: Indicates that the ViewHolder requires full updates, or partial updates if this flag is not set
-
FLAG_MOVED: Used when animating when the position of the ViewHolder changes
-
FLAG_APPEARED_IN_PRE_LAYOUT: ViewHolder appears in the prelayout, need to be animated
2. Cache architecture
2.1 Level 4 Cache
- ArrayList<ViewHolder> mAttachedScrap & ArrayList mChangedScrap
- ArrayList<ViewHolder> mCachedViews
- ViewCacheExtension mViewCacheExtension
- RecycledViewPool mRecyclerPool
2.2 scrap cache
The Scrap cache is composed of mAttachedScrap and mChangedScrap. When RecyclerView calls dispatchLayout, the cache is used to save the RecyclerView sub-view.
Two parts
- mAttachedScrap
- mChangedScrap
The cache feature
- The corresponding data structure is ArrayList
- There is no limit to the size of the cache. The size is equal to the number of RecyclerView child views
- The ViewHolder in the cache does not need to be rebound as long as the Position in the ViewHolder corresponds to the position in the data source
- Call the notifyItemRemoved, notifyItemMoved, and notifyItemInserted methods, and put the ViewHolder into the mAttachedScrap
- Call notifyItemChanged(int Position, Object Payload) if payload! = NULL ViewHolder into mAttachedScrap, otherwise ViewHolder into mChangedScrap
- When notifyDataSetChanged() is called, if Adapter.hasstableids returns true, the ViewHolder is put into mAttachedScrap, otherwise the ViewHolder is recycled into the non-Scrap cache
- LinearLayoutManager. LayoutForPredictiveAnimations (phase), and the rest of the ViewHolder mAttachedScrap array is being squeezed out of the screen
2.3 mCachedViews cache
The cache feature
- The corresponding data structure is ArrayList
- The cache size is limited. The default cache size is 2. You can change the default cache size. If using GridLayoutManager, it is recommended to set this to the number of columns
- The ViewHolder in the cache does not need to be rebound, as long as the Position of the ViewHolder matches the position and itemType in the data source
- The feature of this cache is FIFO
- ViewHolder mFlag If FLAG_INVALID, FLAG_REMOVED, FLAG_UPDATE, or FLAG_ADAPTER_POSITION_UNKNOWN exists, the cache will not be placed in the cache
- When RecyclerView slides, the ViewHolder is put into the cache or retrieved from the cache
2.4 ViewCacheExtension
This interface provides only the GET method, not the PUT method.
2.5 RecyclerViewPool
The cache feature
- The corresponding data structure is SparseArray
, which groups the caches according to the itemType
- ArrayList
. The default size for each itemType is 5
- The ViewHolder in this cache needs to rebind the data
- Can be provided to multiple RecyclerView sharing
3. Recycler.recycleViewHolderInternal(ViewHolder holder)
3.1 Call Timing
- NotifyItemRemoved () is called after the ViewHolder animation ends
- The ViewHolder is squeezed out of the screen after the animation ends
- The ViewHolder that slides off the screen needs to be reclaimed
- NotifyDataSetChanged () is called, and Adapter.hasstableids () returns false
- ViewHolder retrieved from cache failed verification and needs to be reclaimed
- When calling setAdapter ()
3.2 Reclamation Logic
3.2.1 Recycling Flow Chart
3.2.2 Source code analysis
- If the current mCachedViews capacity is greater than or equal to the maximum cache size, recycle the oldest ViewHolder to RecyclerViewPool until mCachedViews size=mViewCacheMax-1
- Cache the ViewHolder on the last slot of mCachedViews
- If the cache to mCachedViews fails, the cache is stored in RecyclerViewPool
3.2.2.1 Recycler. RecycleViewHolderInternal ()
3.2.2.2 Recycler. RecycleCachedViewAt (int cachedViewIndex)
3.2.2.3 RecyclerViewPool. PutRecycledView (ViewHolder scrap)
4. Recycler.tryGetViewHolderForPositionByDeadline(int position, boolean dryRun, long deadlineNs)
4.1 Call Timing (LinearLayoutManager as an example)
DispatchLayoutStep1 (), dispatchLayoutStep2(), sliding RecyclerView will occur multiplexing
- LinearLayoutManager.onLayoutChildren() -> LinearLayoutManager.fill() -> LinearLayoutManager.layoutChunk() -> LinearLayoutManager.LayoutState.next()-> RecyclerView.Recycler.getViewForPosition()
- LinearLayoutManager.layoutForPredictiveAnimations()-> LinearLayoutManager.fill() -> LinearLayoutManager.layoutChunk() -> LinearLayoutManager.LayoutState.next()-> RecyclerView.Recycler.getViewForPosition()
- LinearLayoutManager.scrollBy()-> LinearLayoutManager.fill() -> LinearLayoutManager.layoutChunk() -> LinearLayoutManager.LayoutState.next()-> RecyclerView.Recycler.getViewForPosition()
4.2 Obtaining the ViewHolder Process
2 Recycler. GetChangedScrapViewForPosition (int position)
Get the ViewHolder from the mChangedScrap cache during prelayout. The acquisition logic is as follows:
- Linear traversal mChangedScrap, position = = ViewHolder mPreLayoutPosition, return the ViewHolder, or logic. 2
- Adapter.hasstableids () returns false and null, otherwise logic 3 is used
- Madapter. getItemId(offsetPosition) == Holder. getItemId, return the ViewHolder, otherwise go to logic 4
- None of the above is retrieved, return NULL.
4.2.2 Recycler. GetScrapOrHiddenOrCachedHolderForPosition (int position)
4.2.2.1 cache was obtained from the three places, the method to get after you also need to verify whether legitimate, if validation fails will call Recycler. RecycleViewHolderInternal ()
- mAttachedScrap
- In the hidden List, ChildHelper uses the bitmap algorithm to logically hide the ViewHolder
- mCachedViews
4.2.2.2 To be obtained from mAttachedScrap, all the following conditions must be met:
- Holder. GetLayoutPosition () = = position, namely preliminary layout viewHolder. MPreLayoutPosition = = position, Viewholder. mPosition == Position in the layout
- ! holder.isInvalid()
- Removed ViewHolder cannot be returned without pre-placement
4.2.2.3 Get the ViewHolder from the Hidden List
- Call calls ChildHelper. FindHiddenNonRemovedView (position) for the View
- Call childhelper. unhide(view) without hiding the view
- Call mChildHelper. DetachViewFromParent (layoutIndex), lightweight delete the View, from RecyclerView children array removed, but not redraw RecyclerView
- Put it in the Scrap cache
4.2.2.4 Obtaining ViewHolder from mCachedViews
- ! holder.isInvalid()
- holder.getLayoutPosition() == position
- If so, the ViewHolder will be removed from mCachedViews
Holdings Recycler. GetScrapOrCachedViewForId (long id, int type, Boolean dryRun)
- The attachedScrap is first searched. If both id and Type match, the ViewHolder is returned and, in the case of non-prelayout, the removed is set to Update
- If id matches type but not type, the ViewHolder is reclaimed
- The ViewHolder is then removed from mCachedViews. If the ID and type match, the ViewHolder is returned
- If id matches type but not type, the ViewHolder is reclaimed
4.2.4 getRecycledViewPool (.) getRecycledView (int type)
Get the corresponding ViewHolder from SparseArray based on viewType, and the ViewHolder taken from this cache needs to be rebound
4.2.5 Adapter. CreateViewHolder (RecyclerView RecyclerView, int type)
4.2.6 Recycler. TryBindViewHolderByDeadline (RecyclerView RecyclerView, int type)
- Call the Adapter.bindViewholder () method
- Set the ViewHolder flag to FLAG_BOUND and clear FLAG_UPDATE, FLAG_INVALID, and FLAG_ADAPTER_POSITION_UNKNOWN
- Rewrite onBindViewHolder(VH holder, int position, List