【Android】画面卡顿优化列表流畅度一
卡顿渲染耗时如图:

卡顿表现有如下几个方面:
- 网络图片渲染耗时大
- 上下滑动反应慢,甚至画面不动
- 新增一页数据加载渲染时耗时比较大,上下滑动几乎没有反应,画面停止没有交互响应
背景
实际上这套数据加载逻辑已经运行了快一年多了,之前也没有这些问题的。笔者是后面接手的,也没觉得有问题。也许是最初数据量小当时看不出来
运行到今天设计业务数据量是3650条,实际业务数据条数是1100条左右;这个业务数据量原本也不是特别大。所以也没觉得有问题。
直到其他业务组的数据接入后数据量起来了这个列表数据就卡顿的几乎不能用了,而不凑巧被领导知道了,于是就有了本次优化
原本的设计如下:
列表布局:
<android.support.v4.widget.NestedScrollViewandroid:id="@+id/nestedScrollView"android:layout_width="match_parent"android:layout_height="match_parent"android:fillViewport="true"app:layout_behavior="@string/appbar_scrolling_view_behavior"><live.bingoogolapple.refreshlayout.BGARefreshLayoutandroid:id="@+id/mRefreshLayout"android:layout_width="match_parent"android:layout_height="match_parent"app:layout_behavior="@string/appbar_scrolling_view_behavior"><android.support.v7.widget.RecyclerViewandroid:id="@+id/recycleView_playback"android:layout_width="match_parent"android:layout_height="wrap_content"android:background="#FFFFFFFF"android:nestedScrollingEnabled="false"android:paddingLeft="20dp"android:paddingRight="10dp"android:paddingBottom="8dp"app:layout_behavior="@string/appbar_scrolling_view_behavior" /></live.bingoogolapple.refreshlayout.BGARefreshLayout></android.support.v4.widget.NestedScrollView>
RecyclerView.Adapter数据加载和下面类似:
RecyclerView.Adapter 是用于在 RecyclerView 中展示数据的关键组件之一。下面是使用 RecyclerView.Adapter 的基本步骤:
- 创建自定义的 Adapter 类:首先需要创建一个继承自 RecyclerView.Adapter 的自定义 Adapter 类,该类负责管理数据集合并将数据绑定到 RecyclerView 的 ViewHolder 上。
public class MyAdapter extends RecyclerView.Adapter<MyAdapter.MyViewHolder> {// 适配器的代码
}
- 创建 ViewHolder 类:在自定义的 Adapter 类中,需要创建一个继承自 RecyclerView.ViewHolder 的内部类,用于表示 RecyclerView 中的每个子项的视图。
public class MyAdapter extends RecyclerView.Adapter<MyAdapter.MyViewHolder> {public static class MyViewHolder extends RecyclerView.ViewHolder {// ViewHolder 的代码}
}
- 实现 Adapter 的方法:在自定义的 Adapter 类中,需要实现一些方法,例如 onCreateViewHolder、onBindViewHolder、getItemCount 等。这些方法用于创建 ViewHolder、绑定数据到 ViewHolder、获取数据集合的大小等操作。
public class MyAdapter extends RecyclerView.Adapter<MyAdapter.MyViewHolder> {@Overridepublic MyViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {// 创建 ViewHolder}@Overridepublic void onBindViewHolder(MyViewHolder holder, int position) {// 绑定数据到 ViewHolder}@Overridepublic int getItemCount() {// 获取数据集合的大小}
}
- 在 onCreateViewHolder 方法中创建 ViewHolder:在 onCreateViewHolder 方法中,需要创建并返回一个 ViewHolder 对象,用于表示 RecyclerView 中的每个子项的视图。
public class MyAdapter extends RecyclerView.Adapter<MyAdapter.MyViewHolder> {@Overridepublic MyViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {// 创建 ViewHolderView itemView = LayoutInflater.from(parent.getContext()).inflate(R.layout.item_layout, parent, false);return new MyViewHolder(itemView);}
}
- 在 onBindViewHolder 方法中绑定数据到 ViewHolder:在 onBindViewHolder 方法中,需要将数据绑定到 ViewHolder 上,以便在 RecyclerView 中显示。
public class MyAdapter extends RecyclerView.Adapter<MyAdapter.MyViewHolder> {@Overridepublic void onBindViewHolder(MyViewHolder holder, int position) {// 绑定数据到 ViewHolderMyData data = dataList.get(position);holder.bindData(data);}
}
- 将 Adapter 与 RecyclerView 关联:最后,需要将自定义的 Adapter 与 RecyclerView 关联起来,以便在 RecyclerView 中展示数据。
RecyclerView recyclerView = findViewById(R.id.recycler_view);
MyAdapter adapter = new MyAdapter(dataList);
recyclerView.setAdapter(adapter);
在上面的代码中,dataList 是数据集合,R.layout.item_layout 是每个子项的布局文件。通过以上步骤,就可以使用 RecyclerView.Adapter 在 RecyclerView 中展示数据了。
Glide图片加载库使用
Glide.with(context).load(imgUrl).into(holder.imageview);
BGARefreshLayout上拉加载更多组件使用方式叠加NestedScrollView
BGARefreshViewHolder bgaNormalRefreshViewHolder = new BGANormalRefreshViewHolder(this, true);mRefreshLayout.setRefreshViewHolder(bgaNormalRefreshViewHolder);nestedScrollView = findViewById(R.id.nestedScrollView);if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {nestedScrollView.setOnScrollChangeListener(new View.OnScrollChangeListener() {@Overridepublic void onScrollChange(View view, int scrollX, int scrollY, int oldScrollX, int oldScrollY) {NestedScrollView toNesstedScrollView = (NestedScrollView) view;if (scrollY < 5|| toNesstedScrollView.getChildAt(0).getMeasuredHeight()== view.getMeasuredHeight()) {return;}int height = toNesstedScrollView.getChildAt(0).getMeasuredHeight()- view.getMeasuredHeight();if (scrollY == height) {// 为BGARefreshLayout 设置代理if(mRefreshLayout.getDelegate()==null){mRefreshLayout.setDelegate(XXXXActivity.this);}mRefreshLayout.beginLoadingMore();}}});}
内存上到没有太多可书写的,基本没有涉及内存方面的问题
设定优化方向和目标
画面卡顿的由来
Android 屏幕渲染 16ms 柱形图是指在 Android 应用程序中,屏幕渲染所花费的时间以柱形图的形式进行可视化展示。这个柱形图通常用于监测应用程序的性能,特别是在绘制 UI 时所花费的时间。
在 Android 中,每秒钟需要渲染 60 帧的屏幕,即每帧的时间限制为 16 毫秒(1 秒 = 1000 毫秒 / 60 帧 ≈ 16.67 毫秒)。如果屏幕渲染超过 16 毫秒,就会导致丢帧(屏幕卡顿)。
柱形图中的每个柱子代表一帧的渲染时间。如果柱子的高度超过 16 毫秒,就表示该帧的渲染时间超过了理想的 16 毫秒限制。柱形图越高,表示渲染时间越长,应用程序的性能可能会受到影响。
通过观察柱形图,开发者可以判断应用程序在绘制 UI 时是否存在性能问题,并进行相应的优化。优化的目标是尽量保持每帧的渲染时间在 16 毫秒以内,以确保流畅的用户体验。
- 蓝色部分表示绘制时间或者在Java层创建和更新display list的时间。在一个View 实际被渲染前,它需要先转换为GPU能识别的格式。简单来说可能就是几个绘制命令,复杂一点,我们可能在嵌入了一条从canvas获取的自定义路径。这一步完成之后,输出结果就会被系统作为display list缓存起来。蓝色部分记录了这一帧对所有需要更新的view完成这两步花费的时间。当它很高的时候,说明有很多view突然无效(invalidate)了,或者是有几个自定义view在onDraw函数中做了特别复杂的绘制逻辑
优化方向一:清理布局xml中并没有用到或者被设置无效(invalidate)的view组件,减少布局嵌套层级 - 红色部分代表执行时间,也就是Android 2D渲染引擎(OpenGL)执行display list的时间。为了将变化绘制在屏幕上,Android需要使用OpenGL ES API来绘制这些display list信息,OpenGL最终将数据传给了GPU,然后GPU渲染到屏幕上。View越复杂,OpenGL绘制所需要的命令也越复杂。如果红色这一段比较高,复杂的view都可能是罪魁祸首。还有值得注意的是比较大的峰值,这说明有些view重复提交了,也就是绘制了多次,而它们可能并不需要重绘
优化方向二:采用局部更新view策略 - 橙色部分代表处理时间,更进一步,就是CPU告诉GPU渲染已经完成的时间。这部分是阻塞的,CPU会等待GPU知道确认收到了命令,如果这里比较高,说明GPU做的任务太多了,通常是由于很多复杂的view绘制从而需要过多的OpenGL渲染命令去处理
看下面这张渲染耗时图柱,都破了屏幕了,底部那条红线就没放眼里蛤!

开启渲染耗时图柱开关
一般都在开发者选项-GPU呈现模式分析:

设定优化目标为京东首页在同一机型上的交互表现
京东首页交互流畅度如下:

分以下阶段进行解析当前问题的症结点
- 当前的设计是否满足交互流畅度。流畅度是否能达到京东首页的体感?设计数据只加载文字;加载文字后再加载静态图片;测试验证当前布局、数据逻辑加载的交互体感情况。如不满足那基本就能确定是布局和数据加载逻辑有缺陷,则需要进行重构或重写
- 在第1条满足的基础上,测试同一个网络图片加载到所以列表子项里展示。记录交互体感和京东首页进行对比分析
- 测试单列动态图片加载效果并记录
- 再次测试双列动态图片加载效果并记录
=================================请看下一章
【Android】画面卡顿症结点分析二
smartApi接口开发工具推荐
历时一年半多开发终于smartApi-v1.0.0版本在2023-09-15晚十点正式上线
smartApi是一款对标国外的postman的api调试开发工具,由于开发人力就作者一个所以人力有限,因此v1.0.0版本功能进行精简,大功能项有:
- api参数填写
- api请求响应数据展示
- PDF形式的分享文档
- Mock本地化解决方案
- api列表数据本地化处理
- 再加上UI方面的打磨
下面是一段smartApi使用介绍:

下载地址:
https://pan.baidu.com/s/1kFAGbsFIk3dDR64NwM5y2A?pwd=csdn
相关文章:
【Android】画面卡顿优化列表流畅度一
卡顿渲染耗时如图: 卡顿表现有如下几个方面: 网络图片渲染耗时大上下滑动反应慢,甚至画面不动新增一页数据加载渲染时耗时比较大,上下滑动几乎没有反应,画面停止没有交互响应 背景 实际上这套数据加载逻辑已经运行…...
SNP应邀参加2023中国企业数字化转型峰会暨赛意用户大会
创新驱动科技,数智驱动未来。如今,我国产业数字化进程提速升级,数字产业化规模持续壮大。数据显示,2022年,我国数字经济规模达50.2万亿元,总量稳居世界第二。数字经济已经成为推动传统产业转型升级、促进高…...
黑豹程序员-架构师学习路线图-百科:Knife4j API接口文档管理
文章目录 由来:接口文档第一代:Swagger第二代:Knife4j界面 由来:接口文档 古老编程是一个语言前后端通吃,ASP、JSP、PHP都是如此。 但随着项目规模变大,项目团队也开始壮大,岗位职责开始细分&a…...
PHP安全问题:远程溢出、DoS、safe_mode绕过漏洞
一、Web 服务器安全 PHP 其实不过是 Web服务器的一个模块功能,所以首先要保证 Web服务器的安全。当 然 Web服务器要安全又必须是先保证系统安全,这样就扯远了, 无穷无尽。PHP可以和各种 Web 服务器结合,这里也只讨论 Apache。非常建议以 chroot 方式安装启动 Apache,这样…...
2023云计算发展
未来的云计算世界将是一个充满着无限可能和创意的世界。在这个世界中,人们可以通过云计算技术实现更加便捷、安全、高效的日常生活,同时可以探索更加广阔、神奇的世界。 智能家居将成为云计算的新宠。未来,人们可以通过语音、手势、传感器等…...
javaSE学习笔记(六)泛型,异常
五、泛型 泛型的由来:通过Object转型问题引入--为了解决安全隐患 早期的Object类型可以接收任意的对象类型,但是在实际的使用中,会有类型转换异常的问题 泛型作用 提高安全性(将运行期的错误转换到编译期) 省去强转的麻烦,取…...
C/C++轻量级并发TCP服务器框架Zinx-游戏服务器开发006:基于redis查找玩家姓名+游戏业务实现总结
文章目录 1 Redis的安装与API的使用1.1 安装目录及环境变量1.2 设置远程客户端连接和守护进程1.3 启动redis1.4 Hiredis API的使用1.5 我的动态库和头文件 2 Redis的使用2.1 初始化时候2.2 结束的时候 3 测试4 Makefile5 游戏业务总结 1 Redis的安装与API的使用 1.1 安装目录及…...
数字政府!3DCAT实时云渲染助推上海湾区数字孪生平台
数字孪生,是一种利用物理模型、传感器数据、运行历史等信息,在虚拟空间中构建实体对象或系统的精确映射,从而实现对其全生命周期的仿真、优化和管理的技术。数字孪生可以应用于各个领域,如工业制造、智慧城市、医疗健康、教育培训…...
react之Component存在的2个问题
问题 只要执行setState(),即使不改变状态数据,组件也会重新render()只当前组件重新render(),就会自动重新render子组件 原因 Component中的shouldComponentUpdate()总是返回true 思路 只有当组件的state或props数据发生改变时才重新rend…...
【论文阅读】Generating Radiology Reports via Memory-driven Transformer (EMNLP 2020)
资料链接 论文原文:https://arxiv.org/pdf/2010.16056v2.pdf 代码链接(含数据集):https://github.com/cuhksz-nlp/R2Gen/ 背景与动机 这篇文章的标题是“Generating Radiology Reports via Memory-driven Transformer”…...
IP协议相关技术
文章目录 IP协议相关技术仅凭IP无法完成通信DNSARP IP协议相关技术 仅凭IP无法完成通信 人们在上网的时候其实很少直接输入某个具体的IP地址。 在访问Web站点和发送、接收电子邮件时,我们通常会直接输入Web网站的地址或电子邮件地址等那些由应用层提供的地址&…...
Visual Studio2022安装教程【图文详解】(大一小白)编译软件
工欲善其事,必先利其器。想要学好编程,首先要把手中的工具利用好,今天小编教一下大家如何下载安装并使用史上最强大的编译器--Visual Studio🍗 一.Visual Studio下载及安装 https://visualstudio.microsoft.com/ 打开文件 点击.ex…...
matlab 点云最小二乘拟合平面(PCA法)
目录 一、算法原理二、代码实现三、结果展示本文由CSDN点云侠原创,原文链接。爬虫网站自重。 一、算法原理 见:matlab 点云最小二乘拟合平面(PCA法详细过程版)。 二、代码实现 clc;clear; %% --------...
socks5代理和https代理有什么不同?各自有哪些优点?
socks5代理和https代理是两种不同的代理服务,它们在实现方式、安全性和协议特点等方面存在差异。下面我们来详细了解一下这两种代理的优点。 一、socks5代理的优点 1. 速度快 socks5代理采用了TCP协议,能够有效地减少网络延迟和数据传输速度慢的问题&…...
springboot,spring框架返回204 status code的时候,会吞掉返回值
背景 发现有个有意思的现象,就是当你的接口返回204的 HTTP status code 的时候,会自动把 response body 吃掉,即使代码里是有返回的。例如 (其实204本身就是NO_CONTENT的意思,不过我是真没想到真干掉了返回࿰…...
6-爬虫-scrapy解析数据(使用css选择器解析数据、xpath 解析数据)、 配置文件
1 scrapy解析数据 1.1 使用css选择器解析数据 1.2 xpath 解析数据 2 配置文件 3 整站爬取博客–》爬取详情–》数据传递 scrapy 爬虫框架补充 # 1 打码平台---》破解验证码-数字字母:ddddocr-计算题,滑块,成语。。。-云打码,超…...
idea 一直卡在maven正在解析maven依赖
修改maven Importing的jvm参数 -Xms1024m -Xmx2048m...
警告:未配置spring boot 配置注解处理器
前言 这是我在这个网站整理的笔记,有错误的地方请指出,关注我,接下来还会持续更新。 作者:神的孩子都在歌唱 问题 我再使用ConfigurationProperties(prefix “redisson”)去加载配置文件中的属性的时候,发现idea有个警告 并且配…...
详解虚拟DOM的原理
Virtual DOM(虚拟DOM)是一种编程概念,它是对真实DOM的轻量级抽象表示。在前端开发中,直接操作真实DOM是昂贵的,尤其是当涉及到大量的DOM更新时。Virtual DOM的出现,为优化和提高Web应用的性能提供了一个有效…...
开设自己的网站系类03安装数据库(centos版)
编者买了一个服务器打算自己构建一个网站,用于记录生活。网站大概算是一个个人博客吧。记录创建过程的一些步骤。 前面已经讲过配置服务器的程序运行环境 网站运行还需要数据库,本篇文章则是安装数据库的内容。 卸载mariadb 查看是否有安装 mariadb&…...
深入浅出Asp.Net Core MVC应用开发系列-AspNetCore中的日志记录
ASP.NET Core 是一个跨平台的开源框架,用于在 Windows、macOS 或 Linux 上生成基于云的新式 Web 应用。 ASP.NET Core 中的日志记录 .NET 通过 ILogger API 支持高性能结构化日志记录,以帮助监视应用程序行为和诊断问题。 可以通过配置不同的记录提供程…...
XCTF-web-easyupload
试了试php,php7,pht,phtml等,都没有用 尝试.user.ini 抓包修改将.user.ini修改为jpg图片 在上传一个123.jpg 用蚁剑连接,得到flag...
《Qt C++ 与 OpenCV:解锁视频播放程序设计的奥秘》
引言:探索视频播放程序设计之旅 在当今数字化时代,多媒体应用已渗透到我们生活的方方面面,从日常的视频娱乐到专业的视频监控、视频会议系统,视频播放程序作为多媒体应用的核心组成部分,扮演着至关重要的角色。无论是在个人电脑、移动设备还是智能电视等平台上,用户都期望…...
Java 8 Stream API 入门到实践详解
一、告别 for 循环! 传统痛点: Java 8 之前,集合操作离不开冗长的 for 循环和匿名类。例如,过滤列表中的偶数: List<Integer> list Arrays.asList(1, 2, 3, 4, 5); List<Integer> evens new ArrayList…...
CMake基础:构建流程详解
目录 1.CMake构建过程的基本流程 2.CMake构建的具体步骤 2.1.创建构建目录 2.2.使用 CMake 生成构建文件 2.3.编译和构建 2.4.清理构建文件 2.5.重新配置和构建 3.跨平台构建示例 4.工具链与交叉编译 5.CMake构建后的项目结构解析 5.1.CMake构建后的目录结构 5.2.构…...
Neo4j 集群管理:原理、技术与最佳实践深度解析
Neo4j 的集群技术是其企业级高可用性、可扩展性和容错能力的核心。通过深入分析官方文档,本文将系统阐述其集群管理的核心原理、关键技术、实用技巧和行业最佳实践。 Neo4j 的 Causal Clustering 架构提供了一个强大而灵活的基石,用于构建高可用、可扩展且一致的图数据库服务…...
在Ubuntu24上采用Wine打开SourceInsight
1. 安装wine sudo apt install wine 2. 安装32位库支持,SourceInsight是32位程序 sudo dpkg --add-architecture i386 sudo apt update sudo apt install wine32:i386 3. 验证安装 wine --version 4. 安装必要的字体和库(解决显示问题) sudo apt install fonts-wqy…...
mac 安装homebrew (nvm 及git)
mac 安装nvm 及git 万恶之源 mac 安装这些东西离不开Xcode。及homebrew 一、先说安装git步骤 通用: 方法一:使用 Homebrew 安装 Git(推荐) 步骤如下:打开终端(Terminal.app) 1.安装 Homebrew…...
适应性Java用于现代 API:REST、GraphQL 和事件驱动
在快速发展的软件开发领域,REST、GraphQL 和事件驱动架构等新的 API 标准对于构建可扩展、高效的系统至关重要。Java 在现代 API 方面以其在企业应用中的稳定性而闻名,不断适应这些现代范式的需求。随着不断发展的生态系统,Java 在现代 API 方…...
stm32wle5 lpuart DMA数据不接收
配置波特率9600时,需要使用外部低速晶振...
