RecyclerView的收回复用机制
这道题想调查什么?
调查同学是否对RecyclerView的Recycler熟悉。
考生应该如何回答
问题剖析
RecyclerView是一个大的概念,从界面层次剖析一下它的组成部分,它是由多个Item。Item的创立、赋值、收回、复用与RecyclerView休戚相关,所以列表项是研究RecyclerView研究的重点切入口。
列表项Item有两个非常重要的内容,一个是Item的界面生成,这个和适配器中的onCreateViewHolder相关;别的一个是列表项Item的数据绑定,这个和适配器中的onBindViewHolder相关。onCreateViewHolder是创立ViewHolder,在这个进程中会经过LayoutInflater去生成界面,而LayoutInflater是经过反射的方法实例化View的,依据这个状况可知创立ViewHolder是比较耗时的。onBindViewHolder是绑定相关的视图数据,这个进程中假如设置内容过多或者计算过多都会比较耗时。
当RecyclerView的设计者剖分出onCreateViewHolder、onBindViewHolder这两个函数是比较耗时的状况下,很自然会采取战略便是减少onCreateViewHolder和onBindViewHolder调用,到达功能最优的作用。
状况剖析
假如当时需求创立一个列表项Item,能够选择onCreateViewHolder创立,也能够选择先去读取缓存。依据缓存的界面、数据的不同,会存在两种状况:第一种状况,缓存中有与需求创立列表项item同类型(适配器中的ViewType)的缓存,那么就不需求从头创立,也便是减少了onCreateViewHolder的调用。第二种状况,假如存在一种缓存,它与需求创立的列表项Item类型相同,而且数据相同,那么就不需求从头绑定数据,更不需求从头创立,也便是同时减少onCreateViewHolder、onBindViewHolder的调用。
缓存类型
据上面的状况剖析能够得出缓存的两种类型:第一类是与需求创立Item的类型相同但数据不相同,这种类型的缓存是RecycledViewPool,它给到每个Type(Type指的是适配器Adapter中的ItemType)的缓存容量默许是5。第二类是与需求创立列表项Item的类型相同而且数据相同,这种类型的缓存包含Scrap、CacheViews;
使用场景
当RecyclerView的数据或者界面发生改动的时分,就会用到缓存,大体分为两类状况,一类是进行滑动列表的时分,别的一类便是自动的数据更新。滑动列表的啥时分,一部分Item需求脱离屏幕,别的一部分Item需求进入屏幕,进入屏幕这部分Item就非常有可能是从缓存中读取的而来。 自动更新数据,比方经过notifyDataSetChanged更新一切Item数据、经过notifyItemChanged进行部分Item更新,会将数据先放置到缓存容器中,然后在进行相关的更新操作。
滑动
Item收回
在滑动的进程中是有item脱离屏幕,有item进入屏幕,这个进程了就会触及收回数据和读取缓存,两部分的内容,本文也是从这两个方面给咱们讲解。
图7.32.1
上图7.32.1描绘的是RecyclerView的一个上滑的进程,左边用来表示一个RecyclerView的界面部分,上下两根横线别离代表着屏幕的上边界和下边界方便咱们观察作用。右边是缓存包含CacheViews和RecycledViewPool两种。
图7.32.2 如图7.32.2所示,当列表不断上滑的进程中了,Item1会脱离屏幕,Item假如脱离屏幕就会参加到缓存,首先参加的便是图7.32.2左边的CacheViews缓存。
图7.32.3 如图7.32.3所示,当列表项Item3脱离屏幕时,CacheViews保存着Item1和Item2。这个时分就会呈现一个问题,CacheViews现已装满了,Item3又需求参加这个容器中来,如何处理?其实很简单,新的参加旧的出局,那么CacheViews里边装的便是Item3和Item2。Item1何处何从?它会组织进去RecycledViewPool里边,不过这里会呈现一个改动,那便是Item的数据失效了。如下图7.32.4所示,一切参加RecycledViewPool的缓存假如被读取了,都会需求从头绑定数据即会调用onBindViewHolder。
图7.32.4Item缓存读取
在不断向上滑动的进程中,有新的Item进入屏幕,那新的列表项从何而来?有可能是经过onCreateViewHolder新创立的,也有可能是从缓存中读取而来。
有新的Item需求进入屏幕的时分,会先读取的是CacheViews的缓存,那是任意一个缓存的Item都能够?必定不是,必须要符合方位position相同或ID相同的条件。整个进程会先匹配position(对应适配器里边的position),假如有同position缓存,说明正式需求寻觅的数据。假如经过position没有寻觅到数据,那就会经过id(对应适配器中getViewId中返回的id,默许是0)再寻觅一遍。假如找到相同id的缓存则读取结束,没有读取到的话,就会在RecycledViewPool中选中一个同类型(对应适配器中getViewType返回的Type)的缓存,RecyclerViewPool找到的缓存是需求从头绑定数据的。
图7.32.5
假如RecyclerViewPool中也没有找到缓存,则只能经过CreateViewHolder创立item,然后经过onBindViewHolder绑定数据。
Notify更新数据
NotifyDataSetChanged
假如调用notifyDataSetChanged这个api,这个函数调用的结果是更新界面上一切item的显现内容。这个时分因为复用机制的存在,所以表项Item的界面(View自身)能够复用,可是Item展现的数据是需求从头更新的。在上述进程中Item会先参加缓存,然后再从缓存中读取出来从头绑定数据,符合要求的缓存容器只要RecycledViewPool,因为它保存的Item是需求从头绑定数据的,符合当时的刷新要求。
RecycledViewPool对每个Type的缓存设置的巨细是5。假如调用notifyDataSetChanged这个api,说明有五个Item会参加到RecycledViewPool这一级的缓存里边来,然后在被读取从头赋予新的数据用于展现,这部分的Item是能够有效地收回复用。假如屏幕中展现的列表项Item大于五,因为缓存容器巨细的原因,只能有五个Item能够参加到缓存容器RecycledViewPool,能够收回复用,其余的部分Item因为不能及时参加缓存容器RecycledViewPool,那么它的空间就不会被收回复用,导致空间的糟蹋,导致之后需求从头创立。
假如一定要经过 notifyDataSetChange 方法更新数据,能够经过下面这种方法,在更新前调大缓存空间巨细,更新完成后再调小缓。这种方法能够最大程度地复用已有的 ViewHolder,到达功能最优的作用。
mRecyclerView.getRecycledViewPool().setMaxRecycledViews(0, 屏幕显现的item总数+1 );
mAdapter.notifyDataSetChanged();
new Handler().post(new Runnable() {
@Override
public void run() {
mRecyclerView.getRecycledViewPool()
.setMaxRecycledViews(0, 5);
}
});
setMaxRecycledViews中填入的第一个参数是Type,第二个参数是数目,一般是当时屏幕显现Item的总数加一,加一的原因是因为RecyclerView除了加载当时屏幕的Item以外了,还会额定再加载一个Item。
之所以从头又将MaxRecycledViews的值设置回5,是因为缓存空间变大是满意当时notifyDataSetChange 的需求,后面的一些业务并不是缓存空间越大越好,调回来也是为了减少对后续操作的影响。
NotifyItemChanged
如何只需求更新一个Item的话,能够调用notifyItemChanged,这样的话就只是部分更新,相对来说功能耗费会小很多。假如当时屏幕内有六个列表项Item1…Item7,咱们点击其中的Item4,在这个进程中了,Item4的界面能够复用,数据需求从头绑定,Item1、Item2、Item3、 Item5、Item6、Item7界面和数据都能够复用不需求做任何的改动,那RecyclerView是如何处理这个操作的呢?不着急咱们渐渐讲,Item1、Item2、Item3、 Item5、Item6、Item7会被参加到Scrap中的mAttachedScrap里边,需求替换数据的Item4会被参加到mChangedScrap缓存中,相信咱们看完很清楚,不要变的放一同,需求改动下的又放别的一个容器。之后再经过比对position、id等方法读取出缓存的内容,进行相关的测量布局设置作业,那整个进程就讲清楚了。
总结
- AttachedScrap、ChangedScrap只与Notify更新数据有关,与滑动进程的收回无关,二者的空间是没有限制巨细的。在一次Notify更新的进程中不需求发生改动的列表项Item会存放在AttachedScrap内,需求更新的列表项Item会放置在ChangedScrap,所以AttachedScrap、ChangedScrap被称之为屏幕内的缓存。
- CacheViews是用于保存最新被移除(
remove
)的列表项Item,一般指的是滑动进程中脱离屏幕的列表项Item,会精准的经过方位Position、ID匹配当时缓存是否为需求的内容。因为是精准匹配,所以读取出来的缓存是不需求从头绑定数据的。 - RecycledViewPool是一个终极收回站,真正存放着被标识抛弃(其他池都不愿意收回)的ViewHolder的缓存池,假如上述AttachedScrap、ChangedScrap、CachedViews都找不到ViewHolder的状况下,就会从RecycledViewPool返回一个抛弃的ViewHolder实例。
详细重视公众号:Android老皮
还能解锁 《Android十大板块文档》 ,让学习更靠近未来实战。已形成PDF版
内容如下:
1.Android车载使用开发体系学习指南(附项目实战)
2.Android Framework学习指南,助力成为体系级开发高手
3.2023最新Android中高档面试题汇总+解析,告别零offer
4.企业级Android音视频开发学习路线+项目实战(附源码)
5.Android Jetpack从入门到通晓,构建高质量UI界面
6.Flutter技能解析与实战,跨渠道首要之选
7.Kotlin从入门到实战,全方面提高架构根底
8.高档Android插件化与组件化(含实战教程和源码)
9.Android 功能优化实战+360全方面功能调优
10.Android零根底入门到通晓,高手进阶之路