緩存層級(緩存結(jié)構(gòu)/緩存類型)
- ScrapView(臟視圖):
- 布局期間進入臨時分離狀態(tài)的子視圖
- 可以重復(fù)使用珊膜,而不會與父級RecyclerView完全分離灵奖,如果不需要重新綁定嚼沿,則不進行修改如果視圖被視為臟,則由適配器修改瓷患。
- RV展示成功后骡尽,Scrap這層的緩存就為空了,在從Scrap中取視圖的同時就被移出了緩存
這里的臟怎么理解呢擅编?就是指那些在展示之前必須重新綁定的視圖攀细,比如一個視圖原來展示的是“張三”,之后需要展示“李四”了爱态,那么這個視圖就是臟視圖谭贪,需要重新綁定數(shù)據(jù)后再展示的。
- RecyclerView(回收視圖):
- 先前用于顯示適配器特定位置的數(shù)據(jù)的視圖可以放置在高速緩存中以供稍后重用再次顯示相同類型的數(shù)據(jù)
這可以通過跳過初始布局或構(gòu)造來顯著提高性能
緩存變量
Recycler中的緩存變量
- mAttachedScrap
- Scrap中的一種锦担,這里的數(shù)據(jù)是不做修改的俭识,不會重新走Adapter的綁定方法
- 沒有容量限制的,只要符合條件的我來者不拒洞渔,全收了
final ArrayList<ViewHolder> mAttachedScrap = new ArrayList<>();
- mChangedScrap
- Scrap中的一種
- 存放的是發(fā)生了變化的ViewHolder,如果使用到了這里的緩存的ViewHolder是要重新走Adapter的綁定方法的套媚。
ArrayList<ViewHolder> mChangedScrap = null;
- mCachedViews
- 屬于Recycler(View),
- remove掉的視圖磁椒,已經(jīng)和RV分離的關(guān)系的視圖凑阶,但是它里面的ViewHolder依然保存著之前的信息,比如position衷快、和綁定的數(shù)據(jù)等等
- 緩存是有容量限制的,默認是2(不同版本API可能會有差異)
final ArrayList<ViewHolder> mCachedViews = new ArrayList<ViewHolder>();
- mViewCacheExtension
- 開發(fā)者自由發(fā)揮的姨俩,官方并沒有默認實現(xiàn)蘸拔,它本身是null。
private ViewCacheExtension mViewCacheExtension;
- mRecyclerPool
- 保存的ViewHolder不僅僅是removed掉的視圖环葵,而且是恢復(fù)了出廠設(shè)置的視圖调窍,任何綁定過的痕跡都沒有了,想用這里緩存的ViewHolder那是鐵定要重新走Adapter的綁定方法了
- 按照itemType來分開存儲的
RecycledViewPool mRecyclerPool;
ChildHelper中的緩存變量
- mHiddenViews
- 緩存被隱藏的ViewHolder
final List<View> mHiddenViews = new ArrayList<View>();
RecyclerView的復(fù)用流程
最終調(diào)用到Recycler的tryGetViewHolderForPositionByDeadline方法
//第一步
if (mState.isPreLayout()) {
holder = getChangedScrapViewForPosition(position);
fromScrapOrHiddenOrCache = holder != null;
}
//第二步
if (holder == null) {
holder = getScrapOrHiddenOrCachedHolderForPosition(position, dryRun);
//分三個流程
1.從mAttachedScrap中查找
2.ChildHelper類中的mHiddenViews中查找
3.從mCachedViews中查找的
}
//第三步
if (holder == null && mViewCacheExtension != null) {
final View view = mViewCacheExtension.getViewForPositionAndType(this, position, type);
if (view != null) {
holder = getChildViewHolder(view);
}
}
//第四步
if (holder == null) {
holder = getRecycledViewPool().getRecycledView(type);
}
//第五步
if (holder == null) {
holder = mAdapter.createViewHolder(RecyclerView.this, type);
}
//最后
if (!holder.isBound() || holder.needsUpdate() || holder.isInvalid()) {
...
final int offsetPosition = mAdapterHelper.findPositionOffset(position);
bound = tryBindViewHolderByDeadline(holder, offsetPosition, position, deadlineNs);
//最終調(diào)用 mAdapter.bindViewHolder(holder, offsetPosition);
...
}
- 第一步:getChangedScrapViewForPosition(position)方法中找需要的視圖张遭,但是有個條件mState.isPreLayout()要為true邓萨,這個一般在我們調(diào)用adapter的notifyItemChanged等方法時為true,其實也很好理解,數(shù)據(jù)發(fā)生了變化缔恳,viewholder被detach掉后緩存在mChangedScrap之中宝剖,在這里拿到的viewHolder后續(xù)需要重新綁定
if (mState.isPreLayout()) {
holder = getChangedScrapViewForPosition(position);
fromScrapOrHiddenOrCache = holder != null;
}
- 第二步:如果沒有找到視圖則從getScrapOrHiddenOrCachedHolderForPosition這個方法中繼續(xù)找。
- 首先從mAttachedScrap中查找
- 再次從前面略過的ChildHelper類中的mHiddenViews中查找
- 最后是從mCachedViews中查找的
if (holder == null) {
holder = getScrapOrHiddenOrCachedHolderForPosition(position, dryRun);
}
ViewHolder getScrapOrHiddenOrCachedHolderForPosition(int position, boolean dryRun) {
//1.從mAttachedScrap中查找
for (int i = 0; i < scrapCount; i++) {
final ViewHolder holder = mAttachedScrap.get(i);
return holder;
}
//2.ChildHelper類中的mHiddenViews中查找
if (!dryRun) {
//最終調(diào)用 final View view = mHiddenViews.get(i);
View view = mChildHelper.findHiddenNonRemovedView(position);
if(view != null) {
final ViewHolder vh = getChildViewHolderInt(view);
return vh;
}
}
//3.mCachedViews中查找
final int cacheSize = mCachedViews.size();
for (int i = 0; i < cacheSize; i++) {
final ViewHolder holder = mCachedViews.get(i);
return holder
}
}
- 第三步:mViewCacheExtension中查找歉甚,我們說過這個對象默認是null的万细,是由我們開發(fā)者自定義緩存策略的一層,所以如果你沒有定義過纸泄,這里是找不到View的
if (holder == null && mViewCacheExtension != null) {
...
final View view = mViewCacheExtension.getViewForPositionAndType(this, position, type);
if (view != null) {
holder = getChildViewHolder(view);
}
...
}
- 第四步:從RecyclerPool中查找赖钞,前面我們介紹過RecyclerPool,先通過itemType從SparseArray類型的mscrap中拿到ScrapData,不為空繼續(xù)拿到scrapHeap這個ArrayList聘裁,然后取到視圖雪营,這里拿到的視圖需要重新綁定
if (holder == null) {
holder = getRecycledViewPool().getRecycledView(type);
}
- 第五步:創(chuàng)建ViewHolder
if (holder == null) {
holder = mAdapter.createViewHolder(RecyclerView.this, type);
}
- 最后:綁定ViewHolder
if (!holder.isBound() || holder.needsUpdate() || holder.isInvalid()) {
...
final int offsetPosition = mAdapterHelper.findPositionOffset(position);
bound = tryBindViewHolderByDeadline(holder, offsetPosition, position, deadlineNs);
...
}
private boolean tryBindViewHolderByDeadline(ViewHolder holder, int offsetPosition, int position, long deadlineNs) {
...
mAdapter.bindViewHolder(holder, offsetPosition);
...
}
RecyclerView的回收流程
。衡便。献起。