<commit_before>---
layout:     post
title:      "RecyclerView缓存机制"
date:       2019-01-01
author:     "phantomVK"
header-img: "img/bg/post_bg.jpg"
catalog:    true
tags:
    - Android
---

第一级缓存

```java
final ArrayList<RecyclerView.ViewHolder> mAttachedScrap = new ArrayList();
ArrayList<RecyclerView.ViewHolder> mChangedScrap = null;
final ArrayList<RecyclerView.ViewHolder> mCachedViews = new ArrayList();
```

第二级缓存

```java
private RecyclerView.ViewCacheExtension mViewCacheExtension;
```

第三级缓存

```java
RecyclerView.RecycledViewPool mRecyclerPool;
```

RecyclerView的内部类Recycler

```java
@Nullable
RecyclerView.ViewHolder tryGetViewHolderForPositionByDeadline(int position, boolean dryRun, long deadlineNs) {
    if (position >= 0 && position < RecyclerView.this.mState.getItemCount()) {
        boolean fromScrapOrHiddenOrCache = false;
        RecyclerView.ViewHolder holder = null;
        if (RecyclerView.this.mState.isPreLayout()) {
            holder = this.getChangedScrapViewForPosition(position);
            fromScrapOrHiddenOrCache = holder != null;
        }

        if (holder == null) {
            holder = this.getScrapOrHiddenOrCachedHolderForPosition(position, dryRun);
            if (holder != null) {
                if (!this.validateViewHolderForOffsetPosition(holder)) {
                    if (!dryRun) {
                        holder.addFlags(4);
                        if (holder.isScrap()) {
                            RecyclerView.this.removeDetachedView(holder.itemView, false);
                            holder.unScrap();
                        } else if (holder.wasReturnedFromScrap()) {
                            holder.clearReturnedFromScrapFlag();
                        }

                        this.recycleViewHolderInternal(holder);
                    }

                    holder = null;
                } else {
                    fromScrapOrHiddenOrCache = true;
                }
            }
        }

        int offsetPosition;
        int type;
        if (holder == null) {
            offsetPosition = RecyclerView.this.mAdapterHelper.findPositionOffset(position);
            if (offsetPosition < 0 || offsetPosition >= RecyclerView.this.mAdapter.getItemCount()) {
                throw new IndexOutOfBoundsException("Inconsistency detected. Invalid item position " + position + "(offset:" + offsetPosition + ")." + "state:" + RecyclerView.this.mState.getItemCount() + RecyclerView.this.exceptionLabel());
            }

            type = RecyclerView.this.mAdapter.getItemViewType(offsetPosition);
            if (RecyclerView.this.mAdapter.hasStableIds()) {
                holder = this.getScrapOrCachedViewForId(RecyclerView.this.mAdapter.getItemId(offsetPosition), type, dryRun);
                if (holder != null) {
                    holder.mPosition = offsetPosition;
                    fromScrapOrHiddenOrCache = true;
                }
            }

            if (holder == null && this.mViewCacheExtension != null) {
                View view = this.mViewCacheExtension.getViewForPositionAndType(this, position, type);
                if (view != null) {
                    holder = RecyclerView.this.getChildViewHolder(view);
                    if (holder == null) {
                        throw new IllegalArgumentException("getViewForPositionAndType returned a view which does not have a ViewHolder" + RecyclerView.this.exceptionLabel());
                    }

                    if (holder.shouldIgnore()) {
                        throw new IllegalArgumentException("getViewForPositionAndType returned a view that is ignored. You must call stopIgnoring before returning this view." + RecyclerView.this.exceptionLabel());
                    }
                }
            }

            if (holder == null) {
                holder = this.getRecycledViewPool().getRecycledView(type);
                if (holder != null) {
                    holder.resetInternal();
                    if (RecyclerView.FORCE_INVALIDATE_DISPLAY_LIST) {
                        this.invalidateDisplayListInt(holder);
                    }
                }
            }

            if (holder == null) {
                long start = RecyclerView.this.getNanoTime();
                if (deadlineNs != 9223372036854775807L && !this.mRecyclerPool.willCreateInTime(type, start, deadlineNs)) {
                    return null;
                }

                holder = RecyclerView.this.mAdapter.createViewHolder(RecyclerView.this, type);
                if (RecyclerView.ALLOW_THREAD_GAP_WORK) {
                    RecyclerView innerView = RecyclerView.findNestedRecyclerView(holder.itemView);
                    if (innerView != null) {
                        holder.mNestedRecyclerView = new WeakReference(innerView);
                    }
                }

                long end = RecyclerView.this.getNanoTime();
                this.mRecyclerPool.factorInCreateTime(type, end - start);
            }
        }

        if (fromScrapOrHiddenOrCache && !RecyclerView.this.mState.isPreLayout() && holder.hasAnyOfTheFlags(8192)) {
            holder.setFlags(0, 8192);
            if (RecyclerView.this.mState.mRunSimpleAnimations) {
                offsetPosition = RecyclerView.ItemAnimator.buildAdapterChangeFlagsForAnimations(holder);
                offsetPosition |= 4096;
                RecyclerView.ItemAnimator.ItemHolderInfo info = RecyclerView.this.mItemAnimator.recordPreLayoutInformation(RecyclerView.this.mState, holder, offsetPosition, holder.getUnmodifiedPayloads());
                RecyclerView.this.recordAnimationInfoIfBouncedHiddenView(holder, info);
            }
        }

        boolean bound = false;
        if (RecyclerView.this.mState.isPreLayout() && holder.isBound()) {
            holder.mPreLayoutPosition = position;
        } else if (!holder.isBound() || holder.needsUpdate() || holder.isInvalid()) {
            type = RecyclerView.this.mAdapterHelper.findPositionOffset(position);
            bound = this.tryBindViewHolderByDeadline(holder, type, position, deadlineNs);
        }

        android.view.ViewGroup.LayoutParams lp = holder.itemView.getLayoutParams();
        RecyclerView.LayoutParams rvLayoutParams;
        if (lp == null) {
            rvLayoutParams = (RecyclerView.LayoutParams)RecyclerView.this.generateDefaultLayoutParams();
            holder.itemView.setLayoutParams(rvLayoutParams);
        } else if (!RecyclerView.this.checkLayoutParams(lp)) {
            rvLayoutParams = (RecyclerView.LayoutParams)RecyclerView.this.generateLayoutParams(lp);
            holder.itemView.setLayoutParams(rvLayoutParams);
        } else {
            rvLayoutParams = (RecyclerView.LayoutParams)lp;
        }

        rvLayoutParams.mViewHolder = holder;
        rvLayoutParams.mPendingInvalidate = fromScrapOrHiddenOrCache && bound;
        return holder;
    } else {
        throw new IndexOutOfBoundsException("Invalid item position " + position + "(" + position + "). Item count:" + RecyclerView.this.mState.getItemCount() + RecyclerView.this.exceptionLabel());
    }
}
```

内部类RecycledViewPool

```java
public static class RecycledViewPool {
    private static final int DEFAULT_MAX_SCRAP = 5;
    SparseArray<RecyclerView.RecycledViewPool.ScrapData> mScrap = new SparseArray();
    private int mAttachCount = 0;
<commit_msg>Temp.<commit_after>---
layout:     post
title:      "RecyclerView缓存机制"
date:       2019-01-01
author:     "phantomVK"
header-img: "img/bg/post_bg.jpg"
catalog:    true
tags:
    - Android
---

第一级缓存

```java
final ArrayList<RecyclerView.ViewHolder> mAttachedScrap = new ArrayList();
ArrayList<RecyclerView.ViewHolder> mChangedScrap = null;
final ArrayList<RecyclerView.ViewHolder> mCachedViews = new ArrayList();
```

第二级缓存

```java
private RecyclerView.ViewCacheExtension mViewCacheExtension;
```

第三级缓存

```java
RecyclerView.RecycledViewPool mRecyclerPool;
```

RecyclerView的内部类Recycler

```java
@Nullable
RecyclerView.ViewHolder tryGetViewHolderForPositionByDeadline(int position, boolean dryRun, long deadlineNs) {
    if (position >= 0 && position < RecyclerView.this.mState.getItemCount()) {
        boolean fromScrapOrHiddenOrCache = false;
        RecyclerView.ViewHolder holder = null;
        if (RecyclerView.this.mState.isPreLayout()) {
            holder = this.getChangedScrapViewForPosition(position);
            fromScrapOrHiddenOrCache = holder != null;
        }

        if (holder == null) {
            holder = this.getScrapOrHiddenOrCachedHolderForPosition(position, dryRun);
            if (holder != null) {
                if (!this.validateViewHolderForOffsetPosition(holder)) {
                    if (!dryRun) {
                        holder.addFlags(4);
                        if (holder.isScrap()) {
                            RecyclerView.this.removeDetachedView(holder.itemView, false);
                            holder.unScrap();
                        } else if (holder.wasReturnedFromScrap()) {
                            holder.clearReturnedFromScrapFlag();
                        }

                        this.recycleViewHolderInternal(holder);
                    }

                    holder = null;
                } else {
                    fromScrapOrHiddenOrCache = true;
                }
            }
        }

        int offsetPosition;
        int type;
        if (holder == null) {
            offsetPosition = RecyclerView.this.mAdapterHelper.findPositionOffset(position);
            if (offsetPosition < 0 || offsetPosition >= RecyclerView.this.mAdapter.getItemCount()) {
                throw new IndexOutOfBoundsException("Inconsistency detected. Invalid item position " + position + "(offset:" + offsetPosition + ")." + "state:" + RecyclerView.this.mState.getItemCount() + RecyclerView.this.exceptionLabel());
            }

            type = RecyclerView.this.mAdapter.getItemViewType(offsetPosition);
            if (RecyclerView.this.mAdapter.hasStableIds()) {
                holder = this.getScrapOrCachedViewForId(RecyclerView.this.mAdapter.getItemId(offsetPosition), type, dryRun);
                if (holder != null) {
                    holder.mPosition = offsetPosition;
                    fromScrapOrHiddenOrCache = true;
                }
            }

            if (holder == null && this.mViewCacheExtension != null) {
                View view = this.mViewCacheExtension.getViewForPositionAndType(this, position, type);
                if (view != null) {
                    holder = RecyclerView.this.getChildViewHolder(view);
                    if (holder == null) {
                        throw new IllegalArgumentException("getViewForPositionAndType returned a view which does not have a ViewHolder" + RecyclerView.this.exceptionLabel());
                    }

                    if (holder.shouldIgnore()) {
                        throw new IllegalArgumentException("getViewForPositionAndType returned a view that is ignored. You must call stopIgnoring before returning this view." + RecyclerView.this.exceptionLabel());
                    }
                }
            }

            if (holder == null) {
                holder = this.getRecycledViewPool().getRecycledView(type);
                if (holder != null) {
                    holder.resetInternal();
                    if (RecyclerView.FORCE_INVALIDATE_DISPLAY_LIST) {
                        this.invalidateDisplayListInt(holder);
                    }
                }
            }

            if (holder == null) {
                long start = RecyclerView.this.getNanoTime();
                if (deadlineNs != 9223372036854775807L && !this.mRecyclerPool.willCreateInTime(type, start, deadlineNs)) {
                    return null;
                }

                holder = RecyclerView.this.mAdapter.createViewHolder(RecyclerView.this, type);
                if (RecyclerView.ALLOW_THREAD_GAP_WORK) {
                    RecyclerView innerView = RecyclerView.findNestedRecyclerView(holder.itemView);
                    if (innerView != null) {
                        holder.mNestedRecyclerView = new WeakReference(innerView);
                    }
                }

                long end = RecyclerView.this.getNanoTime();
                this.mRecyclerPool.factorInCreateTime(type, end - start);
            }
        }

        if (fromScrapOrHiddenOrCache && !RecyclerView.this.mState.isPreLayout() && holder.hasAnyOfTheFlags(8192)) {
            holder.setFlags(0, 8192);
            if (RecyclerView.this.mState.mRunSimpleAnimations) {
                offsetPosition = RecyclerView.ItemAnimator.buildAdapterChangeFlagsForAnimations(holder);
                offsetPosition |= 4096;
                RecyclerView.ItemAnimator.ItemHolderInfo info = RecyclerView.this.mItemAnimator.recordPreLayoutInformation(RecyclerView.this.mState, holder, offsetPosition, holder.getUnmodifiedPayloads());
                RecyclerView.this.recordAnimationInfoIfBouncedHiddenView(holder, info);
            }
        }

        boolean bound = false;
        if (RecyclerView.this.mState.isPreLayout() && holder.isBound()) {
            holder.mPreLayoutPosition = position;
        } else if (!holder.isBound() || holder.needsUpdate() || holder.isInvalid()) {
            type = RecyclerView.this.mAdapterHelper.findPositionOffset(position);
            bound = this.tryBindViewHolderByDeadline(holder, type, position, deadlineNs);
        }

        android.view.ViewGroup.LayoutParams lp = holder.itemView.getLayoutParams();
        RecyclerView.LayoutParams rvLayoutParams;
        if (lp == null) {
            rvLayoutParams = (RecyclerView.LayoutParams)RecyclerView.this.generateDefaultLayoutParams();
            holder.itemView.setLayoutParams(rvLayoutParams);
        } else if (!RecyclerView.this.checkLayoutParams(lp)) {
            rvLayoutParams = (RecyclerView.LayoutParams)RecyclerView.this.generateLayoutParams(lp);
            holder.itemView.setLayoutParams(rvLayoutParams);
        } else {
            rvLayoutParams = (RecyclerView.LayoutParams)lp;
        }

        rvLayoutParams.mViewHolder = holder;
        rvLayoutParams.mPendingInvalidate = fromScrapOrHiddenOrCache && bound;
        return holder;
    } else {
        throw new IndexOutOfBoundsException("Invalid item position " + position + "(" + position + "). Item count:" + RecyclerView.this.mState.getItemCount() + RecyclerView.this.exceptionLabel());
    }
}
```

第一级缓存获取

```java
RecyclerView.ViewHolder getScrapOrHiddenOrCachedHolderForPosition(int position, boolean dryRun) {
    int scrapCount = this.mAttachedScrap.size();

    int cacheSize;
    RecyclerView.ViewHolder vh;
    for(cacheSize = 0; cacheSize < scrapCount; ++cacheSize) {
        vh = (RecyclerView.ViewHolder)this.mAttachedScrap.get(cacheSize);
        if (!vh.wasReturnedFromScrap() && vh.getLayoutPosition() == position && !vh.isInvalid() && (RecyclerView.this.mState.mInPreLayout || !vh.isRemoved())) {
            vh.addFlags(32);
            return vh;
        }
    }

    if (!dryRun) {
        View view = RecyclerView.this.mChildHelper.findHiddenNonRemovedView(position);
        if (view != null) {
            vh = RecyclerView.getChildViewHolderInt(view);
            RecyclerView.this.mChildHelper.unhide(view);
            int layoutIndex = RecyclerView.this.mChildHelper.indexOfChild(view);
            if (layoutIndex == -1) {
                throw new IllegalStateException("layout index should not be -1 after unhiding a view:" + vh + RecyclerView.this.exceptionLabel());
            }

            RecyclerView.this.mChildHelper.detachViewFromParent(layoutIndex);
            this.scrapView(view);
            vh.addFlags(8224);
            return vh;
        }
    }

    cacheSize = this.mCachedViews.size();

    for(int i = 0; i < cacheSize; ++i) {
        RecyclerView.ViewHolder holder = (RecyclerView.ViewHolder)this.mCachedViews.get(i);
        if (!holder.isInvalid() && holder.getLayoutPosition() == position) {
            if (!dryRun) {
                this.mCachedViews.remove(i);
            }

            return holder;
        }
    }

    return null;
}
```



内部类RecycledViewPool

```java
public static class RecycledViewPool {
    private static final int DEFAULT_MAX_SCRAP = 5;
    SparseArray<RecyclerView.RecycledViewPool.ScrapData> mScrap = new SparseArray();
    private int mAttachCount = 0;

{'fraction_non_alphanumeric': 0.0970625798212005, 'fraction_numerical': 0.006081615277017576, 'mean_word_length': 2.1825043545577705, 'pattern_counts': {'":': 0, '<': 17, '<?xml version=': 0, '>': 15, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '7384068', 'n_tokens_mistral': 4681, 'n_tokens_neox': 4343, 'n_words': 757}