长列表加载性能优化

一、长列表优化概述

        列表是应用开发中最常见的一类开发场景,它可以将杂乱的信息整理成有规律、易于理解和操作的形式,便于用户查找和获取所需要的信息。应用程序中常见的列表场景有新闻列表、购物车列表、各类排行榜等。随着信息数据的累积,特别是一些新闻应用、购物应用、聊天应用,列表数据往往会达到上万条,针对这类大量数据加载的长列表应用,如何对长列表的性能进行优化是非常重要的。一个正确、高性能的长列表应用能明显降低列表渲染时间、提升页面的滑动帧率、降低应用内存占用,大幅提升用户体验。

二、优化手段

        1、懒加载

        提供列表数据按需加载能力,解决一次性加载长列表数据耗时长、占用过多资源的问题,可以提升页面响应速度。 

        (1)ForEach

        ForEach循环渲染的过程如下:

        a、从列表数据源一次性加载全量数据

        b、为列表数据的每一个元素都创建对应的组件,并全部挂载在组件树上。即,ForEach遍历多少个列表元素,就创建多少个ListItem组件节点并依次挂载在List组件树根节点上

        c、列表内容显示时,只渲染屏幕可视区内的ListItem组件,可视区外的ListItem组件滑动进入屏幕内时,因为已经完成了数据加载和组件创建挂载,直接渲染即可

        

        如果列表数据较少,数据一次性全量加载不是性能瓶颈时,可以直接使用ForEach;但是当数据量大、组件结构复杂的情况下ForEach会出现性能瓶颈。这是因为要一次性加载所有的列表数据,创建所有组件节点并完成组件树的构建,在数据量大时会非常耗时,从而导致页面启动时间过长。另外,屏幕可视区外的组件虽然不会显示在屏幕上,但是仍然会占用内存。在系统处于高负载的情况下,更容易出现性能问题,极限情况下甚至会导致应用异常退出。

        (2)LazyForEach 

        LazyForEach懒加载的原理和渲染过程如下:

        a、LazyForEach会根据屏幕可视区能够容纳显示的组件数量按需加载数据

        b、根据加载的数据量创建组件,挂载在组件树上,构建出一棵短小的组件树。即,屏幕可以展示多少列表项组件,就按需创建多少个ListItem组件节点挂载在List组件树根节点上

        c、 屏幕可视区只展示部分组件。当可视区外的组件需要在屏幕内显示时,需要从头完成数据加载、组件创建、挂载组件树这一过程,直至渲染到屏幕上

        

        LazyForEach实现了按需加载,针对列表数据量大、列表组件复杂的场景,减少了页面首次启动时一次性加载数据的时间消耗,减少了内存峰值。不过在长列表滑动的过程中,因为需要根据用户的滑动行为不断地加载新的内容,这需要进行额外的数据请求和处理,会增加滑动时的计算量,从而对性能产生一定的影响。然而,合理使用LazyForEach的按需加载能力,通过在滑动停止或达到某个阈值时才进行加载,可以减少不必要的计算和请求,从而提高性能,给用户带来更好的体验。总之,在实现按需加载的场景中,需要综合考虑性能和用户体验的平衡,合理地优化加载逻辑和渲染方式,以提升整体的性能表现。

        2、缓存列表项

        提供屏幕可视区域外列表项长度的自定义调节能力,配合懒加载设置可缓存列表项参数,通过预加载数据提升列表滑动体验。

        LazyForEach懒加载可以通过设置cachedCount来指定缓存数量,在设置cachedCount后,除屏幕内显示的ListItem组件外,还会预先将屏幕可视区外指定数量的列表项数据缓存。这样当一个屏幕数据加载完成后,再次向下滑动时,会先加载上一次请求的数据,加载完成后,再加载本次请求的数据。LazyForEach添加了cachedCount缓存列表项后,其渲染过程如下:

        a、首先,请求n+cachedCount条数据,并在屏幕上显示n条数据

        b、当列表滑动,缓存列表项需要从屏幕可视区外进入可视区内时,此时只用渲染组件即可,相比不设置cachedCount提升了显示效率

        c、当列表不断滑动,屏幕可视区外缓存的列表项数量少于cachedCount设置数量时,会触发列表项数据加载事件,继续预加载下一组缓存列表项(cachedCount个)

        d、当上滑下滑间隔进行时,列表两个方向分别缓存cachedCount条数据

        e、如果不显式设置cachedCount,cachedCount默认为1

        

        缓存列表项适合加载列表项数据请求比较耗时的场景。比如,滑动列表中含有短视频、高清图片等数据量比较大的资源,可以通过预先从网络加载并缓存相关数据,缩短渲染前的准备时间,提升列表响应速度。

        使用限制为:缓存列表项仅在使用LazyForEach懒加载时有效,ForEach循环渲染会一次性加载全量数据,故无法也不需要设置缓存列表项。 

        3、动态预加载

        根据历史任务加载耗时情况,动态调整屏幕可视区域外数据预取数量,配合懒加载设置,可在列表不断滑动时,屏幕可视区外实时更新列表数据,通过预取和预渲染数据提升列表滑动体验

        HarmonyOS提供了内容预取的能力Prefetcher,支持应用动态自适应网络状态,通过提前下载一些图片或资源,确保相关资源在需要时能立即显示,以尽可能减少白块出现的概率。

        LazyForEach懒加载可以通过使用Prefetcher来预取和预渲染数据。在使用Prefetcher后,除屏幕内显示的ListItem组件外,还会预先将屏幕可视区外的部分列表项数据进行预渲染和预取。这样当列表向下滑动时,会先显示预渲染组件,屏幕可视区外会动态调整预取范围。预取逻辑在Prefetcher的BasicPrefetcher类中实现,BasicPrefetcher支持预取和预渲染(图像解码、添加到组件树等)过程分离、自适应调整预获取范围、优先加载可视区域、以及取消不必要任务(快速滚动列表的场景下,智能取消不必要任务),其渲染过程如下:

        a、首先,请求n条数据,并在屏幕上显示m条数据

        b、当列表滑动,缓存列表项需要从屏幕可视区外进入可视区内时,此时显示预渲染组件,屏幕可视区外会动态调整预取范围,相比仅设置cachedCount提升了显示效率

        c、当列表不断滑动,屏幕可视区外实时更新列表项、更新预取数据和预渲染数据

        

        动态预加载适合加载列表项数据请求比较耗时的场景。

        4、组件复用

        提供可复用组件对象的缓存资源池,通过重复使用已经创建过并缓存的组件对象,降低相同组件短时间内频繁创建和销毁的开销,提升组件渲染效率。

        HarmonyOS应用框架提供了组件复用能力,可复用组件从组件树上移除时,会进入到一个回收缓存区。后续创建新组件节点时,会复用缓存区中的节点,节约组件重新创建的时间。尤其在列表等场景下,其自定义子组件具有相同的组件布局结构,列表更新时仅有状态变量等数据差异。通过组件复用可以提高列表页面的加载速度和响应速度。

        组件复用机制如下:

        a、标记为@Reusable的组件从组件树上被移除时,组件和其对应的JSView对象都会被放入复用缓存中

        b、当列表滑动新的ListItem将要被显示,List组件树上需要新建节点时,将会从复用缓存中查找可复用的组件节点

        c、找到可复用节点并对其进行更新后添加到组件树中。从而节省了组件节点和JSView对象的创建时间

        

        组件复用生效的条件是:

        (1)自定义组件被@Reusable装饰器修饰,即表示其具备组件复用的能力

        (2)在一个自定义父组件下创建出来的具备组件复用能力的自定义子组件,在可复用自定义组件从组件树上移除之后,会被加入到其自定义父组件的可复用节点缓存中

        (3)在一个自定义父组件下创建可复用的子组件时,若其父自定义组件的可复用节点缓存中有对应类型的可复用子组件,会通过更新可复用子组件的方式,快速创建可复用子组件

        (4) ForEach循环渲染会一次性加载全量数据,因此不支持组件复用

        若业务实现中存在以下场景,并成为UI线程的帧率瓶颈,推荐使用组件复用:

        (1)列表滚动:当应用需要展示大量数据的列表,并且用户进行滚动操作时,频繁创建和销毁列表项的视图可能导致卡顿和性能问题。在这种情况下,使用列表组件的组件复用机制可以重用已经创建的列表项视图,提高滚动的流畅度

        (2)动态布局更新:如果应用中的界面需要频繁地进行布局更新,例如根据用户的操作或数据变化动态改变视图结构和样式,重复创建和销毁视图可能导致频繁的布局计算,影响帧率。在这种情况下,使用组件复用可以避免不必要的视图创建和布局计算,提高性能

        (3)地图渲染:在地图渲染这种场景下,频繁创建和销毁数据项的视图可能导致性能问题。使用组件复用可以重用已创建的视图,只更新数据的内容,减少视图的创建和销毁,能有效提高性能

        5、布局优化

        使用扁平化布局方案,减少视图嵌套层级和组件数,避免过度绘制,提升页面渲染效率。

        列表不同于其他布局,包含了大量重复循环的ListItem,所以对每一个ListItem的布局优化格外重要。错误的布局方式可能会导致组件树和嵌套层数过多,在创建和布局绘制阶段产生较大的性能开销,导致界面卡顿。合理使用布局,减少嵌套层数,能提高布局效率。

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

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

DMA的原理

一、介绍 DMA(Direct Memory Access)是一种允许设备直接与内存进行数据交换的技术,无需‌CPU干预。DMA的主要功能是提供在‌外设和存储器之间或者存储器和存储器之间的高速数据传输。比如使用ADC进行数据采集,可以直接将数据存入…

干货 | 2024制造业数字化现状调查白皮书(免费下载)

导读:在这本白皮书中,我们询问了制造商有关数字化转型的工作情况、2024 年的优先事项和可持续性。研究结果清楚地表明,在数字化方面处于领先地位的制造商转型项目比那些没有规划或刚刚起步的项目实现的价值要大得多。 加入知识星球或关注下方…

运维转型大模型:全面指南与实战总结

运维心里苦谁做谁知道,有时候感觉自己像一个杂工,在公司都快变成一个修电脑的了,不装了我转行了,给大家分享一点经验,希望能帮助到你们。 运维工程师转行至大模型领域需要学习一系列新的技能和知识。以下是一个详细的…

【算法】JAVA刷算法必备数据结构

文章目录 数组List队列和栈栈的应用:表达式求值 数组List ArrayList 类是一个可以动态修改的数组,与普通数组的区别就是它是没有固定大小的限制,我们可以添加或删除元素。 ArrayList 继承了 AbstractList ,并实现了 List 接口。 …

Nest.js实现一个简单的聊天室

本文将介绍如何使用 Nest.js 和 Uni-app 实现一个简单的实时聊天应用。后端使用 nestjs/websockets 和 socket.io,前端使用 uni-app 并集成 socket.io-client。这个项目允许多个用户同时加入聊天并实时交换消息。 效果图: 一、准备工作 安装 Node.js 和…

DAF-Net:一种基于域自适应的双分支特征分解融合网络用于红外和可见光图像融合

论文 DAF-Net: A Dual-Branch Feature Decomposition Fusion Network with Domain Adaptive for Infrared and Visible Image Fusion 提出了一种新的红外和可见光图像融合方法。该方法旨在结合红外图像和可见光图像的互补信息,以提供更全面的场景理解。红外图像在低…

学习C++的第七天!

1.虚函数是在基类中用 virtual 关键字声明的函数,可以在派生类中被重写。纯虚函数是在虚函数的基础上,在基类中被初始化为 0 的函数,含有纯虚函数的类是抽象类,不能被实例化。 2.如果基类的析构函数不是虚函数,当通过…

现代cpp多线程与并发初探

个人博客:Sekyoro的博客小屋 个人网站:Proanimer的个人网站 在现代c(c20)中,有了jthread和协程的概念,使得我们编写并发程序更加方便. 这里作简单学习. 前言知识 多线程编程 std::thread 用于创建一个执行的线程实例,所以它是一切并发编程的基础,使用时需要包含 <thread…

Android个性名片界面的设计——约束布局的应用

节选自《Android应用开发项目式教程》&#xff0c;机械工业出版社&#xff0c;2024年7月出版 做最简单的安卓入门教程&#xff0c;手把手视频、代码、答疑全配齐 【任务目标】 使用约束布局、TextView控件实现一个个性名片界面的设计&#xff0c;界面如图1所示。 图1 个性名片…

Transformer 算法模型详解

核心点&#xff1a;完整讲解Transformer模型&#xff01; 让我们用简单的语言来解释&#xff1a;想象一下&#xff0c;你正在阅读一本书&#xff0c;书中的每个字都很重要。但如果你每次只能关注一个字&#xff0c;理解整本书就会变得很慢。而Transformer模型就像是赋予你超能…

从密码学看盲拍合约:智能合约的隐私与安全新革命!

文章目录 前言一、什么是盲拍合约&#xff1f;二、盲拍合约的优势1.时间压力的缓解2.绑定与秘密的挑战 三、盲拍合约的工作原理1.提交盲出价2.披露出价3.结束拍卖4.退款机制 四、代码示例总结 前言 随着区块链技术的发展&#xff0c;智能合约在各种场景中的应用越来越广泛。盲…

基于Hive和Hadoop的病例分析系统

本项目是一个基于大数据技术的医疗病历分析系统&#xff0c;旨在为用户提供全面的病历信息和深入的医疗数据分析。系统采用 Hadoop 平台进行大规模数据存储和处理&#xff0c;利用 MapReduce 进行数据分析和处理&#xff0c;通过 Sqoop 实现数据的导入导出&#xff0c;以 Spark…

Linux入门2——初识Linux权限

目录 0. Linux下的用户 1.文件访问者的分类 2.文件类型和访问权限 3. 文件权限值的表示方法 4.文件访问权限的相关设置方法 4.1 修改文件的访问权限 4.2修改文件的拥有者和所属组 0. Linux下的用户 在学习Linux权限之前&#xff0c;我们要先来了解Linux下的用户&#x…

vue+UEditor附件上传问题

&#x1f3c6;本文收录于《全栈Bug调优(实战版)》专栏&#xff0c;主要记录项目实战过程中所遇到的Bug或因后果及提供真实有效的解决方案&#xff0c;希望能够助你一臂之力&#xff0c;帮你早日登顶实现财富自由&#x1f680;&#xff1b;同时&#xff0c;欢迎大家关注&&am…

端口隔离配置的实验

端口隔离配置是一种网络安全技术&#xff0c;用于在网络设备中实现不同端口之间的流量隔离和控制。以下是对端口隔离配置的详细解析&#xff1a; 基本概念&#xff1a;端口隔离技术允许用户将不同的端口加入到隔离组中&#xff0c;从而实现这些端口之间的二层数据隔离。这种技…

算法记录——链表

2.链表 2.1判断是否是回文链表 1.方法一&#xff1a;利用栈反转链表 /*** Definition for singly-linked list.* public class ListNode {* int val;* ListNode next;* ListNode() {}* ListNode(int val) { this.val val; }* ListNode(int val, ListNode…

Invalid Executable The executable contains bitcode

Invalid Executable The executable contains bitcode 升级xcode16后&#xff0c;打包上传testflight时三方库报错&#xff1a;Invalid Executable - The executable ***.app/Frameworks/xxx.framework/xxx contains bitcode. 解决方案&#xff1a; 执行一下指令删除该framew…

软件测试学习路线图

软件测试工程师是专门从事软件、系统或产品测试和评估的技术专业人士&#xff0c;确保它们符合既定标准并无任何缺陷。通过精心设计和执行测试计划&#xff0c;软件测试工程师发现 Bug、故障和需要改进的领域&#xff0c;从而提高最终产品的可靠性和性能。 软件测试工程师在软…

Awcing 799. 最长连续不重复子序列

Awcing 799. 最长连续不重复子序列 解题思路: 让我们找到一个数组中&#xff0c;最长的 不包含重复的数 的连续区间的长度。 最优解是双指针算法&#xff1a; 我们用 c n t [ i ] cnt[i] cnt[i]记录 i i i 这个整数在区间内出现的次数。(因为每个数的大小为 1 0 5 10^5 105, …

状态模式原理剖析

《状态模式原理剖析》 状态模式&#xff08;State Pattern&#xff09; 是一种行为设计模式&#xff0c;它允许对象在其内部状态改变时改变其行为。换句话说&#xff0c;当对象状态发生变化时&#xff0c;它的行为也会随之变化。 通过状态模式&#xff0c;可以消除通过 if-else…