先了解一下Recyclerview主要有哪些类
类名 | 作用 |
---|---|
LayoutManager | 负责ItemView的布局和显示管理 |
ItemDecoration | 给ItemView添加子View,如分割线 |
ItemAnimator | 添加或删除的动画效果 |
Adapter | 为ItemView创建视图 |
ViewHolder | 承载包装ItemView |
Recycler | 四级缓存 |
Recycler
我们先要搞清楚缓存复用的对象是谁?ViewHolder(包装View Recyclerview的一个itemView)
Recycler是Recyclerview的内部类,它的主要成员变量是用来缓存和复用ViewHolder的。
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;
//...
}
一级缓存:mAttachedScrap和mChangedScrap (用来缓存还在屏幕内的ViewHolder)
- mAttachedScrap存储当前屏幕中的ViewHolder,按id和position来查找ViewHolder
- mChangedScrap表示数据已经改变的ViewHolder,存储notifyXXX方法时需要改变的ViewHolder
二级缓存:mCachedViews(用来缓存移出到屏幕之外的ViewHolder)
默认情况下缓存容量为2,如果mCachedViews容量已满,会根据FIFO的规则移除旧ViewHolder
三级缓存:mViewCacheExtension(开放给用户的自定义缓存)
四级缓存:mRecyclerPool(ViewHolder缓存池)
用SparseArray存ViewType和ArrayList<ViewHolder>,每个ViewType最多缓存5个ViewHolder。RecyclerPool只保存ViewType类型,不保存数据。
复用
我们从RecyclerView的滑动事件onTouchEvent和布局onLayout入手 RecyclerView.png核心代码tryGetViewHolderForPositionByDeadline分析
ViewHolder tryGetViewHolderForPositionByDeadline(int position,
boolean dryRun, long deadlineNs) {
//...
if (mState.isPreLayout()) {
// 0)从 mChangedScrap 里面去获取 ViewHolder,动画相关
holder = getChangedScrapViewForPosition(position);
}
if (holder == null) {
// 1) mAttachedScrap、mCachedViews
holder = getScrapOrHiddenOrCachedHolderForPosition(position, dryRun);
}
if (holder == null) {
if (mAdapter.hasStableIds()) {
// 2) mAttachedScrap、mCachedViews (通过viewType,itemId获取)
holder = getScrapOrCachedViewForId(mAdapter.getItemId(offsetPosition),
type, dryRun);
}
if (holder == null && mViewCacheExtension != null) {
// 3)mViewCacheExtension 自定义缓存
final View view = mViewCacheExtension
.getViewForPositionAndType(this, position, type);
if (view != null) {
holder = getChildViewHolder(view);
}
}
if (holder == null) {
// 4) 缓冲池里获取
holder = getRecycledViewPool().getRecycledView(type);
}
if (holder == null) {
holder = mAdapter.createViewHolder(RecyclerView.this, type);
}
}
bound = tryBindViewHolderByDeadline(holder, offsetPosition, position, deadlineNs);
return holder;
}
可看到:
tryGetViewHolderForPositionByDeadline(从ViewHolder拿itemView)分情况获取
-
getChangedScrapViewForPosition ----- mChangedScrap 动画相关
-
getScrapOrHiddenOrCachedHolderForPosition ----- mAttachedScrap、mCachedViews
-
getScrapOrCachedViewForId ----- mAttachedScrap、mCachedViews (通过viewType,itemId获取)
-
mViewCacheExtension.getViewForPositionAndType ----- mViewCacheExtension 自定义缓存
-
getRecycledViewPool().getRecycledView 从缓冲池里获取
当没有缓存的时候?—>mAdapter.createViewHolder—>onCreateViewHolder就是给我们重写的。
创建ViewHolder后绑定 —> tryBindViewHolderByDeadline—>mAdapter.bindViewHolder—>onBindViewHolder
缓存
什么时候缓存呢?
所谓缓存,就是RecyclerView怎么往四级缓存里添加数据的。
当我们调用notifyXXX的时候,会触发requestLayout方法,就会重新布局:
//调用链(源码太长,不一一贴出来,只贴核心代码,对照着源码看)
RecyclerView.onLayout()
—>dispatchLayout()
—>dispatchLayoutStep2()
—>mLayout.onLayoutChildren
—>LinearLayoutManager.onLayoutChildren(recycler)
—>detachAndScrapAttachedViews(recycler)
—>RecyclerView.detachAndScrapAttachedViews
—>scrapOrRecycleView(recycler, i, v);
跟踪到scrapOrRecycleView
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);
//——>分析1 (缓存到 mCacheViews 和 RecyclerViewPool)
recycler.recycleViewHolderInternal(viewHolder);
} else {
detachViewAt(index);
//——>分析2 缓存到mAttachedScrap和mChangedScrap
recycler.scrapView(view);
mRecyclerView.mViewInfoStore.onViewDetached(viewHolder);
}
}
——>分析1 缓存到 mCacheViews 和RecyclerViewPool
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) {
//如果mCachedViews容量已满,会根据FIFO的规则取出旧ViewHolder,放到RecycledViewPool,然后再移除。
recycleCachedViewAt(0);
cachedViewSize--;
}
mCachedViews.add(targetCacheIndex, holder);
cached = true;
}
if (!cached) {
//ViewHolder有变化的放RecycledViewPool (RecyclerPool只保存ViewType类型,不保存数据)
addViewHolderToRecycledViewPool(holder, true);
recycled = true;
}
}
}
void recycleCachedViewAt(int cachedViewIndex) {
ViewHolder viewHolder = mCachedViews.get(cachedViewIndex);
addViewHolderToRecycledViewPool(viewHolder, true);
mCachedViews.remove(cachedViewIndex);
}
——>分析2 缓存到mAttachedScrap和mChangedScrap
void scrapView(View view) {
final ViewHolder holder = getChildViewHolderInt(view);
if (holder.hasAnyOfTheFlags(ViewHolder.FLAG_REMOVED | ViewHolder.FLAG_INVALID)
|| !holder.isUpdated() || canReuseUpdatedViewHolder(holder)) {
holder.setScrapContainer(this, false);
//标记为移除或失效||没有改变||item无动画或动画不复用
//放mAttachedScrap
mAttachedScrap.add(holder);
} else {
if (mChangedScrap == null) {
mChangedScrap = new ArrayList<ViewHolder>();
}
holder.setScrapContainer(this, true);
//其它情况放也就是ViewHolder有改变
//放mChangedScrap
mChangedScrap.add(holder);
}
}
网友评论