RecycleView的复用和优化

最近了解了一下RecycleView的缓存机制,做了一些记录,防止遗忘

一、RecyleView四级缓存

image

首先明确RecyecleView中缓存的对象是ViewHolder.

Recycler负责管理和缓存所有的ViewHolder。
RecycleView的缓存从上到下分为四层:scrap、cache、ViewCacheExtension、RecycleViewPool

1.1、 scrap

scrap 用来缓存正在显示的ViewHolder。

scrap 分为两个集合:mAttachedScrap 和 mChangedScrap。

  • mAttachedScrap 用于缓存正在显示的ViewHolder
  • mChangedScrap 用于缓存屏幕上发生变化的viewHolder,可能是数据发生了变化,也可能是ViewHolder类型发生了变化,mChangedScrap 中的ViewHolder会被移动到RecycledViewPool中。

当我们调用 notifyItemRangeChanged 方法的时候,会触发 requestLayout 方法,就会重新布局,重新布局的话,就会先将 viewHolder 放到 scrap 中(屏幕上变化的放入mChangedScrap 中,其余的放入mAttachedScrap 中),然后 fill 布局的时候,再从 mAttachedScrap 里面取出来直接使用。mChangedScrap 中的 viewHolder 会被移动到 RecycledViewPool 中,所以 mChangedScrap 对应的 item 需要从 pool 中取对应的 viewHolder,然后重新绑定。

image

View中的detach和remove

  • detach 在ViewGroup中的实现很简单,只是将当前View从ParentView的ChildView数组中移除,将当前View的mParent设置为null, 可以理解为轻量级的临时remove。
  • remove 代表真正的移除,不光从ChildView数组中移除,其他和View树各项联系也会被彻底斩断。

Recycled View中的Scrap View

  • Scrap View指的是在RecyclerView中,经历了detach操作的缓存。此类缓存是通过position匹配的,不需要重新bindView。
  • Recycled View指代的就是真正的移除操作remove后的缓存,取出时需重新bindView使用。

1.2、cached

数据结构mCachedViews,用于缓存从屏幕中移除,但是可能很快被再次显示的ViewHolder

  • 它是一个 ArrayList 类型,不区分 viewHolder 的类型
  • mCachedViews大小限制为2,但是你可以使用 setItemViewCacheSize()这个方法调整它的大小。

1.3、ViewCacheExtension

这个是需要自定义的,而且使用有很大的限制,所以不深入介绍了。

1.4、RecycledViewPool

RecycledViewPool 储存各个类型的 viewHolder 它缓存的是被恢复出厂设置的viewHolder,需要重新调用bind 绑定数据。

  • RecycledViewPool 是按照ItemViewType 存储ViewHolder的,每种ItemViewType最大数量为5
  • 可以通过 setMaxRecycledViews() 方法来设置每个类型储存的容量。
  • 针对RecycleView嵌套的场景,如一个纵向的RecycleView 嵌套横向的RecycleView ,可以使用 setRecycledViewPool() 方法,公用RecycledViewPool

RecycleView滑出屏幕时的ViewHolder的复用过程

image

滚出屏幕的View会优先保存到mCacheViews, 如果mCacheViews中保存满了,就会保存到RecyclerViewPool中。

  • 检查mCacheViews集合中是否还有空位,如果有空位,则直接放到mCacheViews集合
  • 如果没有的话就把mCacheViews集合中最前面的ViewHolder拿出来放到RecyclerViewPool中,然后再把最新的这个ViewHolder放到mCacheViews集合
  • 如果没有成功缓存到mCacheViews集合中,就直接放到RecyclerViewPool

二、Recycler 缓存加载流程

image
  • scrap负责缓存屏幕中正在显示的ViewHolder,命中缓存后直接使用,不需要create和Bind
  • 如果在 cache (mCachedViews)负责缓存刚刚移出屏幕,很可能被复用的ViewHolder。通过position获取,命中后不需create和bind
  • ViewCacheExtension google预留的一个空的缓存,暂不讨论
  • pool (RecycledViewPool )根据ViewType缓存ViewHolder, 用于缓存数据解绑后的ViewHolder,pool中命中的viewHolder 需要进行重新bind 进行数据绑定
  • 如果所有缓存中都没有命中 viewHolder,会重新调用createViewHolder 和 bindViewHolder

三、一些优化方法

3.1、 setHastFixedSize

当知道Adapter内Item的改变不会影响RecyclerView宽高的时候,可以设置为true让RecyclerView避免重新计算大小。

注意两点:

  • 当调用Adapter的增删改插方法,最后就会根据mHasFixedSize这个值来判断需要不需要requestLayout();
onItemRangeChanged(),

onItemRangeInserted(),

onItemRangeRemoved(),

onItemRangeMoved()

上面四个方法会调用triggerUpdateProcessor方法

    void triggerUpdateProcessor() {
            if (POST_UPDATES_ON_ANIMATION && mHasFixedSize && mIsAttached) {
                ViewCompat.postOnAnimation(RecyclerView.this, mUpdateChildViewsRunnable);
            } else {
                mAdapterUpdateDuringMeasure = true;
                requestLayout();
            }
        }

会根据mHasFixedSize这个值来判断需要不需要requestLayout();

  • 当调用Adapter的notifyDataSetChanged() 最后调用了onChanged,调用了requestLayout(),会去重新测量宽高。
   public void onChanged() {
            assertNotInLayoutOrScroll(null);
            mState.mStructureChanged = true;

            processDataSetCompletelyChanged(true);
            if (!mAdapterHelper.hasPendingUpdates()) {
                requestLayout();
            }
        }

3.2、setHasStableIds

Adapter.setHasStablesId(true)开启固定ID

DemoAdapter  mAdapter=new DemoAdapter();
mAdapter.setHasStablesId(true);

在 Adapter 类中重写getItemId来给每个 Item 一个唯一的ID。

@Override
public long getItemId(int position){
    return items.get(position).getId();
}

setHasStableIds(true)之后,数据为发生变化情况下,滚动recycleView

ViewHolder会被缓存到mAttachedScrap中,复用时通过position 从mAttachedScrap直接取出显示,不需要重新createViewHolder、bindViewHolder

用空间换时间,
从而规避滑动recyelveView过程中出现的闪烁问题。

3.3、recycleView 图片列表快速刷新

recyleView 中显示图片列表,快速滑动容易出现卡顿。一个优化思路,可以设置在滑动过程中暂停正在加载的图片,滑动停止之后再恢复图片的加载。

recyceView?.addOnScrollListener(object : RecyclerView.OnScrollListener() {
            override fun onScrollStateChanged(recyclerView: RecyclerView, newState: Int) {
                super.onScrollStateChanged(recyclerView, newState)
                 if (newState == RecyclerView.SCROLL_STATE_IDLE) {
                    Glide.with(requireActivity()).resumeRequests()
                } else {
                    Glide.with(requireActivity()).pauseRequests()
                }
            }
        })

参考文章:

https://www.jianshu.com/p/1d2213f303fc
https://blog.csdn.net/weixin_43130724/article/details/90068112
https://www.jianshu.com/p/4a2b18135447

https://zhuanlan.zhihu.com/p/80475040

https://www.jianshu.com/p/aeb9ccf6a5a4

图片闪烁问题分析:
https://www.jianshu.com/p/29352def27e6

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

推荐阅读更多精彩内容