在之前渲染過(guò)程中,layoutChunk
方法調(diào)用View view = layoutState.next(recycler)
代碼,該代碼獲取一個(gè)view視圖抖苦,內(nèi)部包含緩存邏輯领炫。RecyclerView緩存共有四級(jí),以下簡(jiǎn)單說(shuō)下四級(jí)緩存遗增。
層級(jí) | |
---|---|
一級(jí)緩存 | mAttachedScrap:數(shù)據(jù)沒有變化的ViewHolder 叫惊; mChangedScrap:數(shù)據(jù)已經(jīng)改變的 ViewHolder |
二級(jí)緩存 | mCachedViews:滑出屏幕之外的 ViewHolder |
三級(jí)緩存 | mViewCacheExtensions:自定義的緩存池 |
四級(jí)緩存 | mRecyclerPool:mCachedViews無(wú)法存放時(shí)候放置的地方,需要重新onBindViewHolder() |
其他:mHiddenViews 和Recycler無(wú)關(guān)做修。在子view被移出屏幕的動(dòng)畫執(zhí)行期間暫時(shí)緩存viewHolder霍狰。
首先看下LayoutState里面的next方法。
View next(RecyclerView.Recycler recycler) {
//執(zhí)行l(wèi)ayoutForPredictiveAnimations之后mScrapList不為null
//mScrapList 實(shí)質(zhì)為不可修改的mAttachedScrap
//這邊忽略饰及,一般mScrapList==null
if (mScrapList != null) {
return nextViewFromScrapList();
}
//獲取一個(gè)view
final View view = recycler.getViewForPosition(mCurrentPosition);
mCurrentPosition += mItemDirection;
return view;
}
在mScrapList == null 的情況下getViewForPosition方法進(jìn)一步獲取View
@NonNull
public View getViewForPosition(int position) {
return getViewForPosition(position, false);
}
View getViewForPosition(int position, boolean dryRun) {
return tryGetViewHolderForPositionByDeadline(position, dryRun, FOREVER_NS).itemView;
}
ViewHolder tryGetViewHolderForPositionByDeadline(int position,
boolean dryRun, long deadlineNs) {
// ...省略
// 0) If there is a changed scrap, try to find from there
if (mState.isPreLayout()) {
// 預(yù)加載下蔗坯,通過(guò)mChangedScrap獲取ViewHolder
holder = getChangedScrapViewForPosition(position);
fromScrapOrHiddenOrCache = holder != null;
}
// 1) Find by position from scrap/hidden list/cache
if (holder == null) {
// 通過(guò)mAttachedScrap、HiddenView(隱藏的view) 燎含、mCachedViews來(lái)獲取ViewHolder
holder = getScrapOrHiddenOrCachedHolderForPosition(position, dryRun);
//. ..省略
}
if (holder == null) {
//...省略
final int type = mAdapter.getItemViewType(offsetPosition);
// 2) Find from scrap/cache via stable ids, if exists
if (mAdapter.hasStableIds()) {
// stableId設(shè)置為true的時(shí)候宾濒,相當(dāng)于調(diào)用了viewholder中的view的requestFocus()方法。這個(gè)方法的作用是給這個(gè)請(qǐng)求requestFocus()的方法的view的下面的那個(gè)view焦點(diǎn)屏箍。
//根據(jù)ids來(lái)查找ViewHolder绘梦,設(shè)置了id,那么會(huì)忽略標(biāo)志位強(qiáng)行復(fù)用viewHolder赴魁。
holder = getScrapOrCachedViewForId(mAdapter.getItemId(offsetPosition),
type, dryRun);
//...省略
}
if (holder == null && mViewCacheExtension != null) {
// We are NOT sending the offsetPosition because LayoutManager does not
// know it.
//自定義的mViewCacheExtensions緩存獲取ViewHolder
final View view = mViewCacheExtension
.getViewForPositionAndType(this, position, type);
if (view != null) {
holder = getChildViewHolder(view);
//...省略
}
}
if (holder == null) {
holder = getRecycledViewPool().getRecycledView(type);
//mRecyclerPool中獲取ViewHolder
}
//..省略
if (holder == null) {
//..省略
holder = mAdapter.createViewHolder(RecyclerView.this, type);
//..省略
}
//..省略
}
}
//..省略
boolean bound = false;
//bindViewHolder
if (mState.isPreLayout() && holder.isBound()){
bound = tryBindViewHolderByDeadline(holder, offsetPosition, position, deadlineNs);
}
//..省略
return holder;
}
tryGetViewHolderForPositionByDeadline 中的代碼展示完卸奉,這邊展示的是一級(jí)緩存mChangedScrap、mAttachedScrap和二級(jí)mCachedViews的邏輯颖御。以下為在一級(jí)榄棵、二級(jí)緩存中獲取ViewHolder常用的代碼。
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++) {
//通過(guò)position查找viewholder
final ViewHolder holder = mChangedScrap.get(i);
//...省略
if (mAdapter.hasStableIds()) {
//再次校驗(yàn)id潘拱,獲取viewhodler
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++) {
final ViewHolder holder = mChangedScrap.get(i);
if (!holder.wasReturnedFromScrap() && holder.getItemId() == id) {
holder.addFlags(ViewHolder.FLAG_RETURNED_FROM_SCRAP);
return holder;
}
}
}
}
return null;
}
ViewHolder getScrapOrHiddenOrCachedHolderForPosition(int position, boolean dryRun) {
final int scrapCount = mAttachedScrap.size();
// Try first for an exact, non-invalid match from scrap.
for (int i = 0; i < scrapCount; i++) {
//獲取一個(gè)精確的疹鳄,合法的scrap
final ViewHolder holder = mAttachedScrap.get(i);
//...省略
return holder;
}
}
if (!dryRun) {
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
final ViewHolder vh = getChildViewHolderInt(view);
//...省略
return vh;
}
}
// Search in our first-level recycled view cache.
//緩存獲取
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
if (!holder.isInvalid() && holder.getLayoutPosition() == position
&& !holder.isAttachedToTransitionOverlay()) {
if (!dryRun) {
mCachedViews.remove(i);
}
if (DEBUG) {
Log.d(TAG, "getScrapOrHiddenOrCachedHolderForPosition(" + position
+ ") found match in cache: " + holder);
}
return holder;
}
}
return null;
}
//Adapter的notifyDataSetChanged會(huì)令所有viewHolder為FLAG_UPDATE和FLAG_INVALID
//設(shè)置id,會(huì)忽略標(biāo)志位置芦岂,強(qiáng)行獲取ViewHolder瘪弓。確保不多次創(chuàng)建view。
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--) {
final ViewHolder holder = mAttachedScrap.get(i);
//根據(jù)id來(lái)查找ViewHolder
if (holder.getItemId() == id && !holder.wasReturnedFromScrap()) {
if (type == holder.getItemViewType()) {
holder.addFlags(ViewHolder.FLAG_RETURNED_FROM_SCRAP);
if (holder.isRemoved()) {
// this might be valid in two cases:
// > item is removed but we are in pre-layout pass
// >> do nothing. return as is. make sure we don't rebind
// > item is removed then added to another position and we are in
// post layout.
// >> remove removed and invalid flags, add update flag to rebind
// because item was invisible to us and we don't know what happened in
// between.
if (!mState.isPreLayout()) {
holder.setFlags(ViewHolder.FLAG_UPDATE, ViewHolder.FLAG_UPDATE
| ViewHolder.FLAG_INVALID | ViewHolder.FLAG_REMOVED);
}
}
return holder;
} else if (!dryRun) {
// if we are running animations, it is actually better to keep it in scrap
// but this would force layout manager to lay it out which would be bad.
// Recycle this scrap. Type mismatch.
mAttachedScrap.remove(i);
removeDetachedView(holder.itemView, false);
quickRecycleScrapView(holder.itemView);
}
}
}
// Search the first-level cache
final int cacheSize = mCachedViews.size();
for (int i = cacheSize - 1; i >= 0; i--) {
final ViewHolder holder = mCachedViews.get(i);
if (holder.getItemId() == id && !holder.isAttachedToTransitionOverlay()) {
if (type == holder.getItemViewType()) {
if (!dryRun) {
mCachedViews.remove(i);
}
return holder;
} else if (!dryRun) {
recycleCachedViewAt(i);
return null;
}
}
}
return null;
}
以上完成了ViewHolder的創(chuàng)建盔腔,接下來(lái)查看各個(gè)緩存的生成杠茬。
- mChangedScrap 和 mAttachedScrap
以下代碼會(huì)將變化的viewholder存入到mChangedScrap、不變的viewholder存入到mAttachedScrap
@Override
public void onLayoutChildren(RecyclerView.Recycler recycler, RecyclerView.State state) {
//..省略
detachAndScrapAttachedViews(recycler);
//..省略
}
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.shouldIgnore()) {
if (DEBUG) {
Log.d(TAG, "ignoring view " + viewHolder);
}
return;
}
if (viewHolder.isInvalid() && !viewHolder.isRemoved()
&& !mRecyclerView.mAdapter.hasStableIds()) {
removeViewAt(index);
recycler.recycleViewHolderInternal(viewHolder);
} else {
detachViewAt(index);
recycler.scrapView(view);
mRecyclerView.mViewInfoStore.onViewDetached(viewHolder);
}
}
- mCachedViews
當(dāng)item被滑出屏幕后會(huì)被保存在mCachedViews中弛随,在dispatchLayoutStep1方法中調(diào)用
processAdapterUpdatesAndSetAnimationFlags時(shí)候會(huì)堅(jiān)持mCachedViews瓢喉,如果mCachedViews有變化,則會(huì)mCachedViews中移除掉ViewHolder舀透。
void recycleViewHolderInternal(ViewHolder holder) {
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;
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;
}
mCachedViews.add(targetCacheIndex, holder);
cached = true;
}
if (!cached) {
addViewHolderToRecycledViewPool(holder, true);
recycled = true;
}
}
- mRecyclerPool
當(dāng)mCachedViews被填滿溢出的viewholder栓票,會(huì)被移動(dòng)到RecyclerRool中。 - mHiddenViews 和 mUnmodifiableAttachedScrap :mHiddenViews當(dāng)view被移除屏幕又要執(zhí)行動(dòng)畫,所有會(huì)暫存到mHiddenViews中走贪。mUnmodifiableAttachedScrap實(shí)為不可修改的mAttachedScrap佛猛,在執(zhí)行動(dòng)畫區(qū)間會(huì)暫時(shí)充當(dāng)mAttachedScrap中的一項(xiàng),只有動(dòng)畫只想完成后會(huì)刪除mAttachedScrap中的viewholder坠狡。