转载请以连接形式标明出处: 本文出自:103style的博客android
源码 base on androidx.recyclerview:recyclerview:1.1.0-alpha05
git
代码说明中已省略了暂不须要关注的代码.github
测试demo地址缓存
更新于:2019/10/29 15.04 以前因为用Android P模拟器测试因此一直显示 mAttachedScrap 中的数据一直为空。真机显示正常,为屏幕内当前显示item的条数。bash
RecyclerView
子项的流程获取的流程大体为: RecyclerView.onLayout(...)
→ LayoutManager.onLayoutChildren(...)
→ LayoutState.next(...)
。ide
源代码调用以下:布局
RecyclerView
的 onLayout(...)
方法:测试
protected void onLayout(boolean changed, int l, int t, int r, int b) {
dispatchLayout();
}
void dispatchLayout() {
dispatchLayoutStep2();
}
private void dispatchLayoutStep2() {
mLayout.onLayoutChildren(mRecycler, mState);
}
复制代码
RecyclerView
设置的 LayoutManager
的 onLayoutChildren(...)
方法: 以 LinearLayoutManager
为例:动画
public void onLayoutChildren(...) {
fill(recycler, mLayoutState, state, false);
}
int fill(...) {
layoutChunk(recycler, state, layoutState, layoutChunkResult);
}
void layoutChunk(...) {
View view = layoutState.next(recycler);
}
List<RecyclerView.ViewHolder> mScrapList;
static class LayoutState {
View next(RecyclerView.Recycler recycler) {
if (mScrapList != null) {
return nextViewFromScrapList();
}
final View view = recycler.getViewForPosition(mCurrentPosition);
mCurrentPosition += mItemDirection;
return view;
}
}
复制代码
这里主要看 recycler.getViewForPosition(mCurrentPosition)
,mScrapList
是有动画效果时预布局保存的ViewHolder
。ui
Recycler中的 mAttachedScrap、mChangedScrap、mCachedViews
ArrayList<ViewHolder> mAttachedScrap = new ArrayList<>();
ArrayList<ViewHolder> mChangedScrap = null;
ArrayList<ViewHolder> mCachedViews = new ArrayList<ViewHolder>();
static final int DEFAULT_CACHE_SIZE = 2;
复制代码
mAttachedScrap:用于缓存显示在屏幕上并符合对应规则的 ViewHolder
,模拟器的话会出现 里面的数据时空的 。 mChangedScrap:用于缓存显示在屏幕上不符合 mAttachedScrap
规则的 ViewHolder
。
Recycler.scrapView(View view)
, 在 调用 attchView
和 detachView
的时候调用。
void scrapView(View view) {
final ViewHolder holder = getChildViewHolderInt(view);
if (holder.hasAnyOfTheFlags(ViewHolder.FLAG_REMOVED | ViewHolder.FLAG_INVALID)
|| !holder.isUpdated() || canReuseUpdatedViewHolder(holder)) {
mAttachedScrap.add(holder);
} else {
if (mChangedScrap == null) {
mChangedScrap = new ArrayList<ViewHolder>();
}
mChangedScrap.add(holder);
}
}
复制代码
mCachedViews:保存滑动过程当中从可见到不可见的ViewHolder
,大小为DEFAULT_CACHE_SIZE = 2
,而且本来数据信息都在,因此能够直接添加到 RecyclerView
中显示,不须要再次从新 onBindViewHolder()
,在数据多时滑动过程当中会变成 3
。
private final ViewInfoStore.ProcessCallback mViewInfoProcessCallback =
new ViewInfoStore.ProcessCallback() {
@Override
public void unused(ViewHolder viewHolder) {
mLayout.removeAndRecycleView(viewHolder.itemView, mRecycler);
}
};
void process(ProcessCallback callback) {
for (int index = mLayoutHolderMap.size() - 1; index >= 0; index--) {
final RecyclerView.ViewHolder viewHolder = mLayoutHolderMap.keyAt(index);
final InfoRecord record = mLayoutHolderMap.removeAt(index);
if ((record.flags & FLAG_APPEAR_AND_DISAPPEAR) == FLAG_APPEAR_AND_DISAPPEAR) {
//从可见变成不可见
callback.unused(viewHolder);
} else if ((record.flags & FLAG_DISAPPEARED) != 0) {
if (record.preInfo == null) {
// 相似出现消失但发生在不一样的布局通道之间。
// 当布局管理器使用自动测量时,可能会发生这种状况
callback.unused(viewHolder);
}
}
}
}
复制代码
ChildHelper.mHiddenViews
mHiddenViews
中保留在添加或删除数据或其余操做,保存在屏幕内可见的 带动画子视图。
List<View> mHiddenViews = new ArrayList<View>();
复制代码
mHiddenViews
是在 RecycleView
中 addAnimatingView(ViewHolder viewHolder)
的时候,根据特定状况添加的。
private void addAnimatingView(ViewHolder viewHolder) {
final boolean alreadyParented = view.getParent() == this;
if (viewHolder.isTmpDetached()) {
mChildHelper.attachViewToParent(view, -1, view.getLayoutParams(), true);
} else if (!alreadyParented) {
mChildHelper.addView(view, true);
} else {
mChildHelper.hide(view);
}
}
复制代码
RecycledViewPool.mScrap
每一个 viewType
的缓存最可能是 DEFAULT_MAX_SCRAP = 5
个,viewType
即 RecyclerView
中 子view 的样式,即 adapter
中的 getItemViewType
获取的值。
private static final int DEFAULT_MAX_SCRAP = 5;
static class ScrapData {
final ArrayList<ViewHolder> mScrapHeap = new ArrayList<>();
int mMaxScrap = DEFAULT_MAX_SCRAP;
}
SparseArray<ScrapData> mScrap = new SparseArray<>();
复制代码
添加和获取的方法:
DEFAULT_MAX_SCRAP = 5
时,则丢弃。 存数据即为 释放 mCachedViews
的数据则会放入缓存池。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;
}
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;
}
scrapHeap.add(scrap);
}
复制代码
RecyclerView.Recycler
的 getViewForPosition(int position)
方法:
public View getViewForPosition(int position) {
return getViewForPosition(position, false);
}
View getViewForPosition(int position, boolean dryRun) {
return tryGetViewHolderForPositionByDeadline(position, dryRun, FOREVER_NS).itemView;
}
复制代码
RecyclerView.Recycler
的tryGetViewHolderForPositionByDeadline(...)
方法获取 holder
。 也就是对应的缓存机制的主要逻辑,找到即返回。
mState.isPreLayout()
,是否是执行动画的预布局,是的话则尝试从 mChangedScrap
中获取。mAttachedScrap
、mHiddenViews
、mCachedViews
获取。mAdapter.hasStableIds()
以及 mViewCacheExtension!=null
判断是否从用户设置的相关逻辑中获取。RecycledViewPool
中的 mScrap
的 ScrapData
中获取。mAdapter.createViewHolder
从新建立。ViewHolder tryGetViewHolderForPositionByDeadline(...) {
...
if (mState.isPreLayout()) {
//若是是预布局,即执行简单的动画 就会尝试这里
holder = getChangedScrapViewForPosition(position);
}
// 从 scrap/hidden list/cache 尝试获取
if (holder == null) {
holder = getScrapOrHiddenOrCachedHolderForPosition(position, dryRun);
}
if (holder == null) {
final int offsetPosition = mAdapterHelper.findPositionOffset(position);
...
final int type = mAdapter.getItemViewType(offsetPosition);
//经过 adapter.setHasStableIds设置 默认为false
if (mAdapter.hasStableIds()) {}
//mViewCacheExtension 经过 RecyclerView.setViewCacheExtension设置 默认为null
if (holder == null && mViewCacheExtension != null) {
//经过自定义的缓存机制区获取
}
if (holder == null) {
// 尝试从RecycledViewPool中去获取holder
holder = getRecycledViewPool().getRecycledView(type);
}
if (holder == null) {
//调用Adapter.createViewHolder建立holder
holder = mAdapter.createViewHolder(RecyclerView.this, type);
}
}
...
return holder;
}
复制代码
若是是预布局,则尝试从 mChangedScrap 中获取:
ViewHolder getChangedScrapViewForPosition(int position) {
// 若是是预布局,请检查 mChangedScrap 是否彻底匹配。
for (int i = 0; i < mChangedScrap.size(); i++) {
final ViewHolder holder = mChangedScrap.get(i);
if (!holder.wasReturnedFromScrap() && holder.getLayoutPosition() == position) {
holder.addFlags(ViewHolder.FLAG_RETURNED_FROM_SCRAP);
return holder;
}
}
// 默认 false
if (mAdapter.hasStableIds()) {
...
}
return null;
}
复制代码
尝试从缓存中 mAttachedScrap、mHiddenViews、mCachedViews获取: RecyclerView.Recycler
的getScrapOrHiddenOrCachedHolderForPosition(...)
获取 holder
.
ViewHolder getScrapOrHiddenOrCachedHolderForPosition(int position, boolean dryRun) {
final int scrapCount = mAttachedScrap.size();
// 尝试从mAttachedScrap获取
for (int i = 0; i < scrapCount; i++) {
final ViewHolder holder = mAttachedScrap.get(i);
if (!holder.wasReturnedFromScrap() && holder.getLayoutPosition() == position
&& !holder.isInvalid() && (mState.mInPreLayout || !holder.isRemoved())) {
holder.addFlags(ViewHolder.FLAG_RETURNED_FROM_SCRAP);
return holder;
}
}
//dryRun: getViewForPosition(position, false)传入的是false
if (!dryRun) {
View view = mChildHelper.findHiddenNonRemovedView(position);
if (view != null) {
...
return vh;
}
}
// 从一级缓存中尝试获取
final int cacheSize = mCachedViews.size();
for (int i = 0; i < cacheSize; i++) {
final ViewHolder holder = mCachedViews.get(i);
// 若是适配器具备稳定的ID,则无效的视图持有者可能在缓存中,
// 由于能够经过getScrapOrCachedViewForId检索它们
if (!holder.isInvalid() && holder.getLayoutPosition() == position
&& !holder.isAttachedToTransitionOverlay()) {
if (!dryRun) {
mCachedViews.remove(i);
}
return holder;
}
}
return null;
}
复制代码
从缓存池RecycledViewPool中的 mScrap 的 ScrapData 中获取ViewHolder,并从缓存池中移除: RecycledViewPool
的 getRecycledView(int viewType)
:
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;
}
复制代码
demo主要是添加了 两种类型 的数据,默认添加 50 条数据,设置一屏幕最多显示 8 条数据, 而后经过 滑动,添加,删除 数据,再经过反射来获取对应缓存的数据。
TestAdapter: onCreateViewHolder onCreateViewHolderCount = 1
TestAdapter: onBindViewHolder position = 0
TestAdapter: onCreateViewHolder onCreateViewHolderCount = 2
TestAdapter: onBindViewHolder position = 1
...
TestAdapter: onCreateViewHolder onCreateViewHolderCount = 8
TestAdapter: onBindViewHolder position = 7
MainActivity1572332870607: ChildHelper.mHiddenViews =0
MainActivity1572332870607: LayoutManager.LayoutState.mScrapList =0
MainActivity1572332870607: Recycler.mViewCacheMax =2
MainActivity1572332870607: Recycler.mAttachedScrap.size() = 0
MainActivity1572332870607: Recycler.mChangedScrap.size() = 0
MainActivity1572332870607: Recycler.mCachedViews.size() = 0
MainActivity1572332870607: RecycledViewPool.mScrap.get(viewType = 1).mMaxScrapField = 5
MainActivity1572332870607: RecycledViewPool.mScrap.get(viewType = 1).mScrapHeap.size() = 0
MainActivity1572332870607: RecycledViewPool.mScrap.get(viewType = 2).mMaxScrapField = 5
MainActivity1572332870607: RecycledViewPool.mScrap.get(viewType = 2).mScrapHeap.size() = 0
MainActivity1572332870611: ChildHelper.mHiddenViews =0
MainActivity1572332870611: LayoutManager.LayoutState.mScrapList =0
MainActivity1572332870611: Recycler.mViewCacheMax =2
MainActivity1572332870611: Recycler.mAttachedScrap.size() = 8
MainActivity1572332870611: Recycler.mChangedScrap.size() = 0
MainActivity1572332870611: Recycler.mCachedViews.size() = 0
MainActivity1572332870611: RecycledViewPool.mScrap.get(viewType = 1).mMaxScrapField = 5
MainActivity1572332870611: RecycledViewPool.mScrap.get(viewType = 1).mScrapHeap.size() = 0
MainActivity1572332870611: RecycledViewPool.mScrap.get(viewType = 2).mMaxScrapField = 5
MainActivity1572332870611: RecycledViewPool.mScrap.get(viewType = 2).mScrapHeap.size() = 0
复制代码
mViewCacheMax
变成了 3
, Recycler.mCachedViews
缓存了 position
为 0
和 9
对应位置的数据。即屏幕上下都有不可见的数据是,缓存的是上下各一条不可见视图的数据。TestAdapter: onCreateViewHolder onCreateViewHolderCount = 9
TestAdapter: onBindViewHolder position = 8
TestAdapter: onCreateViewHolder onCreateViewHolderCount = 10
TestAdapter: onBindViewHolder position = 9
MainActivity1572332997091: ChildHelper.mHiddenViews =0
MainActivity1572332997091: LayoutManager.LayoutState.mScrapList =0
MainActivity1572332997091: Recycler.mViewCacheMax =3
MainActivity1572332997091: Recycler.mAttachedScrap.size() = 8
MainActivity1572332997091: Recycler.mChangedScrap.size() = 0
MainActivity1572332997091: Recycler.mCachedViews.size() = 2
MainActivity: mCachedViews:---0 = ViewHolder{1bcb007 position=0 id=-1, oldPos=-1, pLpos:-1 no parent}
MainActivity: mCachedViews:---1 = ViewHolder{c735183 position=9 id=-1, oldPos=-1, pLpos:-1 no parent}
MainActivity1572332997091: RecycledViewPool.mScrap.get(viewType = 1).mMaxScrapField = 5
MainActivity1572332997091: RecycledViewPool.mScrap.get(viewType = 1).mScrapHeap.size() = 0
MainActivity1572332997091: RecycledViewPool.mScrap.get(viewType = 2).mMaxScrapField = 5
MainActivity1572332997091: RecycledViewPool.mScrap.get(viewType = 2).mScrapHeap.size() = 0
复制代码
咱们看到没有调用 onCreateViewHolder, Recycler 中的 mViewCacheMax
仍是 3
, Recycler.mCachedViews
缓存了 position
为 9
和 8
对应位置的数据。即滑动到顶端缓存的是 下方不可见的两条数据。
MainActivity1572333360514: ChildHelper.mHiddenViews =0
MainActivity1572333360514: LayoutManager.LayoutState.mScrapList =0
MainActivity1572333360514: Recycler.mViewCacheMax =3
MainActivity1572333360514: Recycler.mAttachedScrap.size() = 8
MainActivity1572333360514: Recycler.mChangedScrap.size() = 0
MainActivity1572333360514: Recycler.mCachedViews.size() = 2
MainActivity: mCachedViews:---0 = ViewHolder{c735183 position=9 id=-1, oldPos=-1, pLpos:-1 no parent}
MainActivity: mCachedViews:---1 = ViewHolder{9f16d94 position=8 id=-1, oldPos=-1, pLpos:-1 no parent}
MainActivity1572333360514: RecycledViewPool.mScrap.get(viewType = 1).mMaxScrapField = 5
MainActivity1572333360514: RecycledViewPool.mScrap.get(viewType = 1).mScrapHeap.size() = 0
MainActivity1572333360514: RecycledViewPool.mScrap.get(viewType = 2).mMaxScrapField = 5
MainActivity1572333360514: RecycledViewPool.mScrap.get(viewType = 2).mScrapHeap.size() = 0
复制代码
咱们看到调用了 2
次 onCreateViewHolder, Recycler 中的 mViewCacheMax
仍是 3
, Recycler.mCachedViews
缓存了 position
为 2
、3
和 12
对应位置的的 三条
数据。 RecycledViewPool
中缓存了一条 viewType = 2
的数据,即上图有两列的 item.
TestAdapter: onBindViewHolder position = 1
MainActivity1572333690740: ChildHelper.mHiddenViews =1
MainActivity: mHiddenViews:---0 = android.widget.LinearLayout{9d33071 V.E...... ......I. 0,2268-1440,2588}
MainActivity1572333690740: LayoutManager.LayoutState.mScrapList =0
MainActivity1572333690740: Recycler.mViewCacheMax =3
MainActivity1572333690740: Recycler.mAttachedScrap.size() = 8
MainActivity1572333690740: Recycler.mChangedScrap.size() = 0
MainActivity1572333690740: Recycler.mCachedViews.size() = 3
MainActivity: mCachedViews:---0 = ViewHolder{15ffd7e position=11 id=-1, oldPos=-1, pLpos:-1 no parent}
MainActivity: mCachedViews:---1 = ViewHolder{c735183 position=10 id=-1, oldPos=-1, pLpos:-1 no parent}
MainActivity: mCachedViews:---2 = ViewHolder{9f16d94 position=9 id=-1, oldPos=-1, pLpos:-1 no parent}
MainActivity1572333690740: RecycledViewPool.mScrap.get(viewType = 1).mMaxScrapField = 5
MainActivity1572333690740: RecycledViewPool.mScrap.get(viewType = 1).mScrapHeap.size() = 0
MainActivity1572333690740: RecycledViewPool.mScrap.get(viewType = 2).mMaxScrapField = 5
MainActivity1572333690740: RecycledViewPool.mScrap.get(viewType = 2).mScrapHeap.size() = 0
复制代码
咱们发现 ChildHelper.mHiddenViews
中多了条数据。只有看到添加或删除动画时,mHiddenViews中才会有数据 。
TestAdapter: onBindViewHolder position = 1
MainActivity1571565149149: ChildHelper.mHiddenViews =1
MainActivity: mHiddenViews:---0 = android.widget.LinearLayout{c44a637 V.E...... ......I. 0,1589-1080,1813}
MainActivity1571565149149: LayoutManager.LayoutState.mScrapList =0
MainActivity1571565149149: Recycler.mViewCacheMax =3
MainActivity1571565149149: Recycler.mAttachedScrap.size() = 8
MainActivity1571565149149: Recycler.mChangedScrap.size() = 0
MainActivity1571565149149: Recycler.mCachedViews.size() = 3
MainActivity: mCachedViews:---0 = ViewHolder{3d52e3f position=11 id=-1, oldPos=-1, pLpos:-1 no parent}
MainActivity: mCachedViews:---1 = ViewHolder{e8dba5e position=10 id=-1, oldPos=-1, pLpos:-1 no parent}
MainActivity: mCachedViews:---2 = ViewHolder{6647799 position=9 id=-1, oldPos=-1, pLpos:-1 no parent}
MainActivity1571565149149: RecycledViewPool.mScrap.get(viewType = 1).mMaxScrapField = 5
MainActivity1571565149149: RecycledViewPool.mScrap.get(viewType = 1).mScrapHeap.size() = 0
MainActivity1571565149149: RecycledViewPool.mScrap.get(viewType = 2).mMaxScrapField = 5
MainActivity1571565149149: RecycledViewPool.mScrap.get(viewType = 2).mScrapHeap.size() = 1
MainActivity: mScrapHeap:---0 = ViewHolder{996130c position=-1 id=-1, oldPos=-1, pLpos:-1 unbound no parent}
复制代码
经过上文咱们能够了解到:
获取 子View 最终是调用了 LayoutManager 中 LayoutState 的 next(RecyclerView.Recycler recycler)。 而后判断是不是显示动画效果,是的话则直接从 mScrapList 获取,不然经过缓存机制去获取。
缓存数据相关的变量分别表明的意思: mAttachedScrap:用于缓存显示在屏幕上并符合对应规则的 ViewHolder,测试发如今Android P 的模拟器上 里面的数据时空的。 mChangedScrap:用于缓存显示在屏幕上不符合 mAttachedScrap 规则的 ViewHolder。 mCachedViews:保存滑动过程当中从可见到不可见的 ViewHolder,大小为DEFAULT_CACHE_SIZE = 2
,而且本来数据信息都在,因此能够直接添加到 RecyclerView 中显示,不须要再次从新 onBindViewHolder()
,在数据多时,滑动过程当中 mCachedViews
的大小会变成 3
。 mHiddenViews 中保留在添加或删除数据或其余操做,保存在屏幕内可见的 带动画子视图。
RecyclerView 的缓存机制大体是:
mChangedScrap
中获取。mAttachedScrap
、mHiddenViews
、mCachedViews
获取。mAdapter.hasStableIds()
以及 mViewCacheExtension!=null
判断是否从用户自定义设置的相关逻辑中获取。RecycledViewPool
中 mScrap
的 ScrapData
中获取,每种样式最多缓存 5个。mAdapter.createViewHolder
建立新的。若是有写错的地方,请帮忙评论指正,感谢!
若是以为不错的话,请帮忙点个赞呗。
以上
扫描下面的二维码,关注个人公众号 Android1024, 点关注,不迷路。