RecyclerView 緩存原理
通常認(rèn)為RecyclerView有四級(jí)緩存,RecyclerView的緩存是通過(guò)Recycler類來(lái)完成的,方法的入口:
public View getViewForPosition(int position) {
return getViewForPosition(position, false);
}
緩存的內(nèi)容是ViewHolder,緩存的地方,是Recycler的幾個(gè)list:
final ArrayList<ViewHolder> mAttachedScrap = new ArrayList<ViewHolder>();
private ArrayList<ViewHolder> mChangedScrap = null;
final ArrayList<ViewHolder> mCachedViews = new ArrayList<ViewHolder>();
private final List<ViewHolder>
mUnmodifiableAttachedScrap = Collections.unmodifiableList(mAttachedScrap);
private int mViewCacheMax = DEFAULT_CACHE_SIZE;
private RecycledViewPool mRecyclerPool;
private ViewCacheExtension mViewCacheExtension;
private static final int DEFAULT_CACHE_SIZE = 2;
第一級(jí)緩存
mAttachedScrap: 用于緩存顯示在屏幕上的 item 的 ViewHolder
mChangedScrap: 還是屏幕內(nèi)的ViewHolder,沒(méi)看懂啥意思,可能是用于緩存局部改變時(shí)候的holder
第二級(jí)緩存
mCachedViews:劃出屏幕外的item,這個(gè)list的默認(rèn)大小是2,
第三級(jí)緩存
mViewCacheExtension:自定義緩存,RecyclerView默認(rèn)是沒(méi)有實(shí)現(xiàn)的,留給開發(fā)者自己實(shí)現(xiàn)
第四級(jí)緩存
mRecyclerPool:mCachedViews的數(shù)量達(dá)到上限之后,會(huì)把ViewHolder存入mRecyclerPool。mRecyclerPool用SparseArray來(lái)緩存進(jìn)入這一級(jí)的ViewHolder:
static class ScrapData {
final ArrayList<ViewHolder> mScrapHeap = new ArrayList<>();
int mMaxScrap = DEFAULT_MAX_SCRAP;
long mCreateRunningAverageNs = 0;
long mBindRunningAverageNs = 0;
}
SparseArray<ScrapData> mScrap = new SparseArray<>();
但存在mRecyclerPool的 ViewHolder 的數(shù)據(jù)信息會(huì)被重置掉,相當(dāng)于 ViewHolder 是一個(gè)重創(chuàng)新建的一樣,所以需要重新調(diào)用 onBindViewHolder 來(lái)綁定數(shù)據(jù),所以盡量不要讓ViewHolder進(jìn)入這一層
緩存優(yōu)化
進(jìn)入RecyclerPool的ViewHolder會(huì)被重置,會(huì)從新執(zhí)行bindViewHolder,所以從效率上來(lái)講,很費(fèi)性能。
所以為了避免進(jìn)入這一層緩存,可以在在第三層自定義緩存自己實(shí)現(xiàn),也就是自定義mViewCacheExtension。在這里自己維護(hù)一個(gè)viewType對(duì)應(yīng)View的SparseArray。這樣可以避免因?yàn)槎喾Ntype導(dǎo)致的holder重建。
public abstract static class ViewCacheExtension {
@Nullable
public abstract View getViewForPositionAndType(@NonNull Recycler recycler, int position,
int type);
}
注意getViewForPositionAndType返回的是view而不是ViewHolder,然后會(huì)通過(guò)view的layoutParams拿到ViewHolder。