LayoutManager
获取View
是通过Recycler
,所以它的缓存机制一定是在 Recycler
中
获取View
的入口在getViewForPosition
方法中:
# Recycler
public View getViewForPosition(int position) {
return getViewForPosition(position, false);
}
View getViewForPosition(int position, boolean dryRun) {
// ViewHolder.itemView
return tryGetViewHolderForPositionByDeadline(position, dryRun, FOREVER_NS).itemView;
}
ViewHolder tryGetViewHolderForPositionByDeadline(int position, boolean dryRun, long deadlineNs) {
// 0.from changed scrap
if (mState.isPreLayout()) {
holder = getChangedScrapViewForPosition(position);
fromScrapOrHiddenOrCache = holder != null;
}
// 1.from scrap/hidden list/cache
holder = getScrapOrHiddenOrCachedHolderForPosition(position, dryRun);
// 2.from scrap/cache via stable ids, if exists
if (mAdapter.hasStableIds()) {
holder = getScrapOrCachedViewForId(mAdapter.getItemId(offsetPosition),
type, dryRun);
}
// 3.recyclerview pool
holder = getRecycledViewPool().getRecycledView(type);
// 4.create new viewholder
holder = mAdapter.createViewHolder(RecyclerView.this, type);
}
0-3
这四步会从“缓存”里面获取 ViewHolder
0.getChangedScrapViewForPosition -> mChangedScrap
mChangedScrap:存储数据变化的ViewHolder
ArrayList<ViewHolder> mChangedScrap = null;
ViewHolder getChangedScrapViewForPosition(int position) {
// mChangedScrap
for (int i = 0; i < changedScrapSize; i++) {
final ViewHolder holder = mChangedScrap.get(i);
if (!holder.wasReturnedFromScrap() && holder.getLayoutPosition() == position) {
holder.addFlags(ViewHolder.FLAG_RETURNED_FROM_SCRAP);
return holder;
}
}
return null;
}
1.getScrapOrHiddenOrCachedHolderForPosition -> mAttachedScrap、ChildHelper.mHiddenViews、mCachedViews
- mAttachedScrap:一次重新布局中,会暂时缓存所有的
ViewHolder
- mHiddenViews:暂存被隐藏的
ItemView
,比如做正再做离场动画的ItemView
又因某种原因需要进场的时候被使用。 - mCachedViews:真正意义上的一级缓存,之前的只是暂时存放,称为暂存区更为合理
ArrayList<ViewHolder> mAttachedScrap = new ArrayList<>();
final ArrayList<ViewHolder> mCachedViews = new ArrayList<ViewHolder>();
ViewHolder getScrapOrHiddenOrCachedHolderForPosition(int position, boolean dryRun) {
// 1.attachedScrap
for (int i = 0; i < scrapCount; i++) {
final ViewHolder holder = mAttachedScrap.get(i);
return holder;
}
// 2.ChildHelper.mHiddenViews
View view = mChildHelper.findHiddenNonRemovedView(position);
if (view != null) {
ViewHolder vh = getChildViewHolderInt(view);
return vh;
}
// 3.mCachedViews: first-level recycled view cache
int cacheSize = mCachedViews.size();
for (int i = 0; i < cacheSize; i++) {
final ViewHolder holder = mCachedViews.get(i);
return holder;
}
return null;
}
2.getScrapOrCachedViewForId -> mAttachedScrap、mCachedViews
调用该方法的前提是设置了mHasStableIds
,才会走这段逻辑。
根据itemId
而不是position
,从mAttachedScrap
和mCachedViews
中查找ViewHolder
。
// mAttachedScrap
ViewHolder getScrapOrCachedViewForId(long id, int type, boolean dryRun) {
final int count = mAttachedScrap.size();
for (int i = count - 1; i >= 0; i--) {
final ViewHolder holder = mAttachedScrap.get(i);
if (holder.getItemId() == id && !holder.wasReturnedFromScrap()) {
return holder;
}
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()) {
return holder;
}
return null;
}
思考:设置hasStableId有什么作用?
3.RecycledViewPool -> mScrap
如果以上缓存都找不到,说明只根据position
已经无法找到ViewHolder
了,就会根据ViewType
在RecycledViewPool
中调用getRecycledView(type)
寻找了。
# Recycler
holder = getRecycledViewPool().getRecycledView(type);
# RecycledViewPool
final int type = mAdapter.getItemViewType(offsetPosition);
SparseArray<ScrapData> mScrap = new SparseArray<>();
public ViewHolder getRecycledView(int viewType) {
final ScrapData scrapData = mScrap.get(viewType);
if (scrapData != null && !scrapData.mScrapHeap.isEmpty()) {
final ArrayList<ViewHolder> scrapHeap = scrapData.mScrapHeap;
for (int i = scrapHeap.size() - 1; i >= 0; i--) {
if (!scrapHeap.get(i).isAttachedToTransitionOverlay()) {
return scrapHeap.remove(i);
}
}
}
return null;
}
static class ScrapData {
final ArrayList<ViewHolder> mScrapHeap = new ArrayList<>();
int mMaxScrap = DEFAULT_MAX_SCRAP;
long mCreateRunningAverageNs = 0;
long mBindRunningAverageNs = 0;
}
4.mAdapter.createViewHolder
如果缓存中都找不到,就只能调用mAdapter.createViewHolder
去创建新的ViewHolder