0%

RecyclerView在不同操作下的ViewHolder回收分析

起因

RV在几乎在所有的有界面的Android应用都会使用到,可以说日常开发中RV是老熟人天天见,可是仔细想想熟悉它的什么呢?熟悉的是怎么去使用,对于它的内部原理太过陌生。曾经多次点看它的源码,但都迷失在成千上万的代码与注释中。幸好网上还有很多关于RV优秀的文章,让我能抓住RV的一些重点,其中一个重点就是它对viewHolder的回收与复用机制,关于这一块网上很多文章已经分析过了,Recycler、四级缓存等等,但我还是有一个疑问,在什么操作下会将什么viewHolder放到哪一个缓存?由于RV源码太复杂了😭,下面就带着这个疑问片面地分析下它的缓存设计。

Recycler与RecyclerPool类

Recycler类是RV的一个内部类,正如它的名字一样它是负责回收的,在它的内部定义了好几种关于回收的成员变量,先熟悉一下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16

public final class Recycler {

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

ArrayList<ViewHolder> mChangedScrap = null;

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

RecycledViewPool mRecyclerPool;
private ViewCacheExtension mViewCacheExtension;
static final int DEFAULT_CACHE_SIZE = 2;
}



有三种类型为ViewHolder的ArrayList,显然它们是都是用来缓存不同时机的ViewHolder的,接下来是一个类成员RecycledViewPool,它的代码如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70

public static class RecycledViewPool {

private static final int DEFAULT_MAX_SCRAP = 5;

static class ScrapData {

//回收list

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

int mMaxScrap = DEFAULT_MAX_SCRAP;

long mCreateRunningAverageNs = 0;

long mBindRunningAverageNs = 0;

}

SparseArray<ScrapData> mScrap = new SparseArray<>();

private ScrapData getScrapDataForType(int viewType) {
ScrapData scrapData = mScrap.get(viewType);
if (scrapData == null) {
scrapData = new ScrapData();
mScrap.put(viewType, scrapData);
}
return scrapData;
}

/**

* Acquire a ViewHolder of the specified type from the pool, or {@code null} if none are

* present.

*
* @param viewType ViewHolder type.
* @return ViewHolder of the specified type acquired from the pool, or {@code null} if none
* are present.
*/
@Nullable
public ViewHolder getRecycledView(int viewType) {
final ScrapData scrapData = mScrap.get(viewType);
if (scrapData != null && !scrapData.mScrapHeap.isEmpty()) {
final ArrayList<ViewHolder> scrapHeap = scrapData.mScrapHeap;
return scrapHeap.remove(scrapHeap.size() - 1);
}
return null;
}
/**
* Add a scrap ViewHolder to the pool.
* <p>
* If the pool is already full for that ViewHolder's type, it will be immediately discarded.
*
* @param scrap ViewHolder to be added to the pool.
*/
public void putRecycledView(ViewHolder scrap) {
final int viewType = scrap.getItemViewType();
final ArrayList<ViewHolder> scrapHeap = getScrapDataForType(viewType).mScrapHeap;
if (mScrap.get(viewType).mMaxScrap <= scrapHeap.size()) {
return;
}
if (DEBUG && scrapHeap.contains(scrap)) {
throw new IllegalArgumentException("this scrap item already exists");
}
scrap.resetInternal();
scrapHeap.add(scrap);
}
}

在它内部还有一个静态内部类ScrapData,ScrapData类中出现了ArrayList,显然又是用来保存ViewHolder的,但为什么要单独设计一个类中呢,再看看下面一行,定义了一个SparseArray,SparseArray是一个Android特有的类,类似于HashMap,但它的key只能是int,结合RecycledViewPool的put和get方法可以知道,SparseArray中的key就是RV中的itemType,并且每个itemType最多保存5个,所以,在RecycledViewPool中viewHolder是按照它们的类型来分别保存的,这一点也提醒了我:之前那三个缓存是不分类型的!

ViewHolder类

这是一个在开发中经常使用的类,日常使用中也就是在继承ViewHolder,在其中保存itemView的子View,但今天关注的点不一样,在ViewHolder中定义了很多关于ViewHolder状态的常量,主要的几个如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
/**
* This ViewHolder has been bound to a position; mPosition, mItemId and mItemViewType
* are all valid.
*/
static final int FLAG_BOUND = 1 << 0;
/**
* The data this ViewHolder's view reflects is stale and needs to be rebound
* by the adapter. mPosition and mItemId are consistent.
*/
static final int FLAG_UPDATE = 1 << 1;
/**
* This ViewHolder's data is invalid. The identity implied by mPosition and mItemId
* are not to be trusted and may no longer match the item view type.
* This ViewHolder must be fully rebound to different data.
*/
static final int FLAG_INVALID = 1 << 2;
/**
* This ViewHolder points at data that represents an item previously removed from the
* data set. Its view may still be used for things like outgoing animations.
*/
static final int FLAG_REMOVED = 1 << 3;
/**
* This ViewHolder should not be recycled. This flag is set via setIsRecyclable()
* and is intended to keep views around during animations.
*/
static final int FLAG_NOT_RECYCLABLE = 1 << 4;

int mFlags;
void addFlags(int flags) {
mFlags |= flags;
}
  • FLAG_BOUND:viewHolder与RV中POSITION绑定了,就是调用了onBindViewHolder方法吧

  • FLAG_INVALID:onBindViewHolder的数据失效了,需要重新绑定

  • FLAG_UPDATE:viewHolder的view需要重新更新数据

关于缓存的一些关键的类介绍完了,接下来开始分析具体的使用场景中这几个缓存是怎么发挥作用的

调用setAdapter方法

setAdapter这个方法可以多次调用,第一次调用显然没有任何缓存可言,这里只考虑第一次调用后再次调用的情况,如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
//=====RecyclerView中
public void setAdapter(@Nullable Adapter adapter) {
// bail out if layout is frozen
setLayoutFrozen(false);
setAdapterInternal(adapter, false, true);
processDataSetCompletelyChanged(false);
requestLayout();
}

private void setAdapterInternal(@Nullable Adapter adapter, boolean compatibleWithPrevious,
boolean removeAndRecycleViews) {
if (mAdapter != null) {
mAdapter.unregisterAdapterDataObserver(mObserver);
mAdapter.onDetachedFromRecyclerView(this);
}
if (!compatibleWithPrevious || removeAndRecycleViews) {
removeAndRecycleViews();
}
mAdapterHelper.reset();
final Adapter oldAdapter = mAdapter;
mAdapter = adapter;
if (adapter != null) {
adapter.registerAdapterDataObserver(mObserver);
adapter.onAttachedToRecyclerView(this);
}
if (mLayout != null) {
mLayout.onAdapterChanged(oldAdapter, mAdapter);
}
mRecycler.onAdapterChanged(oldAdapter, mAdapter, compatibleWithPrevious);
mState.mStructureChanged = true;
}

/**
* Removes and recycles all views - both those currently attached, and those in the Recycler.
*/
void removeAndRecycleViews() {
......
mRecycler.clear();
}

setAdapter会调用setAdapterInternal(adapter, false, true)方法,从中可以看到RV和Adapter之间是观察者模式,每一次的setAdapter都是RV反过来订阅了Adapter,Adapter通过各种notify**通知RV数据更新。这个方法后面传入的两个参数是字面意思都很好理解,第一个是否和上一个Adapter的类型兼容,第二个是否移除回收views,compatibleWithPrevious为false,所以会调用removeAndRecycleViews方法,在这个方法中又会调用Rcycler的clear方法,好了这就进入正题了,下面看看这个方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
//=====Recycler中
public void clear() {
//清空mAttachedScrap
mAttachedScrap.clear();
recycleAndClearCachedViews();
}

void recycleAndClearCachedViews() {
final int count = mCachedViews.size();
for (int i = count - 1; i >= 0; i--) {
recycleCachedViewAt(i);
}
mCachedViews.clear();
......
}

void recycleCachedViewAt(int cachedViewIndex) {
ViewHolder viewHolder = mCachedViews.get(cachedViewIndex);
addViewHolderToRecycledViewPool(viewHolder, true);
mCachedViews.remove(cachedViewIndex);
}

void addViewHolderToRecycledViewPool(@NonNull ViewHolder holder, boolean dispatchRecycled) {
......
getRecycledViewPool().putRecycledView(holder);
}

一系列的调用可以发现,首先会移除RV中所有的view(代码不贴了,这里只关注回收相关的),然后清空了mAttachedScrap,然后将mCachedViews中的viewholder移动到了RecycledViewPool,之后清空mCachedViews中的viewholder,此时只有RecyclerViewPool中有缓存,但感觉不对,已经换Adapter了所有数据都会变,万一旧的Adapter和新的Adapter的itemType有一样的但viewHolder不一样,那还是会从Pool中去取viewHolder,岂不是会崩?多虑了~回到setAdapterInternal方法中会发现这里最后还调用了mRecycler.onAdapterChanged方法,如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
      void onAdapterChanged(Adapter oldAdapter, Adapter newAdapter,
boolean compatibleWithPrevious) {
if (oldAdapter != null) {
detach(); //mAttachCount--
}
if (!compatibleWithPrevious && mAttachCount == 0) {
clear();
}
if (newAdapter != null) {
attach();//mAttachCount++
}
}

public void clear() {
for (int i = 0; i < mScrap.size(); i++) {
ScrapData data = mScrap.valueAt(i);
data.mScrapHeap.clear();
}
}

由于一个RecyclerViewPool可以设置给多个RV,mAttachCount表示这个Pool关联的RV的数量,当mAttachCount为0时就清空Pool中的所有的viewHolder,所以最后三种缓存都给清空了,如图所示:

setAdapter.png

调用setAdapter方法缓存的更新就分析完了,可以总结为:

  • 不给RV设置RecyclerViewPool的情况下,如果重新setAdapter会清空所有的三级缓存

  • 如果单独设置RecyclerViewPool,并且多个RV同时在使用,会将viewHolder移动最多5个到RecyclerViewPool

调用notifyDataChanged方法

在日常使用中一旦给RV设置了Adapter,一般都不会去再次设置adapter,更多时候是调用Adapter的notify***方法更新数据,现在就来分析一下调用notifyDataChanged时缓存的情况。刚才提到过RV与Adapter之间是观察者模式,Adapter是被观察者、RV是观察者,当我们调用adapter的notifyDataChanged时最后会调用到RV内部的RecyclerViewDataObserver的onChanged方法,如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
//==RecyclerView中
private class RecyclerViewDataObserver extends AdapterDataObserver {
RecyclerViewDataObserver() {
}
@Override
public void onChanged() {
assertNotInLayoutOrScroll(null);
mState.mStructureChanged = true;
processDataSetCompletelyChanged(true);
if (!mAdapterHelper.hasPendingUpdates()) {
requestLayout();
}
}
}

void processDataSetCompletelyChanged(boolean dispatchItemsChanged) {
mDispatchItemsChangedEvent |= dispatchItemsChanged;
mDataSetHasChangedAfterLayout = true;
markKnownViewsInvalid();
}
void markKnownViewsInvalid() {
//getUnfilteredChildCount(): Returns the total number of children.
final int childCount = mChildHelper.getUnfilteredChildCount();
for (int i = 0; i < childCount; i++) {
final ViewHolder holder = getChildViewHolderInt(mChildHelper.getUnfilteredChildAt(i));
if (holder != null && !holder.shouldIgnore()) {
holder.addFlags(ViewHolder.FLAG_UPDATE | ViewHolder.FLAG_INVALID);
}
}
markItemDecorInsetsDirty();
mRecycler.markKnownViewsInvalid();
}

//==Recycler中
void markKnownViewsInvalid() {
final int cachedCount = mCachedViews.size();
for (int i = 0; i < cachedCount; i++) {
final ViewHolder holder = mCachedViews.get(i);
if (holder != null) {
holder.addFlags(ViewHolder.FLAG_UPDATE | ViewHolder.FLAG_INVALID);
holder.addChangePayload(null);
}
}

if (mAdapter == null || !mAdapter.hasStableIds()) {
// we cannot re-use cached views in this case. Recycle them all
recycleAndClearCachedViews();
}
}

最后会调用到RV的markKnownViewsInvalid方法,在这里会取出所有被添加到RV中viewHoder,然后给它们添加上ViewHolder.FLAG_UPDATE和ViewHolder.FLAG_INVALID的FLAG,这两个标记上面介绍了,标记这个viewHolder的数据、位置失效,接着又调用Recycler的同名方法将mCachedViews中的viewHolder标记为失效,然后再调用recycleAndClearCachedViews方法,这个方法上面也提到过,就是将mCachedViews中的viewHolder移动到了RecyclerViewPool中去。

此刻可以总结为:RV中添加的viewHoder仍然在,但被打了标记,mAttachedScrap为空,mCachedViews为空,RecyclerViewPool中可能有缓存。

回到onChanged方法中,最后会调用requestLayout重新布局,requestLayout会触发RV重新measure、layout、draw,在layout的时候将所有的布局都交给了LayoutManager,调用它的onLayoutChildren方法,以LinearLayoutManager为例,在真正进行布局之前会调用detachAndScrapAttachedViews方法,顾名思义就是要detach掉RV中所有的viewHolder

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
//==LinearLayoutManager中
public void detachAndScrapAttachedViews(@NonNull Recycler recycler) {
final int childCount = getChildCount();
for (int i = childCount - 1; i >= 0; i--) {
final View v = getChildAt(i);
scrapOrRecycleView(recycler, i, v);
}
}

private void scrapOrRecycleView(Recycler recycler, int index, View view) {
final ViewHolder viewHolder = getChildViewHolderInt(view);
.......
//成立
if (viewHolder.isInvalid() && !viewHolder.isRemoved()
&& !mRecyclerView.mAdapter.hasStableIds()) {
removeViewAt(index);
recycler.recycleViewHolderInternal(viewHolder);
} else {
detachViewAt(index);
recycler.scrapView(view);
mRecyclerView.mViewInfoStore.onViewDetached(viewHolder);
}
}
//==Recycler中
void recycleViewHolderInternal(ViewHolder holder) {
.......
boolean cached = false;
boolean recycled = false;
.......
if (forceRecycle || holder.isRecyclable()) {
//不成立
if (mViewCacheMax > 0
&& !holder.hasAnyOfTheFlags(ViewHolder.FLAG_INVALID
| ViewHolder.FLAG_REMOVED
| ViewHolder.FLAG_UPDATE
| ViewHolder.FLAG_ADAPTER_POSITION_UNKNOWN)) {
// Retire oldest cached view
int cachedViewSize = mCachedViews.size();
if (cachedViewSize >= mViewCacheMax && cachedViewSize > 0) {
recycleCachedViewAt(0);
cachedViewSize--;
}
int targetCacheIndex = cachedViewSize;
......
mCachedViews.add(targetCacheIndex, holder);
cached = true;
}

if (!cached) {//成立
addViewHolderToRecycledViewPool(holder, true);
recycled = true;
}
} else {
.....
}
....
}

这一系列的调用中会根据viewHolder的flag将它放到不同的缓存中去,这时这些被标记的viewHolder的flags会导致它被RV移除并被移动到RecyclerViewPool中去,如图所示:

notifyDataChanged.png

到这里就明确了在调用notifyDataChanged的时候,RV中所有的viewHolder都会标记失效并被移除到RecyclerPool中去,mCachedView、mAttchedView都为空。

调用notifyItemChanged方法

和notifyDataChanged调用一样,调用链如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
//==RV中
@Override
public void onItemRangeChanged(int positionStart, int itemCount, Object payload) {
assertNotInLayoutOrScroll(null);
if (mAdapterHelper.onItemRangeChanged(positionStart, itemCount, payload)) {
triggerUpdateProcessor();
}
}
//==AdapterHelper中
boolean onItemRangeChanged(int positionStart, int itemCount, Object payload) {
if (itemCount < 1) {
return false;
}
mPendingUpdates.add(obtainUpdateOp(UpdateOp.UPDATE, positionStart, itemCount, payload));
mExistingUpdateTypes |= UpdateOp.UPDATE;
return mPendingUpdates.size() == 1;
}
//AdapterHelper中,mCallback是RV 
private void postponeAndUpdateViewHolders(UpdateOp op) {
if (DEBUG) {
Log.d(TAG, "postponing " + op);
}
mPostponedList.add(op);
switch (op.cmd) {
case UpdateOp.ADD:
mCallback.offsetPositionsForAdd(op.positionStart, op.itemCount);
break;
case UpdateOp.MOVE:
mCallback.offsetPositionsForMove(op.positionStart, op.itemCount);
break;
case UpdateOp.REMOVE:
mCallback.offsetPositionsForRemovingLaidOutOrNewView(op.positionStart, op.itemCount);
break;
case UpdateOp.UPDATE:
mCallback.markViewHoldersUpdated(op.positionStart, op.itemCount, op.payload);
break;
default:
throw new IllegalArgumentException("Unknown update op type for " + op);
}
}

//==RV中
@Override
public void markViewHoldersUpdated(int positionStart, int itemCount, Object payload) {
viewRangeUpdate(positionStart, itemCount, payload);
mItemsChanged = true;
}

void viewRangeUpdate(int positionStart, int itemCount, Object payload) {
final int childCount = mChildHelper.getUnfilteredChildCount();
final int positionEnd = positionStart + itemCount;
for (int i = 0; i < childCount; i++) {
final View child = mChildHelper.getUnfilteredChildAt(i);
final ViewHolder holder = getChildViewHolderInt(child);
if (holder == null || holder.shouldIgnore()) {
continue;
}
if (holder.mPosition >= positionStart && holder.mPosition < positionEnd) {
// We re-bind these view holders after pre-processing is complete so that
// ViewHolders have their final positions assigned.
holder.addFlags(ViewHolder.FLAG_UPDATE);
holder.addChangePayload(payload);
// lp cannot be null since we get ViewHolder from it.
((LayoutParams) child.getLayoutParams()).mInsetsDirty = true;
}
}
mRecycler.viewRangeUpdate(positionStart, itemCount);
}

AdapterHelper的onItemRangeChanged将这个更新事件添加到一个排队执行的List中,triggerUpdateProcessor或者其他事件会触发List的中的事件被执行,不管怎么调用最后都会调用到AdapterHelper的postponeAndUpdateViewHolders,在这里会将需要更新的viewHolder标记为ViewHolder.FLAG_UPDATE,这些都是在RV真正开始布局之前调用的,之后会走正常的layout流程,又以LienarLayoutManager为例,还是会调用detachAndScrapAttachedViews,但这次只有需要更新的viewHolder被标记为FLAG_UPDATE了,其他都没有格外标记,回收的逻辑如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38

//==RV中
private void scrapOrRecycleView(Recycler recycler, int index, View view) {
final ViewHolder viewHolder = getChildViewHolderInt(view);
.......
if (viewHolder.isInvalid() && !viewHolder.isRemoved()
&& !mRecyclerView.mAdapter.hasStableIds()) {
removeViewAt(index);
recycler.recycleViewHolderInternal(viewHolder);
} else {//成立
detachViewAt(index);
recycler.scrapView(view);
mRecyclerView.mViewInfoStore.onViewDetached(viewHolder);
}
}
//==Recycler中
void scrapView(View view) {
final ViewHolder holder = getChildViewHolderInt(view);
//对不需要更新的viewHolder成立
if (holder.hasAnyOfTheFlags(ViewHolder.FLAG_REMOVED | ViewHolder.FLAG_INVALID)
|| !holder.isUpdated() || canReuseUpdatedViewHolder(holder)) {
//不成立
if (holder.isInvalid() && !holder.isRemoved() && !mAdapter.hasStableIds()) {
throw new IllegalArgumentException("Called scrap view with an invalid view."
+ " Invalid views cannot be reused from scrap, they should rebound from"
+ " recycler pool." + exceptionLabel());
}
holder.setScrapContainer(this, false);
mAttachedScrap.add(holder);
} else { //对需要更新的viewHolder成立
if (mChangedScrap == null) {
mChangedScrap = new ArrayList<ViewHolder>();
}
holder.setScrapContainer(this, true);
mChangedScrap.add(holder);
}
}

RV中所有的viewHolder都会被detach,对不需要更新的viewHolder放到了mAttachedScrap中,对需要更新的viewHolder是放到了mChangedScrap中,其他的缓存没有变,如图所示:

notifyItemChanged.png

调用notifyItemInserted方法

和notifyItemChanged一样,在layout之前会执行AdapterHelper中的postponeAndUpdateViewHolders,这次是UpdateOp.ADD了,所以会调用RV的offsetPositionsForAdd,如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45

@Override
public void offsetPositionsForAdd(int positionStart, int itemCount) {
offsetPositionRecordsForInsert(positionStart, itemCount);
mItemsAddedOrRemoved = true;
}



void offsetPositionRecordsForInsert(int positionStart, int itemCount) {
final int childCount = mChildHelper.getUnfilteredChildCount();
for (int i = 0; i < childCount; i++) {
final ViewHolder holder = getChildViewHolderInt(mChildHelper.getUnfilteredChildAt(i));
if (holder != null && !holder.shouldIgnore() && holder.mPosition >= positionStart) {
if (DEBUG) {
Log.d(TAG, "offsetPositionRecordsForInsert attached child " + i + " holder "
+ holder + " now at position " + (holder.mPosition + itemCount));
}
//改变ViewHolder的位置
holder.offsetPosition(itemCount, false);
mState.mStructureChanged = true;
}
}
mRecycler.offsetPositionRecordsForInsert(positionStart, itemCount);
requestLayout();
}


//==ViewHolder中
void offsetPosition(int offset, boolean applyToPreLayout) {
if (mOldPosition == NO_POSITION) {
mOldPosition = mPosition;
}
if (mPreLayoutPosition == NO_POSITION) {
mPreLayoutPosition = mPosition;
}
if (applyToPreLayout) {
mPreLayoutPosition += offset;
}
mPosition += offset;
if (itemView.getLayoutParams() != null) {
((LayoutParams) itemView.getLayoutParams()).mInsetsDirty = true;
}
}

这个过程只是改变了ViewHolder的位置,没有改变它的状态,参考刚才对notifyItemChanged的分析,RV重新layout时也只是将所有的viewHolder detach了,然后放进mAttachedScrap中,只是这一次viewHolder中代表位置的mPosition值发生了变化。

滑动

具体的分析参见RecyclerView缓存机制(回收些啥?)

可以概括为:在滑动的时候 ,触发RV的onTouchEvent,在它的ACTION_MOVE中,如果这个滑动被认为是一次SCROLL_STATE_DRAGGING就会将这个滑动事件转发给LayoutMamager,以LinearLayoutManager为例,会调用其中的fill方法去添加新的viewHolder到RV中,同时也会detach和回收掉滑出屏幕的viewHolder,fill方法中会依次调用新的item的measure和layout方法,最后在scrollBy方法中会移动所有的item,导致item的invalidate被调用,绘制view。

对滑出屏幕的viewHoler会调用recycleChildren回收,如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49

//==RV中

private void recycleChildren(RecyclerView.Recycler recycler, int startIndex, int endIndex) {
if (startIndex == endIndex) {
return;
}

if (DEBUG) {
Log.d(TAG, "Recycling " + Math.abs(startIndex - endIndex) + " items");
}
if (endIndex > startIndex) {
for (int i = endIndex - 1; i >= startIndex; i--) {
removeAndRecycleViewAt(i, recycler);
}
} else {
for (int i = startIndex; i > endIndex; i--) {
removeAndRecycleViewAt(i, recycler);
}
}
}



public void removeAndRecycleViewAt(int index, @NonNull Recycler recycler) {
final View view = getChildAt(index);
//detach
removeViewAt(index);
recycler.recycleView(view);
}

//==Recycler中
public void recycleView(@NonNull View view) {
// This public recycle method tries to make view recycle-able since layout manager
// intended to recycle this view (e.g. even if it is in scrap or change cache)
ViewHolder holder = getChildViewHolderInt(view);
if (holder.isTmpDetached()) {
removeDetachedView(view, false);
}
if (holder.isScrap()) {
holder.unScrap();
} else if (holder.wasReturnedFromScrap()) {
holder.clearReturnedFromScrapFlag();
}
recycleViewHolderInternal(holder);
}



移出屏幕的viewHolder会被detach,然后调用Recycler的recycleView方法回收,这里又调用到了

recycleViewHolderInternal方法,这次会被放到mCachedView中去,如果mCachedView满了就把以前的发到Pool中去,再把这个viewHolder放进mCachedView中。

复用

前面提到过在LinearLayoutManager的fill方法会去添加新的viewHolder到RV中,那这个viewHolder从哪里来呢?显然是要从缓存中获取的,Recycler提供了一个叫tryGetViewHolderForPositionByDeadline的方法获取缓存的viewHolder,具体分析请看

RecyclerView缓存机制(咋复用?)

可以概括为: RV在每一次需要为某个位置添加viewHolder的时候都会通过这个方法去获取viewHolder,首先是根据position在mAttchScrap中去寻找,没有再根据position去mCachedViews中寻找,如果找到了直接返回使用,没有再去RecyclerViewPool中根据itemType寻找。

参考:

Android ListView与RecyclerView对比浅析–缓存机制

RecyclerView缓存机制