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一致也会调用。)

相关推荐
百锦再2 小时前
第11章 泛型、trait与生命周期
android·网络·人工智能·python·golang·rust·go
会跑的兔子3 小时前
Android 16 Kotlin协程 第二部分
android·windows·kotlin
键来大师3 小时前
Android15 RK3588 修改默认不锁屏不休眠
android·java·framework·rk3588
江上清风山间明月5 小时前
Android 系统超级实用的分析调试命令
android·内存·调试·dumpsys
百锦再6 小时前
第12章 测试编写
android·java·开发语言·python·rust·go·erlang
用户69371750013849 小时前
Kotlin 协程基础入门系列:从概念到实战
android·后端·kotlin
SHEN_ZIYUAN10 小时前
Android 主线程性能优化实战:从 90% 降至 13%
android·cpu优化
曹绍华10 小时前
android 线程loop
android·java·开发语言
雨白10 小时前
Hilt 入门指南:从 DI 原理到核心用法
android·android jetpack
介一安全10 小时前
【Frida Android】实战篇3:基于 OkHttp 库的 Hook 抓包
android·okhttp·网络安全·frida