RecyclerView的缓存机制(面试常客)

news2025/1/11 18:30:50

在构建滚动列表时,我们常首选RecyclerView,出于它优秀的缓存复用机制。

核心机制

RecyclerView的缓存机制又称回收复用机制,RecyclerView构建列表视图分为以下三步:

 第一步的创建ViewHolder是RecyclerView构建视图时最耗时的操作,而RecyclerView正是因为做到了缓存复用,从而减少调用创建ViewHolder的次数。

当列表被创建时结构如下,每个ViewHolder中的A、B代表不同的View种类(ViewType):

当我们滚动第一个ViewHolder(此处简称p0)到屏幕以外时,它会被存入mCachedViews中:

此处被回收的p0依旧保存着之前的数据,各种内部状态没有被重置。

如果我们向上滚动,被缓存的p0会从mCachedViews缓存区中移回列表:

 

因此不需要重复CreateViewHolder和BindViewHolder,减少了耗时。

可见,缓存区是用来存储最近离开屏幕区的ViewHolder。 这些ViewHolder因为出于屏幕区的边界,更容易因为用户的滚动和抖动被重新显示,因此更需要被快速的加载到视图中。

缓存区存储ViewHolder的默认大小为2,开发者可根据实际需求更改大小。

当缓存区已满,而有新的ViewHolder被放入缓存区时,最先被置入缓存区的ViewHolder会被移到mRecyclerPool中:

进入mRecyclerPool的ViewHolder各种内部状态会被重置,如position会被置为-1,可理解为与之前的数据已解绑;图中的A和B表示不同的ViewType,每个能存储5个ViewHolder,这个大小也是可以被修改的。

分析完列表移出ViewHolder的过程,我们反过来看移入ViewHolder的:

假设此时有一个新的类型为B的ViewHolder(简称p7)要移入屏幕区。

首先查看缓存区中是否有可以复用的ViewHolder。这是因为被放入此处的ViewHolder是不需要重置数据的,因此优先考虑这里的ViewHolder,如果有相同的position,就会被取出复用。此处的逻辑和前面p0重新移入的逻辑相同;

图中的例子position=7,与缓存区中的两个ViewHolder的position都不相同,因此无法复用;接下来查看循环池中的ViewHolder。因为View类型为B的循环池中没有ViewHolder,所以依然无法复用;此时我们只能重新走Create和Bind的方法。

如果循环池中有相同类型的ViewHolder:

会从循环池中取出复用,因为被放入循环池中的ViewHolder都是被重置了的,虽然不用走Create方法,但是重新的Bind还是要有的。

是否可以只用一种ViewType?

对于不同的ViewType我们有不同的循环池,那我们是否可以只用一种ViewType,延长这一个循环池,提高回收复用率?

首先是可读性和可维护性会变差。因为不同的ViewType通常数据结构和视图样式方式不同,如果为了让一种ViewType都适配他们,代码肯定会变得很乱。

其次是不用懒加载,增加调用耗时;使用懒加载,无法避免视图切换耗时。首先因为ViewType的数据结构和视图样式等诸多内容不尽相同,如果一次全部加载内存开销必然很大,因此我们使用懒加载的方式最为合适,只有需要的时候才取出使用;

但因为我们把原本应该设计为不同的ViewType都塞进了一个ViewType,导致会一次加载所有原本不同的ViewType,无法使用懒加载避免这一现象;

而同时因为ViewType视图样式不同,肯定要重新inflate不同的视图样式,根本没有减少耗时。

RecyclerView缓存机制失效的情况

重写Adapter的onFailedToRecycleView

首先查看RecyclerView的源码:

在是否回收ViewHolder的判断中包含两个条件:boolean值forceRecycle和isRecyclable方法。我们首先查看后者代码:

后者的isRecyclable方法获取到的是一个名为TransientState的值(直译:临时状态)。当某个ViewHolder中的某个View在做属性动画时,这个值会属性动画开始和结束时被设置:

 

这么来说,如果一个ViewHolder在离开显示区时,同时内部有任意一个view在做属性动画,TransientState的值就会是false,isRecyclable方法返回的也是false,这个ViewHolder就无法被存储到缓存区或循环池中。

这种情况最容易出现在ViewHolder中存在循环属性动画的场景。如果没有对这个循环属性动画做处理,那么就会出现缓存机制失效的情况。如dy中的专辑旋转动画:

而是否回收ViewHolder的判断中,前者forceRecycle是根据onFailedToRecycleView方法设定的,我们可以重写这个方法:

override fun onFailedToRecycleView(holder: MyViewHolder?): Boolean {
    return true
}

onBindViewHolder中重置View的动画属性

为什么Android要设计ViewHolder中的View的属性动画不能被回收,要我们如此大费周章?

这么设计的原因是防止ViewHolder被复用时之前的动画状态被保留。因此我们可以在重新绑定ViewHolder的onBindViewHolder方法中,重置View的动画属性,如偏转角度(Rotation)、透明度(Alpha)等。

RecyclerView的特殊复用机制

循环池(mRecyclerPool)可以服务于多个RecyclerView。当其他RecyclerView中拥有循环池中相同的ViewType时,他们都可以使用这个循环池。

参考 

Android RecyclerView的缓存机制_哔哩哔哩_bilibili

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2040337.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

鸿蒙(API 12 Beta3版)【使用通话设备切换组件】使用投播组件

基本概念 系统不再提供音频输出设备切换的API,如果需要应用内切换音频输出设备,请实现AVCastPicker组件,相关参数可参考[ohos.multimedia.avCastPicker]和 [ohos.multimedia.avCastPickerParam]。 本文将主要介绍AVCastPicker组件接入&…

【论文笔记】:PVswin-YOLOv8s:基于无人机的行人和车辆检测,使用改进的YOLOv8在智能城市中进行交通管理

摘要 在智慧城市中,有效的交通拥堵管理取决于熟练的行人和车辆检测。无人机 (UAV) 提供了一种具有移动性、成本效益和宽视野的解决方案,然而,优化识别模型对于克服小型和遮挡物体带来的挑战至关重要。为了解决这些问题…

推出 SAM 2:适用于视频和图像的下一代 Meta Segment Anything 模型

继图像元分割模型(SAM) 取得成功之后,我们发布了SAM 2,这是一个用于在图像和视频中实时提示对象分割的统一模型,可实现最先进的性能。 为了秉承我们的开放科学方针,我们通过宽松的 Apache 2.0 许可证共享代码和模型权重。 我们还…

嵌入式linux系统镜像制作day2

点击上方"蓝字"关注我们 01、前言 嵌入式linux系统镜像制作day1这一节先了解,后面实操 02、Yocto项目快速启动 Yocto项目通过OpenEmbedded构建系统为各种平台(包括x86-64和仿真平台)提供了一个针对ARM、MIPS、PowerPC和x86架构的开源开发环境。您可以使用Yocto项…

WebDeveloper:1靶机渗透测试

一、靶机下载地址 https://www.vulnhub.com/entry/web-developer-1,288/ 二、信息收集 1、主机发现 # 使用命令 nmap 192.168.145.0/24 -sn | grep -B 2 "00:0C:29:54:22:E9" 2、端口扫描 # 使用命令 nmap 192.168.145.216 -p- -sV 3、指纹识别 # 使用命令 wha…

【SpringCloud】什么是MQ

RabbitMQ 1.什么是MQ 1.1 同步和异步通信 微服务间通讯有同步和异步两种方式: 同步通讯:就例如打电话一样,需要实时响应异步通讯:就例如发邮件一样,不需要马上回复 1.1.1同步通讯 Feign的远程调用就属于同步通讯…

【数据分析】描述性统计分析 - 直方图

一、什么是直方图 由一批长方形构成,通过长方形的面积或高度来代表对应组在数据中所占的比例。用长方形的面积代表对应组的频数与组距的比时,则称为频率分布直方图;当用长方形的高代表对应组的频数时,则称为频数分布直方图。但严格统计意义上…

WT32-ETH01开发板模块,启明云端物联网方案,乐鑫ESP32多样化开发应用

在物联网(IoT)的浪潮中,无线Wi-Fi模块作为连接传统硬件与现代智能网络的桥梁,正逐渐成为智能家居和设备通信不可或缺的一部分。Wi-Fi模块也被称为串口Wi-Fi模块,是一种嵌入式模块,它能够将串口或TTL电平信号转换为符合Wi-Fi无线网…

普元EOS-多数据源时业务数据库初始化

1 问题 EOS开发应用的时候,可以采用多数据源。 项目启动的是时候报错如下: 2024-08-14 16:03:56.105 ERROR [EOS-DEMO-SYS,EOSLOWCODE,default,cuipengyu:EOSLOWCODE:28015,,,,] 28960 --- [Timer-0] c.p.g.a.a.m.RdcloudStatisticsThread : User…

66_1JSON【浏览器中处理JSON、Java中处理JSON(FastJSON、Jackson)】、Java中的bean

JSON 概念 JSON:JavaScript Object Notation是一种表示对象的方式 基于JavaScript语言的轻量级的数据交换格式;(即:用来传输数据的一种格式) 现在传输数据的方式更多是采用json的格式,渐渐代替了XML JSON的数据表示 JSON采用名值…

封装一个细粒度的限流器

文章目录 原因限流对象限流后的做法怎么确定限流阈值观测业务性能数据压测借鉴链路上的其他服务手动计算 四种静态限流算法令牌桶漏桶固定窗口与滑动窗口 手写限流算法令牌桶漏桶固定窗口滑动窗口 分布式限流的具体实现 原因 尽管云原生网关里有统一入口的限流(根据…

股指期货套期保值中的展期管理有哪些?

在复杂的金融市场环境中,展期作为一种重要的风险管理工具,被广泛应用于期货交易中,特别是当投资者需要对长期资产进行套期保值时。展期的核心思想在于,通过连续替换高流动性的近月期货合约来替代流动性较差的远月合约,…

untiy有渲染线程和逻辑线程嘛

之前我也这么认为,其实unity引擎是单线程的,当然后续的jobs不在考虑范围内 如果你在一个awake 或者 start方法中 延时,是会卡住主线程的 比如 其实游戏引擎有一个基础简单理解,那就是不断的进行一个循环,在这个周期循…

网络分段如何增强 OT 网络的可见性

防火墙在保护运营技术(OT) 网络和系统方面发挥什么作用? 很多人会说,防火墙是一种防御机制,用于保护该环境免受 IT 和外界的影响。对于负责该关键系统正常运行的操作员来说,防火墙是阻止他人进入的外围保护。它也是需要从 OT 系统…

Unity Render Streaming项目实践经验

UnityRenderStreaming项目 项目github地址见上,我使用项目的3.1.0-exp.7版本、Unity 2023.1.0版本、windows11运行。 1下载项目包 2在Unity Hub中打开RenderStreaming~文件夹 3在package manager中导入com.unity.renderstreaming package 因为已经下载过了就选择install pa…

Linux shell编程学习笔记71: sort 命令——构造有序世界

0 前言 在大数据时代,我们面对和使用大量数据,如果数据是有序的,无疑是有益的。 在Linux中,我们可以使用 sort 命令来构造一个有序的空间。 1 sort命令 的功能、格式和选项说明 我们可以使用命令sort --help来获取帮助信息。 …

Spark MLlib 特征工程(下)

Spark MLlib 特征工程(下) 前面我们提到,典型的特征工程包含如下几个环节,即预处理、特征选择、归一化、离散化、Embedding 和向量计算,如下图所示。 在上一讲,我们着重讲解了其中的前 3 个环节,也就是预处理、特征选…

Memecoin的火爆与AMM在Solana上的主导地位

随着Solana区块链的高速发展,尤其是近年来Memecoin市场的崛起,AMM(自动做市商)逐渐成为Solana去中心化交易所(DEX)的主导交易模式。尽管Solana以其高效性能能够支持每秒数千笔交易,足以让中心化…

【C语言篇】深入理解指针3(附转移表源码)

文章目录 数组指针什么是数组指针数组指针变量的初始化 二维数组传参的本质函数指针函数指针变量的创建函数指针变量的使用 两端有趣的代码typedef 关键字 函数指针数组转移表写在最后 数组指针 什么是数组指针 在【C语言篇】深入理解指针2我们学习了指针数组,指针…

Qt项目【上位机十字屏开发】

效果图 说明 重写 QWidget 中的 paintEvent() 处理绘图事件&#xff0c;废话不多说&#xff0c;上代码 源码 #ifndef MYWIDGETFORM_H #define MYWIDGETFORM_H#include <QWidget>namespace Ui { class myWidgetForm; }enum MYTYPE{SIZEWIDTH,SIZEHEIGHT,TOPWIDTH,TOPHE…