从RecyclerView的名字就可以看出,它的主要作用就是对View的回收。相比较ListView它的优势是它本身不需要关心视图的问题,它不需要关心如何将子View放在合适的位置,不需要关心如何分割这些子View,更不用关心这些子View的外观。它要做的仅仅是回收和复用的工作。现在我们就来看一下如此优秀强大的RecyclerView它负责的工作都是怎样展开的。
在开始之前先来看一下关于RecyclerView的灵魂三问,带着这三个问题去源码中找答案,这样可以更好的理解源码,理解它的工作流程。
问题一:RecyclerView回收的是什么?复用的是什么?
问题二:回收来的东西放到了哪里?复用的东西又是从哪里取得的?
问题三:什么时候回收?什么时候复用?
之所以将回收和复用设计出来,其原因一定是为了提升有很多数据的列表的性能。所以,回收和复用一定是和滑动有关的。因此,我们就从RecyclerView的onTouchEvent的滑动事件开始分析。
我们先来看一下回收机制。
回收:当一个itemView从可见到不可见时,RecyclerView利用回收机制将itemView存放到缓存池中,当其他itemView出现时,不需要每次都去创建一个新的itemView,可以只是onBindViewHolder绑定数据就可以了。
在Action_Move事件中,调用了RecyclerView的scrollByInternal方法。代码如下:
/**
* Does not perform bounds checking. Used by internal methods that have already validated input.
*
* It also reports any unused scroll request to the related EdgeEffect.
*
* @param x The amount of horizontal scroll request
* @param y The amount of vertical scroll request
* @param ev The originating MotionEvent, or null if not from a touch event.
*
* @return Whether any scroll was consumed in either direction.
*/
boolean scrollByInternal(int x, int y, MotionEvent ev) {
if (mAdapter != null) {
eatRequestLayout();
onEnterLayoutOrScroll();
TraceCompat.beginSection(TRACE_SCROLL_TAG);
fillRemainingScrollValues(mState);
if (x != 0) {
// 关键代码1
consumedX = mLayout.scrollHorizontallyBy(x, mRecycler, mState);
unconsumedX = x - consumedX;
}
if (y != 0) {
// 关键代码2
consumedY = mLayout.scrollVerticallyBy(y, mRecycler, mState);
unconsumedY = y - consumedY;
}
}
return consumedX != 0 || consumedY != 0;
}
scrollByInternal方法的内部关键部分就是根据参数x或者y调用了mLayout的scrollHorizontallyBy或者mLayout.scrollVerticallyBy方法。mLayout就是LayoutManager的实例对象。LayoutManager是一个抽象类,我们不妨直接看一下它的子类,LinearLayoutManager。刚才说到调用了mLayout的scrollHorizontallyBy/scrollVerticallyBy方法,现在直接看一下LinearLayoutManager中这两个方法的实现。代码不再贴出来,两者都是调用了scrollBy方法。在这个scrollBy方法中,调用了fill方法。现在看一下fill方法的代码。
int fill(RecyclerView.Recycler recycler, LayoutState layoutState,
RecyclerView.State state, boolean stopOnFocusable) {
// 代码省略。。。
if (layoutState.mScrollingOffset != LayoutState.SCROLLING_OFFSET_NaN) {
// 关键代码1
recycleByLayoutState(recycler, layoutState);
}
// 代码省略。。。
// 关键代码2
layoutChunk(recycler, state, layoutState, layoutChunkResult);
// 。。。。
}
关键处就两个地方,第一个recycleByLayoutState,通过方法名,大概意思就是通过布局状态回收。第二个关键点就是layoutChunk,这个方法跟复用有关系。先来看一个recycleByLayoutState(回收)。代码跟进:
/**
* Helper method to call appropriate recycle method depending on current layout direction
*
* @param recycler Current recycler that is attached to RecyclerView
* @param layoutState Current layout state. Right now, this object does not change but
* we may consider moving it out of this view so passing around as a
* parameter for now, rather than accessing {@link #mLayoutState}
* @see #recycleViewsFromStart(android.support.v7.widget.RecyclerView.Recycler, int)
* @see #recycleViewsFromEnd(android.support.v7.widget.RecyclerView.Recycler, int)
* @see android.support.v7.widget.LinearLayoutManager.LayoutState#mLayoutDirection
*/
private void recycleByLayoutState(RecyclerView.Recycler recycler, LayoutState layoutState) {
if (!layoutState.mRecycle || layoutState.mInfinite) {
return;
}
if (layoutState.mLayoutDirection == LayoutState.LAYOUT_START) {
recycleViewsFromEnd(recycler, layoutState.mScrollingOffset);
} else {
recycleViewsFromStart(recycler, layoutState.mScrollingOffset);
}
}
方法作用是,根据当前的布局方向调用适当的回收方法。我们只挑一个方向说明,recycleViewsFromStart方法,其中调用了recycleChildren方法。recycleChildren方法的代码如下所示:
/**
* Recycles children between given indices.
*
* @param startIndex inclusive
* @param endIndex exclusive
*/
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);
}
}
}
方法作用是,回收指点位置之间的子View。直接看removeAndRecycleViewAt方法。代码如下:
/**
* Remove a child view and recycle it using the given Recycler.
*
* @param index Index of child to remove and recycle
* @param recycler Recycler to use to recycle child
*/
public void removeAndRecycleViewAt(int index, Recycler recycler) {
final View view = getChildAt(index);
removeViewAt(index);
recycler.recycleView(view);
}
该方法的作用是,移除一个子View,并且使用给定的Recycler回收它。到现在终于看到了,调用回收的方法recycleView。其代码如下:
/**
* Recycle a detached view. The specified view will be added to a pool of views
* for later rebinding and reuse.
*
* A view must be fully detached (removed from parent) before it may be recycled. If the
* View is scrapped, it will be removed from scrap list.
*
* @param view Removed view for recycling
* @see LayoutManager#removeAndRecycleView(View, Recycler)
*/
public void recycleView(View view) {
// 。。。。
// 关键代码
recycleViewHolderInternal(holder);
}
recycleView的作用是,回收分离的视图。指定的视图将被添加到一个视图池中,以供以后重新绑定和重用。其关键代码就是最后的recycleViewHolderInternal方法。代码如下:
/**
* internal implementation checks if view is scrapped or attached and throws an exception
* if so.
* Public version un-scraps before calling recycle.
*/
void recycleViewHolderInternal(ViewHolder holder) {
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) {
// 关键代码1。。。
recycleCachedViewAt(0);
cachedViewSize--;
}
int targetCacheIndex = cachedViewSize;
if (ALLOW_THREAD_GAP_WORK
&& cachedViewSize > 0
&& !mPrefetchRegistry.lastPrefetchIncludedPosition(holder.mPosition)) {
// when adding the view, skip past most recently prefetched views
int cacheIndex = cachedViewSize - 1;
while (cacheIndex >= 0) {
int cachedPos = mCachedViews.get(cacheIndex).mPosition;
if (!mPrefetchRegistry.lastPrefetchIncludedPosition(cachedPos)) {
break;
}
cacheIndex--;
}
targetCacheIndex = cacheIndex + 1;
}
// 关键代码2。。。
mCachedViews.add(targetCacheIndex, holder);
cached = true;
}
if (!cached) {
// 关键代码3。。。
addViewHolderToRecycledViewPool(holder, true);
recycled = true;
}
} else {
// 。。。。
}
// 。。。
}
关键代码有三处。看第一处其执行的条件是mCacheViews的大小大于mViewCacheMax(默认为2)且mCacheViews不为空。mCacheViews是一个泛型为ViewHodler的ArrayList集合,至此我们就能知道,所谓的回收和复用其实就是针对ViewHolder而言的。关键1处,recycleCachedViewAt方法,代码如下:
void recycleCachedViewAt(int cachedViewIndex) {
if (DEBUG) {
Log.d(TAG, "Recycling cached view at index " + cachedViewIndex);
}
ViewHolder viewHolder = mCachedViews.get(cachedViewIndex);
if (DEBUG) {
Log.d(TAG, "CachedViewHolder to be recycled: " + viewHolder);
}
addViewHolderToRecycledViewPool(viewHolder, true);
mCachedViews.remove(cachedViewIndex);
}
代码比较少,逻辑就是先从mCacheViews集合中拿出相应位置的ViewHolder对象,再在将其从mCacheViews集合中移除之前,此ViewHolder作为addViewHolderToRecycledViewPool方法的参数执行了相应的操作。最后,将其从mCacheViews集合中删除了。现在我们看一下addViewHolderToRecycledViewPool方法做了些什么。代码如下所示:
/**
* Prepares the ViewHolder to be removed/recycled, and inserts it into the RecycledViewPool.
*
* Pass false to dispatchRecycled for views that have not been bound.
*
* @param holder Holder to be added to the pool.
* @param dispatchRecycled True to dispatch View recycled callbacks.
*/
void addViewHolderToRecycledViewPool(ViewHolder holder, boolean dispatchRecycled) {
//。。。
if (dispatchRecycled) {
// 代码1
dispatchViewRecycled(holder);
}
holder.mOwnerRecyclerView = null;
// 代码
getRecycledViewPool().putRecycledView(holder);
}
该方法作用是:准备好ViewHolder以被移除或回收,并且将其出入到RecyclerViewPool中。此方法中,有两个关键点,第一就是dispatchViewRecycled方法对此holder的处理;第二就是getRecycledViewPool().putRecycledView(holder)。dispatchViewRecycled方法代码如下:
void dispatchViewRecycled(ViewHolder holder) {
if (mRecyclerListener != null) {
mRecyclerListener.onViewRecycled(holder);
}
if (mAdapter != null) {
// 调用Adapter的view回收方法
mAdapter.onViewRecycled(holder);
}
if (mState != null) {
mViewInfoStore.removeViewHolder(holder);
}
if (DEBUG) Log.d(TAG, "dispatchViewRecycled: " + holder);
}
到此,该ViewHolder就被Adapter回收了。下面接着看,上面的关键代码2。直接跟进代码就是了。
public void putRecycledView(ViewHolder scrap) {
final int viewType = scrap.getItemViewType();
final ArrayList 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);
}
这个方法中重要的就是getScrapDataForType方法了。其代码如下:
private ScrapData getScrapDataForType(int viewType) { ScrapData scrapData = mScrap.get(viewType); if (scrapData == null) { scrapData = new ScrapData(); // 关键代码 mScrap.put(viewType, scrapData); } return scrapData; }
这一方法关键之处就是往SparseArray类型的mScrap中添加了以viewType为key,scrapData为value的对象。mScrap是RecycledViewPool中维护的一个SparseArray结构的map集合。回收来的东西放到了这里。
至此,RecyclerView的回收机制介绍完了。下面接着分析复用机制。
上面分析回收机制的时候提到了fill方法,其中有两个关键点一个是recycleByLayoutState方法,另一个是layoutChunk方法。回收机制就是从recycleByLayoutState开始,现在我们分析回收机制的入口layoutChunk方法,并且我们会按照流程整个分析一遍。代码如下:
void layoutChunk(RecyclerView.Recycler recycler, RecyclerView.State state,
LayoutState layoutState, LayoutChunkResult result) {
// 关键代码next方法。。。
View view = layoutState.next(recycler);
// 。。。。
LayoutParams params = (LayoutParams) view.getLayoutParams();
if (layoutState.mScrapList == null) {
if (mShouldReverseLayout == (layoutState.mLayoutDirection
== LayoutState.LAYOUT_START)) {
// 添加View
addView(view);
} else {
// 添加View
addView(view, 0);
}
} else {
// 。。。。
}
// 测量childView
measureChildWithMargins(view, 0, 0);
// 。。。。
// To calculate correct layout position, we subtract margins.
// 布局childView
layoutDecoratedWithMargins(view, left, top, right, bottom);
// 。。。
}
layoutChunk方法代码很长,但是关键的几点已经在源码中标注出来了。首先就是LayoutState的next方法。next方法作用有两点:
第一:获取要布局的下一个元素的视图。
第二:将当前项的索引更新到下一项。
代码如下:
View next(RecyclerView.Recycler recycler) {
if (mScrapList != null) {
return nextViewFromScrapList();
}
// 关键代码。。。
final View view = recycler.getViewForPosition(mCurrentPosition);
mCurrentPosition += mItemDirection;
return view;
}
next方法代码不多,关键点也就一个,那就是recycler的getViewForPosition方法。在分析复用机制之前有必要先来看一下Recycler类中的主要代码。
public final class Recycler {
final ArrayList mAttachedScrap = new ArrayList<>();
ArrayList mChangedScrap = null;
final ArrayList mCachedViews = new ArrayList();
private final List
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;
// 。。。。
}
类的代码很多(得有1000行),但是结构不太复杂。复用涉及到的四级缓存都在这里有声明。
一级缓存:mAttachScrap,mChangedScrap
二级缓存:mCacheViews
三级缓存:mViewCacheExtension(自定义缓存)
四级缓存:mRecyclerPool(缓存池)。
了解了Recycler类中的四级缓存后,我们接着上面的分析继续往下走。getViewforPosition方法最终会调用到tryGetViewHolderForPositionByDeadline方法,看一下其中的实现。代码如下:
/**
* Attempts to get the ViewHolder for the given position, either from the Recycler scrap,
* cache, the RecycledViewPool, or creating it directly.
*/
@Nullable
ViewHolder tryGetViewHolderForPositionByDeadline(int position,
boolean dryRun, long deadlineNs) {
if (mState.isPreLayout()) {
// 关键代码1。。。
holder = getChangedScrapViewForPosition(position);
fromScrapOrHiddenOrCache = holder != null;
}
// 1) Find by position from scrap/hidden list/cache
if (holder == null) {
// 关键代码2。。。
holder = getScrapOrHiddenOrCachedHolderForPosition(position, dryRun);
if (holder != null) {
// 关键代码3。。。
if (!validateViewHolderForOffsetPosition(holder)) {
// recycle holder (and unscrap if relevant) since it can't be used
if (!dryRun) {
// we would like to recycle this but need to make sure it is not used by
// animation logic etc.
holder.addFlags(ViewHolder.FLAG_INVALID);
if (holder.isScrap()) {
removeDetachedView(holder.itemView, false);
holder.unScrap();
} else if (holder.wasReturnedFromScrap()) {
holder.clearReturnedFromScrapFlag();
}
// 回收方法
recycleViewHolderInternal(holder);
}
holder = null;
}
}
}
if (holder == null) {
// 获取当前position对应的item的type
final int type = mAdapter.getItemViewType(offsetPosition);
// 2) Find from scrap/cache via stable ids, if exists
if (mAdapter.hasStableIds()) {
// 关键代码4。。。
holder = getScrapOrCachedViewForId(mAdapter.getItemId(offsetPosition),
type, dryRun);
}
// 关键代码5。。。(自定义缓存)
if (holder == null && mViewCacheExtension != null) {
// We are NOT sending the offsetPosition because LayoutManager does not
// know it.
// 通过getViewForPositionAndType来获取view
final View view = mViewCacheExtension
.getViewForPositionAndType(this, position, type);
if (view != null) {
holder = getChildViewHolder(view);
//。。。
}
}
if (holder == null) { // fallback to pool
if (DEBUG) {
Log.d(TAG, "tryGetViewHolderForPositionByDeadline("
+ position + ") fetching from shared pool");
}
// 关键代码6。。。
holder = getRecycledViewPool().getRecycledView(type);
if (holder != null) {
holder.resetInternal();
if (FORCE_INVALIDATE_DISPLAY_LIST) {
invalidateDisplayListInt(holder);
}
}
}
if (holder == null) {
long start = getNanoTime();
if (deadlineNs != FOREVER_NS
&& !mRecyclerPool.willCreateInTime(type, start, deadlineNs)) {
// abort - we have a deadline we can't meet
return null;
}
// 关键代码7。。。创建ViewHolder
holder = mAdapter.createViewHolder(RecyclerView.this, type);
}
}
// 代码省略。。。。
return holder;
}
注释中对它功能的描述是:尝试获取给定位置的ViewHolder,可以从回收器scrap,cache,RecyclerViewPool获取,也可以直接创建。该方法代码很多,即便精简下来也比较多。现在针对它关键的几处进行分析。
关键代码1:getChangedScrapViewHolderForPosition方法。代码如下:
ViewHolder getChangedScrapViewForPosition(int position) {
// If pre-layout, check the changed scrap for an exact match.
final int changedScrapSize;
if (mChangedScrap == null || (changedScrapSize = mChangedScrap.size()) == 0) {
return null;
}
// find by position
for (int i = 0; i < changedScrapSize; i++) {
// 通过position从mChangedScrap中获取ViewHolder
final ViewHolder holder = mChangedScrap.get(i);
if (!holder.wasReturnedFromScrap() && holder.getLayoutPosition() == position) {
holder.addFlags(ViewHolder.FLAG_RETURNED_FROM_SCRAP);
return holder;
}
}
// find by id
if (mAdapter.hasStableIds()) {
final int offsetPosition = mAdapterHelper.findPositionOffset(position);
if (offsetPosition > 0 && offsetPosition < mAdapter.getItemCount()) {
final long id = mAdapter.getItemId(offsetPosition);
for (int i = 0; i < changedScrapSize; i++) {
// 通过stableIds从mChangedScrap中查找ViewHolder
final ViewHolder holder = mChangedScrap.get(i);
if (!holder.wasReturnedFromScrap() && holder.getItemId() == id) {
holder.addFlags(ViewHolder.FLAG_RETURNED_FROM_SCRAP);
return holder;
}
}
}
}
return null;
}
它的作用就是通过position和stableIds从Recycler中的mChangedScrap中查找ViewHolder。
关键2:getScrapOrHiddenOrCachedHolderForPosition方法。见名知意,其作用是从scrap或隐藏列表或缓存中通过position查找ViewHolder。代码如下:
/**
* Returns a view for the position either from attach scrap, hidden children, or cache.
*
* @param position Item position
* @param dryRun Does a dry run, finds the ViewHolder but does not remove
* @return a ViewHolder that can be re-used for this position.
*/
ViewHolder getScrapOrHiddenOrCachedHolderForPosition(int position, boolean dryRun) {
final int scrapCount = mAttachedScrap.size();
// Try first for an exact, non-invalid match from scrap.
// 先从mAttachScrap中查找
for (int i = 0; i < scrapCount; i++) {
final ViewHolder holder = mAttachedScrap.get(i);
return holder;
}
}
// dryRun为false
if (!dryRun) {
// 从HiddenView中获得View
View view = mChildHelper.findHiddenNonRemovedView(position);
if (view != null) {
// This View is good to be used. We just need to unhide, detach and move to the
// scrap list.
// 通过View的LayoutParams获得ViewHolder
final ViewHolder vh = getChildViewHolderInt(view);
// 从HiddenView中移除
mChildHelper.unhide(view);
。。。
mChildHelper.detachViewFromParent(layoutIndex);
// 回收到scrap中
scrapView(view);
return vh;
}
}
// Search in our first-level recycled view cache.
// 从CacheView中获取
final int cacheSize = mCachedViews.size();
for (int i = 0; i < cacheSize; i++) {
final ViewHolder holder = mCachedViews.get(i);
// invalid view holders may be in cache if adapter has stable ids as they can be
// retrieved via getScrapOrCachedViewForId
// 判断ViewHolder是否有效,position是否相同
if (!holder.isInvalid() && holder.getLayoutPosition() == position) {
if (!dryRun) {
mCachedViews.remove(i);
}
。。。。
return holder;
}
}
return null;
}
首先就是从mScrap中获取,如果没有就从hiddenView中获取,最后从CacheView中获取,在CacheView中获取到之后,进行判断ViewHolder是否有效,position是否相同。
关键代码3:就是检查当前ViewHolder与当前的position是否相匹配。
回收方法recycleViewHolderInternal中,做的操作就是,将得到的ViewHolder放置到mCacheViews中或者RecycledViewPool缓存池中。
关键代码4:getScrapOrCachedViewForId方法。作用就是通过id从Scrap或者CachedView中获取ViewHolder。在调用此方法之前,调用了getItemViewType方法,这个是对应RecyclerVIew多样式条目的情况,从此方法可以获取当前item的条目类型。现在看一下getScrapOrCachedViewForId方法的代码。
ViewHolder getScrapOrCachedViewForId(long id, int type, boolean dryRun) {
// Look in our attached views first
final int count = mAttachedScrap.size();
for (int i = count - 1; i >= 0; i--) {
// 从mAttchedScrap中获取ViewHolder
final ViewHolder holder = mAttachedScrap.get(i);
if (holder.getItemId() == id && !holder.wasReturnedFromScrap()) {
if (type == holder.getItemViewType()) {
return holder;
} else if (!dryRun) {
// 从scrap中移除
mAttachedScrap.remove(i);
removeDetachedView(holder.itemView, false);
// 加入cachedView或者RecycledViewPool中
quickRecycleScrapView(holder.itemView);
}
}
}
// Search the first-level cache
// 从cachedView中获取
final int cacheSize = mCachedViews.size();
for (int i = cacheSize - 1; i >= 0; i--) {
final ViewHolder holder = mCachedViews.get(i);
if (holder.getItemId() == id) {
if (type == holder.getItemViewType()) {
if (!dryRun) {
mCachedViews.remove(i);
}
return holder;
} else if (!dryRun) {
recycleCachedViewAt(i);
return null;
}
}
}
return null;
}
这里的判断逻辑跟上边getScrapOrHiddenOrCachedHolderForPosition方法中的判断逻辑几乎一致。所以,我认为,第一级缓存,就是mAttachedScrap。刚刚介绍的两个方法就是从mAttachedScrap中根据position或者id这两种方式去获取ViewHolder。第二级缓存,就是从mCachedViews中获取ViewHolder。第一级缓存中,混合了第二级缓存。现在接着往下看。
关键代码5:自定义缓存,通过调用getChildViewHolder方法,getChildViewHolder方法中调用了getChildViewHolderInt方法。其中是通过child的LayoutParams获取ViewHolder。
关键代码6:getRecycledViewPool().getRecycledView(type)。也就是第四级缓存。通过RecycledViewPool来获取ViewHolder。看一下这两个方法的代码。
RecycledViewPool getRecycledViewPool() {
if (mRecyclerPool == null) {
mRecyclerPool = new RecycledViewPool();
}
return mRecyclerPool;
}
public static class RecycledViewPool {
private static final int DEFAULT_MAX_SCRAP = 5;
static class ScrapData {
ArrayList mScrapHeap = new ArrayList<>();
int mMaxScrap = DEFAULT_MAX_SCRAP;
long mCreateRunningAverageNs = 0;
long mBindRunningAverageNs = 0;
}
// 采用了SparseArray数据结构
SparseArray mScrap = new SparseArray<>();
private int mAttachCount = 0;
}
public ViewHolder getRecycledView(int viewType) {
final ScrapData scrapData = mScrap.get(viewType);
if (scrapData != null && !scrapData.mScrapHeap.isEmpty()) {
final ArrayList scrapHeap = scrapData.mScrapHeap;
return scrapHeap.remove(scrapHeap.size() - 1);
}
return null;
}
RecycledViewPool中使用了SparseArray,它类似于一个HashMap,内部的key就是我们的viewType,而value存放的就是ArrayList
最后分析一下关键代码7。经过上述四级缓存之后,如果此时ViewHolder还为null,这样就会调用mAdapter.createViewHolder方法来创建viewHolder了。
至此,我们的复用机制也介绍完了。最后我们来集中看一下最开始提到的那三个问题。
问题一:RecyclerView回收的是什么?复用的是什么?
这个问题,已经回答过了。在整个的过程中我们提到最多的也是这个回收和复用的对象ViewHolder。
问题二:回收来的东西放到了哪里?复用的东西又是从哪里取得的?
回收来的东西放到了RecycledViewPool缓存池中。至于复用的东西从哪里取得的这个问题,想必不用我再回答了吧。复用过程四级缓存不就是讲的这个问题吗。
问题三:什么时候回收?什么时候复用?
回收是在itemView将要消失的时候,复用则发生在itemView由不可见到可见的时候。
最后附上这两个过程的流程图就该结束本文了。这两张图是直接“拿”的现成的。若有侵权请告知,立即删除。
复用过程:
至此,本文就结束了。谢谢各位看官能坚持看完。
在此我特别感谢,这两张流程图的作者波澜不惊非常感谢。