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的回收流程

。。。

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 157,298评论 4 360
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 66,701评论 1 290
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 107,078评论 0 237
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 43,687评论 0 202
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,018评论 3 286
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,410评论 1 211
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,729评论 2 310
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,412评论 0 194
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,124评论 1 239
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,379评论 2 242
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 31,903评论 1 257
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,268评论 2 251
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 32,894评论 3 233
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,014评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,770评论 0 192
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,435评论 2 269
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,312评论 2 260

推荐阅读更多精彩内容