RecyclerView緩存機制

緩存層級(緩存結構/緩存類型)

  • ScrapView(髒視圖):
    • 佈局期間進入臨時分離狀態的子視圖
    • 可以重複使用,而不會與父級RecyclerView完全分離,如果不需要重新綁定,則不進行修改如果視圖被視爲髒,則由適配器修改。
    • RV展示成功後,Scrap這層的緩存就爲空了,在從Scrap中取視圖的同時就被移出了緩存

這裏的髒怎麼理解呢?就是指那些在展示之前必須重新綁定的視圖,比如一個視圖原來展示的是“張三”,之後需要展示“李四”了,那麼這個視圖就是髒視圖,需要重新綁定數據後再展示的。

  • RecyclerView(回收視圖):
    • 先前用於顯示適配器特定位置的數據的視圖可以放置在高速緩存中以供稍後重用再次顯示相同類型的數據

這可以通過跳過初始佈局或構造來顯着提高性能

緩存變量

Recycler中的緩存變量

  • mAttachedScrap
    • Scrap中的一種,這裏的數據是不做修改的,不會重新走Adapter的綁定方法
    • 沒有容量限制的,只要符合條件的我來者不拒,全收了
final ArrayList<ViewHolder> mAttachedScrap = new ArrayList<>();
  • mChangedScrap
    • Scrap中的一種
    • 存放的是發生了變化的ViewHolder,如果使用到了這裏的緩存的ViewHolder是要重新走Adapter的綁定方法的。
ArrayList<ViewHolder> mChangedScrap = null;
  • mCachedViews
    • 屬於Recycler(View),
    • remove掉的視圖,已經和RV分離的關係的視圖,但是它裏面的ViewHolder依然保存着之前的信息,比如position、和綁定的數據等等
    • 緩存是有容量限制的,默認是2(不同版本API可能會有差異)
final ArrayList<ViewHolder> mCachedViews = new ArrayList<ViewHolder>();
  • mViewCacheExtension
    • 開發者自由發揮的,官方並沒有默認實現,它本身是null。
private ViewCacheExtension mViewCacheExtension;
  • mRecyclerPool
    • 保存的ViewHolder不僅僅是removed掉的視圖,而且是恢復了出廠設置的視圖,任何綁定過的痕跡都沒有了,想用這裏緩存的ViewHolder那是鐵定要重新走Adapter的綁定方法了
    • 按照itemType來分開存儲的
RecycledViewPool mRecyclerPool;

ChildHelper中的緩存變量

  • mHiddenViews
    • 緩存被隱藏的ViewHolder
final List<View> mHiddenViews = new ArrayList<View>();

RecyclerView的複用流程

最終調用到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);
    //最終調用 mAdapter.bindViewHolder(holder, offsetPosition);
    ...
}
  • 第一步:getChangedScrapViewForPosition(position)方法中找需要的視圖,但是有個條件mState.isPreLayout()要爲true,這個一般在我們調用adapter的notifyItemChanged等方法時爲true,其實也很好理解,數據發生了變化,viewholder被detach掉後緩存在mChangedScrap之中,在這裏拿到的viewHolder後續需要重新綁定
if (mState.isPreLayout()) {
    holder = getChangedScrapViewForPosition(position);
    fromScrapOrHiddenOrCache = holder != null;
}
  • 第二步:如果沒有找到視圖則從getScrapOrHiddenOrCachedHolderForPosition這個方法中繼續找。
    • 首先從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) {
        //最終調用 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的,是由我們開發者自定義緩存策略的一層,所以如果你沒有定義過,這裏是找不到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,不爲空繼續拿到scrapHeap這個ArrayList,然後取到視圖,這裏拿到的視圖需要重新綁定
if (holder == null) {
    holder = getRecycledViewPool().getRecycledView(type);
}
  • 第五步:創建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的回收流程

。。。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章