This article has been published exclusively by guolin_blog, an official wechat account

Recently, I am studying the recycle mechanism of RecyclerView. By the way, I will take note. As we know, RecyclerView is managed by recycling mechanism when layout sub-view is used. There are also a lot of articles on the analysis of recycling mechanism on the Internet, the analysis is also very detailed, what level of cache ah, first go to mChangedScrap and then where to take ah and so on; But in fact, I want to say that RecyclerView recycling mechanism is indeed very perfect, covering various scenarios, but not every scene of recycling will go through all the process of the mechanism. For example, scenarios such as setLayoutManager, setAdapter, notifyDataSetChanged, or sliding trigger the recycle reuse mechanism. But if only the RecyclerView sliding scene triggered by the recycling reuse mechanism, in fact, does not need four levels of cache to participate in.

Emmm, should be a little confused, then continue to read it, will be a little bit slowly analyzed. This article will not analyze the source code for recycling mechanism line by line, as other articles do, and I don’t have the ability to do so, so I will analyze the source code based on a specific scenario, so it will be easier to understand. End of nonsense, let’s get down to business.

To the chase

RecyclerView recycle recycle mechanism internal realization is by Recycler internal class, the following is to explain the recycle recycle mechanism of RecyclerView with such a page sliding scene.

RecyclerView: RecyclerView-v7-25.1.0.jar LayoutManager: recyclerView-v7-25.1.jar LayoutManager: GridLayoutManager extends LinearLayoutManager (recyclerview – v7-25.1.0. Jar)

This page can display 5 cards per line, and each card has the same item layout type.

Before we start analyzing the recycling reuse mechanism, let’s ask a few questions:

Q1: If you swipe down, the display of the 5 cards in the new line will be demultiplexed from the cached ViewHolder, and the 5 cards in the first line will be removed from the screen and recycled. In this process, should the 5 cards be reused first and then recycled? Or recycle first? Or recycle as you go? That is, is it possible that the ViewHolder reused for the 5 cards in the new row is the same 5 cards that were recycled in the first row?

Before the second question, take a look at some pictures:

Black box represents the screen, RecyclerView first downward slide, the third line card display, and then upward slide, the third line out of the screen, the first line is displayed. OnCreateViewHolder (); onBindViewHolder();

Red box 1 is the log of RecyclerView sliding down operation. The display of the 5 cards in the third line is the ViewHolder created again. Red box 2 is the log for sliding up again. The ViewHolder used for redisplaying the first row of 5 cards is reused because there is no log for the create ViewHolder. Then only onBindViewHolder() is used for rebinding the data for the next 3 cards. So here’s the question:

Q2: In this process, why is it that when RecyclerView slides up again to redisplay the first row of 5 cards, only the last 3 cards trigger the onBindViewHolder() method to rebind data? Obviously 5 cards are multiplexed.

On the basis of the above operation, we continue to operate:

Based on the operation in question 2, 15 ViewHolder have now been created and rows 1 and 2 are displayed. Slide down twice and log the process as follows:

Red box 1 is the log of the second problem operation, which is truncated here only to show that subsequent logs continue operations on the above basis;

Red box 2 is the log for the first slide. Compared with the log for problem 2, the ViewHolder used by the 5 cards in the third row is also reused, and only the last 3 cards trigger onBindViewHolder() to rebind data.

Red box 3 is the log for the second downward slide. The ViewHolder used by the first three cards is reused, and the ViewHolder used by the second two cards is recreated, and onBindViewHolder() is called for all five cards to rebind data.

So,

Q3: If you swipe up or down a few times, there’s no log for onCreateViewHolder() anymore, so RecyclerView creates 17 Viewholders, but sometimes only 3 of the 5 cards in a row need to be rebound. Sometimes all 5 cards need to be rebound. Why?

If understand RecyclerView recycling reuse mechanism, then these three problems also know the reason; Conversely, if you know the reasons of these three problems, then it is easier to understand the recycling mechanism of RecyclerView; So, it should be easier to analyze the source code in a particular scenario with a problem in mind.

Source code analysis

Actually, according to the log of question 2, we can answer question 1. On the basis of the current 10 Viewholders for lines 1 and 2, the viewholders for the 5 new cards in the third row need to be recreated. In other words, in this sliding down process, the reuse mechanism of the 5 new cards works first. Then the five cards in line 1 are removed from the screen and the recycling mechanism works.

So, let’s take a look at the source code of the reuse mechanism

Reuse mechanism

getViewForPosition()

This method is the portal to Recycler, the recycle API that can be used by the Recycler to return views that we want to recycle or recycle.

tryGetViewHolderForPositionByDeadline()

So the internal realization of Recycler is in this method. Before analyzing the logic, let’s look at the Recycler structures used to cache ViewHolder.

mAttachedScrap: The ViewHolder, which is used to cache items that are displayed on the screen, is like RecyclerView, which is used to remove children and then add them back in onLayout, Therefore, this List should be used to temporarily store children in the layout process. Anyway, the ViewHolder for reuse will not be found in this List during RecyclerView sliding.

MChangedScrap: this didn’t understand is why, look at the name should be related to the ViewHolder data changes, in the process of RecyclerView sliding, also did not find here looking for reuse ViewHolder, so this can temporarily put aside first.

* * mCachedViews: ** This is much more important. The recycle and reuse of the sliding process is the first processing of the List, the original ViewHolder data stored in the RecyclerView can be directly added to display, do not need to re-onBindViewholder ().

MUnmodifiableAttachedScrap: don’t know why, skip for the time being.

**mRecyclerPool: ** This is also important, but the data stored in the ViewHolder will be reset, as if the ViewHolder was a new one, so you need to call onBindViewHolder again to bind the data.

**mViewCacheExtension: **mViewCacheExtension: **mViewCacheExtension:

So let’s look at the reuse logic:

The first step is simple: If position is outside the range of item, throw an exception. Keep reading

If you’re on isPreLayout(), go to mChangedScrap. So what does this isPreLayout represent? , there are two assignments.

Emmm, apparently, is set to false before the onLayoutChildren of the LayoutManager, but I still don’t know what that does, it looks like mstate.minprelaYou = false, so it doesn’t come in here, I’ll skip it for a moment. Keep going.

Follow up on this method

First, the viewHolder with the same position in the mAttachedScrap must match some conditions, such as the viewHolder has not been removed, is valid and so on. The viewHolder will be returned if the conditions are met.

So, the key here is to understand exactly what the mAttachedScrap is and which Viewholders are stored.

A remote control button operation, no matter whether there is sliding, will cause the RecyclerView to re-onlayout, then to layout, RecyclerView will remove all children first, and then add it again, Complete a layout process. So where do I put the temporary remove viewHolder, in the mAttachedScrap, that’s my understanding.

Therefore, the viewHolder stored in the mAttachedScrap does not seem to have much to do with recycling or reuse.

Some online analysis of the article has said that RecyclerView will reuse in order to mChangedScrap, mAttachedScrap and so on cache to find, did not find further to find, from the code is correct, but I think there is a problem with this expression. As far as the sliding scenario based on RecyclerView is concerned, the reuse of new cards and the recycling mechanism of old cards will not involve mChangedScrap and mAttachedScrap, so I think it would be better to analyze the recycling mechanism based on a certain scenario. Like mChangedScrap, I don’t understand what it’s for, but I guess it’s a reuse scenario that only gets involved when the data changes, so when I analyze reuse based on sliding scenarios, even if I don’t understand this, it doesn’t matter much.

Keep reading

Emmm: Hidden, hidden, hidden, hidden, hidden, hidden. I don’t know, if I skip this, I’m not going to worry about it, and I’m not going to worry about recycling when I slide.

This is where I’m going to focus, take notes take notes, reuse this mechanism in sliding scenes.

The default size of mCachedViews is 2. Traverse mCachedViews to find ViewHolder with the same position. As mentioned before, data information stored in The ViewHolder is saved in mCachedViews, so mCachedViews can be interpreted as, Only the old card can reuse the ViewHolder. The new card cannot be used from the ViewHolder in mCachedViews.

After finding viewholder

If the position match finds a ViewHolder, it is necessary to determine whether the ViewHolder has been removed.

The above search is done in mCachedViews. If you can’t find it, you should continue to search again. The search was done by position just now, so change it to ID this time, and then repeat the above steps to search again, as follows

GetScrapOrCachedViewForId () to do with getScrapOrHiddenOrCacheHolderForPosition () in fact, only one is through the position to seek ViewHolder, One is by ID. This id is not the Android: ID we set in the XML, but rather an attribute that the Adapter holds. This attribute is not used by default, so step 5 will not actually be executed unless we override the Adapter setHasStableIds(), Since it’s not a common scenario, I’m going to skip it and move on.

This is often said that the extension class, RecyclerView give us custom extension class, we can rewrite getViewForPositionAndType reuse () method to implement its strategy. However, also did not use, then this part also as not to execute, skip. To continue down

That’s the point. Take notes, take notes.

The ViewPool will create different lists according to different item types. The default size of each List is 5. So let’s see, how do WE find it in the ViewPool

As mentioned earlier, the ViewPool will create different sets of viewholers for different viewtypes, so whenever you reuse a ViewHolder cache for the same type in the ViewPool, you will reuse the last one. Unlike mCachedViews which requires various matching conditions, as long as there can be reused.

Looking at the code after “Figure 7”, once the ViewHolder is held, resetInternal() is called again to reset the ViewHolder so that it can be used as a new ViewHolder. That’s why any ViewHolder you take from here needs to be onBindViewHolder() again.

So what if I still don’t find it in ViewPool? Keep going

If no ViewHolder can be found in the ViewPool, call Adapter onCreateViewHolder to create a new ViewHolder.

There are a number of steps to find the ViewHolder. Once the ViewHolder is found at any point, the following steps are ignored, and you continue to determine whether the data needs to be rebound, as well as check that the layout parameters are valid. As follows:

Here, tryGetViewHolderForPositionByDeadline () this method is over. So this is sort of the RecyclerView reuse mechanism, and we’re skipping a lot of places here, because RecyclerView has all kinds of scenarios that can refresh its view, like reset setLayoutManager(), reset setAdapter(), Or notifyDataSetChanged(), or sliding, or something like that. Relayouts recycle and reuse ViewHolder, so the reuse mechanism needs to take into account a variety of scenarios.

It is a little difficult to chew through the code line by line, so I only use the sliding scenario of RecyclerView to analyze the recycling and reuse mechanism involved.

Let’s look at the recycling mechanism

Recycling mechanism

There are many ways to recycle Recycler because Recycler has various recyclers, such as mAttachedScrap, mCachedViews, etc. Different recyclers have different opportunities to recycle.

Therefore, or based on the sliding scene of RecyclerView, the entrance of the card recycling removed from the screen is:

The sliding scenario analyzed in this paper will be handled by scrollVerticallyBy() of the LinearLayoutManager when RecyclerView slides. LayoutManager then calls the fill() method to process the cards that need to be reused and recycled, and finally calls the recyclerView() method above to start recycling.

The logic of recycling is relatively simple. LayoutManager iterates over the removed cards and then recyses each card. During recycling, ViewHolder is placed into mCachedViews. Take a ViewHolder in mCachedViews and throw it into the ViewPool cache, and then mCachedViews can make room for the newly reclaimed ViewHolder.

To sum up:

Recycle recycle in RecyclerView sliding scenario 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.

The overall flow chart is as follows :(can be enlarged to view)

Finally, explain the opening question

Q1: If you swipe down, the display of the 5 cards in the new line will be demultiplexed from the cached ViewHolder, and the 5 cards in the first line will be removed from the screen and recycled. In this process, should the 5 cards be reused first and then recycled? Or recycle first? Or recycle as you go? That is, is it possible that the ViewHolder reused for the 5 cards in the new row is the same 5 cards that were recycled in the first row?

A: Reuse and then recycle. The 5 cards in the new line are recycled from the current mCachedViews and ViewPool cache, and then re-created. The 5 cards in the line removed from the screen are recycled and cached in mCachedViews and ViewPool. So a new row of 5 cards and reuse cannot use the 5 cards that have just been removed from the screen.

Q2: In this process, why is it that when RecyclerView slides up again to redisplay the first row of 5 cards, only the last 3 cards trigger the onBindViewHolder() method to rebind data? Obviously 5 cards are multiplexed.

A: The structures involved in recycling and reuse in the sliding scenario are mCachedViews and ViewPool, with the former having a default size of 2 and the latter 5. So, when the third row is displayed, the five cards in the first row are recycled. When they are recycled, they are first cached in mCachedViews, and then removed from the old one to the ViewPool. Of all the five cards, two are cached in mCachedViews, and three are cached in the ViewPool. The LayoutManager controls which two caches are stored in mCachedViews.

In the example above, we use a GridLayoutManager. The recycling logic is implemented in the parent LinearLayoutManager class. The first row of cards is recycled from back to front, so the latest two cards are 0 and 1, which will be stored in mCachedViews. Slots 2, 3, and 4 are in the ViewPool.

So, when you slide up again, the first 5 cards in the first row will be reused in the two structures. As mentioned before, the ViewHolder stored in mCachedViews can only be reused in the original position. The ViewHolder can be used directly in mCachedViews for both card bits 0 and 1, and the ViewHolder does not need to be rebound. For card bits 2, 3 and 4, the ViewHolder can be used directly in ViewPool. There are three Viewholders cached in the ViewPool, so all the five bits in the first row are used for reuse, and only three bits need to be rebound for reuse from the ViewPool.

Q3: If you swipe up or down a few times, there’s no log for onCreateViewHolder() anymore, so RecyclerView creates 17 Viewholders, but sometimes only 3 of the 5 cards in a row need to be rebound. Sometimes all 5 cards need to be rebound. Why?

A: Sometimes only three cards in a row need to be rebound for the same reason as Q2, because mCachedView caches the ViewHolder of the current position. The ViewHolder can be used directly. 17 Viewholders were created because there were only three caches in the ViewPool for the fourth row, and the two caches in mCachedViews were not used for the fourth row because they were viewholders of 0 and 1. You’ll need to create two more Viewhodlers for the last two cards in the fourth row.