RecyclerView的功能优化
在咱们谈RecyclerView的功能优化之前,先让咱们回顾一下RecyclerView的缓存机制。
RecyclerView缓存机制
众所周知,RecyclerView拥有四级缓存,它们分别是:
- Scrap缓存:包含mAttachedScrap和mChangedScrap,又称屏内缓存,不参加滑动时的收回复用,只是用作临时保存的变量。
- mAttachedScrap:只保存从头布局时从RecyclerView别离的item的无效、未移除、未更新的holder。
- mChangedScrap:只会担任保存从头布局时产生改变的item的无效、未移除的holder。
- CacheView缓存:mCachedViews又称离屏缓存,用于保存最新被移除(remove)的ViewHolder,现已和RecyclerView别离的视图,这一级的缓存是有容量限制的,默许最大数量为2。
- ViewCacheExtension:mViewCacheExtension又称拓宽缓存,为开发者预留的缓存池,开发者能够自己拓宽收回池,一般不会用到。
- RecycledViewPool:终极的收回缓存池,真实存放着被标识废弃(其他池都不愿意收回)的ViewHolder的缓存池。这里的ViewHolder是现已被抹除数据的,没有任何绑定的痕迹,需求从头绑定数据。
RecyclerView的收回原理
(1)假如是RecyclerView不翻滚状况下缓存(比方删除item)、从头布局时。
- 把屏幕上的ViewHolder与屏幕别离下来,存放到Scrap中,即产生改动的ViewHolder缓存到mChangedScrap中,不产生改动的ViewHolder存放到mAttachedScrap中。
- 剩余ViewHolder会按照
mCachedViews
>RecycledViewPool
的优先级缓存到mCachedViews或许RecycledViewPool中。
(2)假如是RecyclerView翻滚状况下缓存(比方滑动列表),在滑动时填充布局。
- 先移除滑出屏幕的item,第一级缓存mCachedViews优先缓存这些ViewHolder。
- 由于mCachedViews最大容量为2,当mCachedViews满了以后,会利用先进先出准则,把旧的ViewHolder存放到RecycledViewPool中后移除掉,腾出空间,再将新的ViewHolder添加到mCachedViews中。
- 最后剩余的ViewHolder都会缓存到终极收回池RecycledViewPool中,它是依据itemType来缓存不同类型的ArrayList,最大容量为5。
RecyclerView的复用原理
当RecyclerView要拿一个复用的ViewHolder时:
- 假如是预加载,则会先去mChangedScrap中精准查找(分别依据position和id)对应的ViewHolder。
- 假如没有就再去mAttachedScrap和mCachedViews中精确查找(先position后id)是不是本来的ViewHolder。
- 假如还没有,则终究去mRecyclerPool找,假如itemType类型匹配对应的ViewHolder,那么回来实例,让它
从头绑定数据
。 - 假如mRecyclerPool也没有回来ViewHolder才会调用
createViewHolder()
从头去创立一个。
这里有几点需求注意:
- 在mChangedScrap、mAttachedScrap、mCachedViews中拿到的ViewHolder都是精准匹配。
- mAttachedScrap和mCachedViews没有产生改变,是直接运用的。
- mChangedScrap由于产生了改变,mRecyclerPool由于数据已被抹去,所以都需求调用
onBindViewHolder()
从头绑定数据才能运用。
缓存机制总结
- RecyclerView最多能够缓存 N(屏幕最多可显现的item数【Scrap缓存】) + 2 (屏幕外的缓存【CacheView缓存】) + 5*M (M代表M个ViewType,缓存池的缓存【RecycledViewPool】)。
- RecyclerView实际只要两层缓存可供运用和优化。由于Scrap缓存池不参加翻滚的收回复用,所以CacheView缓存池被称为一级缓存,又由于ViewCacheExtension缓存池是给开发者定义的缓存池,一般不用到,所以RecycledViewPool缓存池被称为二级缓存。
假如想深入了解RecyclerView缓存机制的同学,能够参阅《RecyclerView的收回复用缓存机制详解》 这篇文章。
功能优化方案
依据上面咱们对缓存机制的了解,咱们能够简略得到以下几个大方向:
- 1.进步ViewHolder的复用,削减ViewHolder的创立和数据绑定作业。【最重要】
- 2.优化
onBindViewHolder
办法,削减ViewHolder绑定的时刻。由于ViewHolder可能会进行屡次绑定,所以在onBindViewHolder()
尽量只做简略的作业。 - 3.优化
onCreateViewHolder
办法,削减ViewHolder创立的时刻。
进步ViewHolder的复用
1.多运用Scrap进行部分更新。
- (1) 运用
notifyItemChange
、notifyItemInserted
、notifyItemMoved
和notifyItemRemoved
等办法代替notifyDataSetChanged
办法。 - (2) 运用
notifyItemChanged(int position, @Nullable Object payload)
办法,传入需求改写的内容进行部分增量改写。这个办法一般很少有人知道,详细做法如下:- 首要在notify的时候,在payload中传入需求改写的数据,一般运用Bundle作为数据的载体。
- 然后重写
RecyclerView.Adapter
的onBindViewHolder(@NonNull RecyclerViewHolder holder, int position, @NonNull List<Object> payloads)
办法@Override public void onBindViewHolder(@NonNull RecyclerViewHolder holder, int position, @NonNull List<Object> payloads) { if (CollectionUtils.isEmpty(payloads)) { Logger.e("正在进行全量改写:" + position); onBindViewHolder(holder, position); return; } // payloads为非空的状况,进行部分改写 //取出咱们在getChangePayload()办法回来的bundle Bundle payload = WidgetUtils.getChangePayload(payloads); if (payload == null) { return; } Logger.e("正在进行增量改写:" + position); for (String key : payload.keySet()) { if (KEY_SELECT_STATUS.equals(key)) { holder.checked(R.id.scb_select, payload.getBoolean(key)); } } }
详细运用办法可参阅XUI中的RecyclerView部分增量改写 中的代码。
- (3) 运用
DiffUtil
、SortedList
进行部分增量改写,进步改写效率。和上面讲的传入payload
原理相同,这两个是Android默许提供给咱们运用的两个封装类。这里我以DiffUtil
举例说明该如何运用。- 首要需求完成
DiffUtil.Callback
的5个笼统办法,详细可参阅DiffUtilCallback.java - 然后调用
DiffUtil.calculateDiff
办法回来比较的成果DiffUtil.DiffResult
。 - 最后调用
DiffUtil.DiffResult
的dispatchUpdatesTo
办法,传入RecyclerView.Adapter进行数据改写。
- 首要需求完成
详细运用办法可参阅XUI中的DiffUtil部分改写 和 XUI中的SortedList自动数据排序改写 中的代码。
2.合理设置RecyclerViewPool的巨细。假如一屏的item较多,那么RecyclerViewPool的巨细就不能再运用默许的5,可适度增大Pool池的巨细。假如存在RecyclerView中嵌套RecyclerView的状况,能够考虑复用RecyclerViewPool缓存池,削减开支。
3.为RecyclerView设置setHasStableIds
为true,并一起重写RecyclerView.Adapter的getItemId
办法来给每个Item一个唯一的ID,进步缓存的复用率。
4.视状况运用setItemViewCacheSize(size)
来加大CacheView缓存数目,用空间交换时刻进步流通度。对于可能来回滑动的RecyclerView,把CacheViews的缓存数量设置大一些,能够省去ViewHolder绑定的时刻,加速布局显现。
5.当两个数据源大部分类似时,运用swapAdapter
代替setAdapter
。这是由于setAdapter
会直接清空RecyclerView上的所有缓存,但是swapAdapter
会将RecyclerView上的ViewHolder保存到pool中,这样当数据源类似时,就能够进步缓存的复用率。
优化onBindViewHolder办法
1.在onBindViewHolder办法中,去除冗余的setOnItemClick等事件。由于直接在onBindViewHolder办法中创立匿名内部类的办法来完成setOnItemClick,会导致在RecyclerView快速滑动时创立很多对象。应当把事件的绑定在ViewHolder创立的时候和对应的rootView进行绑定。
2.数据处理与视图绑定别离,去除onBindViewHolder办法里边的耗时操作,只做朴实的数据绑定操作。当程序走到onBindViewHolder办法时,数据应当是预备齐备的,禁止在onBindViewHolder办法里边进行数据获取的操作。
3.有很多图片时,翻滚时中止加载图片,中止后再去加载图片。
4.对于固定尺寸的item,能够运用setHasFixedSize
避免requestLayout
。
优化onCreateViewHolder办法
1.下降item的布局层级,能够削减界面创立的渲染时刻。
2.Prefetch预取。假如你运用的是嵌套的RecyclerView,或许你自己写LayoutManager,则需求自己完成Prefetch,重写collectAdjacentPrefetchPositions
办法。
其他
以上都是针对RecyclerView的缓存机制打开的优化方案,其实还有几种方案可供参阅。
1.取消不需求的item动画。详细的做法是:
((SimpleItemAnimator) recyclerView.getItemAnimator()).setSupportsChangeAnimations(false);
2.运用getExtraLayoutSpace
为LayoutManager设置更多的预留空间。当RecyclerView的元素比较高,一屏只能显现一个元素的时候,第一次滑动到第二个元素会卡顿,这个时候就需求预留的额定空间,让RecyclerView预加载可重用的缓存。
最后
以上就是RecyclerView功能优化的全部内容,俗话说:百闻不如一见,百见不如一干,大家仍是赶忙着手尝试着开始进行优化吧!
我是xuexiangjys,一枚热爱学习,喜好编程,勤于思考,致力于Android架构研究以及开源项目经历分享的技术up主。获取更多资讯,欢迎微信搜索公众号:【我的Android开源之旅】
本文正在参加「金石方案 . 瓜分6万现金大奖」