RecyclerView缓存相关

RecyclerView基本结构

RecyclerView:列表容器

LayoutManager:布局管理器

Recycler:管理列表项的创建、回收、复用

Adapter:用于列表项的创建与数据绑定

ViewHodler:RecyclerView缓存的基本单元,持有ItemView

缓存结构

RecyclerView有四级缓存,相关字段都放在Recycler这个RecyclerView的内部类里,这里按照缓存的查找顺序从左到右排列

ScrapView

Scrap分为mAttachedScrap与mChangedScrap,是仅在布局阶段中使用到的缓存列表,无数量限制,每一次布局是都会将屏幕上的holder放入scrap中,布局完后将这个列表的ViewHolder清空,没有复用的holder将放入到RecyclerViewPool中,Scrap中的View出于detach状态,但是还没有被RecyclerView remove掉。

mAttachedScrap:数据不合法(调用notify)或者数据未变更的holder,后者可以直接复用,而无需绑定数据

mChangedScrap:数据发生变更的ViewHolder,例如调用了notifyItemChanged的holder。

CachedViews

在全生命周期流程中使用到,其中保存了刚刚滑出屏幕的ViewHolder,这些holder在滑回来时可以直接使用而不需要重新绑定数据,默认缓存数量是2个,在GapWorker机制下可以有更多个。

ViewCacheExtension

在全生命周期流程中使用到,无默认实现,可外部注入,只能用来提供ViewHolder,无存入逻辑,基本没有使用场景,可考虑用于提供预加载的View。

less 复制代码
public abstract static class ViewCacheExtension {
    @Nullable
    public abstract View getViewForPositionAndType(@NonNull Recycler recycler, int position, int type);
}

RecyclerViewPool

在全生命周期流程中使用到,按照类型去缓存ViewHoder,每种类型默认缓存5个,可动态设置,超出缓存数量后多余的ViewHolder会被丢弃。支持多个RecyclerView设置同一个pool,做到跨场景的复用。

相关机制

StableId

用于给每一个Item分配一个独立的id,它所带来的影响如下:

当代码中调用了notifyDataSetChange时,没有设置StableId的情况下,由于RecyclerView不知道哪些holder是有效的,所以所有的ViewHolder都会先被回收到pool中,再从pool里复用。 而当设置了StableId后,RecyclerView知道哪些item是还在的,所以它会先将所有的holder放入到scrap列表中,再从scrap里进行复用,剩余没复用的才会存放到pool里。这样虽能减少复用流程但是也有个坑,在设置stableId之前,item是可以复用到pool里的holder,而设置了stableId之后,由于这些holder不会回收到pool,而pool里又没有对应类型的holder时,将会通过adapter创建新的holder,导致性能下降。

GapWorker

RecyclerView的一个预加载机制,仅在滑动场景下生效(拖拽、惯性、嵌套滑动),它会在主线程空闲期间,根据滑动方向和距离去预先准备下一个item(包含创建与绑定数据),当产生滑动后,它首先去计算下一帧到来的时间,然后判断下一帧到来之前是否能将ViewHolder创建好,如果可以它就会去创建ViewHolder,否则不创建。

创建好的Holder会被缓存到mCachedView里,并且会扩大cacheView的size,不会挤出调正常滑动产生的cacheView。

离屏回收

typescript 复制代码
#LinearLayoutManager.java
public void setRecycleChildrenOnDetach(boolean recycleChildrenOnDetach) {
    mRecycleChildrenOnDetach = recycleChildrenOnDetach;
}

字面意思就是当recyclerView从父容器脱离后,回收所有的view到RecyclerViewPool中,如果此RecyclerView与其他RecyclerView共享一个pool,那就可以很好的提供holder给其他场景的RecyclerView了,但是这样也有概率产生bug,一个RecyclerView在detach的时候回收了所有的item,但是重新attach的时候却没有恢复item(原因未知)。导致界面空白。

完整复用流程

1、如果是预布局(动画相关)则从mChangedScrap中查找holder,只需要position对上即可。

2、从mAattchedScrap、mCachedViews中查找holder,需要position对上且数据有效才能返回(调用notifyxxx则会导致数据失效)。

3、如果有设置stableId,则从第2步的缓存列表中查找holdler,与第2步不同的是,不再用position去匹配holder了,而是通过id,同时也不再判断数据是否有效。

4、从ViewCacheExtension中获取holder(忽略)。

5、从RecyclerViewPool中获取holder。

6、通过adapter创建holder。

7、如果有需要,则绑定holder的数据。(如调了notifyxxx的holder,即便有设置stableId并且id一致也会调用。)

相关推荐
恋猫de小郭1 小时前
IntelliJ IDEA 2024.3 K2 模式已发布稳定版,Android Studio Meerkat 预览也正式支持
android·android studio
找藉口是失败者的习惯5 小时前
Jetpack Compose 如何布局解析
android·xml·ui
Estar.Lee10 小时前
查手机号归属地免费API接口教程
android·网络·后端·网络协议·tcp/ip·oneapi
温辉_xh10 小时前
uiautomator案例
android
工业甲酰苯胺11 小时前
MySQL 主从复制之多线程复制
android·mysql·adb
少说多做34311 小时前
Android 不同情况下使用 runOnUiThread
android·java
Estar.Lee13 小时前
时间操作[计算时间差]免费API接口教程
android·网络·后端·网络协议·tcp/ip
找藉口是失败者的习惯13 小时前
从传统到未来:Android XML布局 与 Jetpack Compose的全面对比
android·xml
Jinkey14 小时前
FlutterBasic - GetBuilder、Obx、GetX<Controller>、GetxController 有啥区别
android·flutter·ios
大白要努力!16 小时前
Android opencv使用Core.hconcat 进行图像拼接
android·opencv