【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&…...
Flask RESTful 示例
目录 1. 环境准备2. 安装依赖3. 修改main.py4. 运行应用5. API使用示例获取所有任务获取单个任务创建新任务更新任务删除任务 中文乱码问题: 下面创建一个简单的Flask RESTful API示例。首先,我们需要创建环境,安装必要的依赖,然后…...
uni-app学习笔记二十二---使用vite.config.js全局导入常用依赖
在前面的练习中,每个页面需要使用ref,onShow等生命周期钩子函数时都需要像下面这样导入 import {onMounted, ref} from "vue" 如果不想每个页面都导入,需要使用node.js命令npm安装unplugin-auto-import npm install unplugin-au…...
linux 错误码总结
1,错误码的概念与作用 在Linux系统中,错误码是系统调用或库函数在执行失败时返回的特定数值,用于指示具体的错误类型。这些错误码通过全局变量errno来存储和传递,errno由操作系统维护,保存最近一次发生的错误信息。值得注意的是,errno的值在每次系统调用或函数调用失败时…...
Python爬虫(一):爬虫伪装
一、网站防爬机制概述 在当今互联网环境中,具有一定规模或盈利性质的网站几乎都实施了各种防爬措施。这些措施主要分为两大类: 身份验证机制:直接将未经授权的爬虫阻挡在外反爬技术体系:通过各种技术手段增加爬虫获取数据的难度…...
什么是Ansible Jinja2
理解 Ansible Jinja2 模板 Ansible 是一款功能强大的开源自动化工具,可让您无缝地管理和配置系统。Ansible 的一大亮点是它使用 Jinja2 模板,允许您根据变量数据动态生成文件、配置设置和脚本。本文将向您介绍 Ansible 中的 Jinja2 模板,并通…...
DeepSeek 技术赋能无人农场协同作业:用 AI 重构农田管理 “神经网”
目录 一、引言二、DeepSeek 技术大揭秘2.1 核心架构解析2.2 关键技术剖析 三、智能农业无人农场协同作业现状3.1 发展现状概述3.2 协同作业模式介绍 四、DeepSeek 的 “农场奇妙游”4.1 数据处理与分析4.2 作物生长监测与预测4.3 病虫害防治4.4 农机协同作业调度 五、实际案例大…...
AGain DB和倍数增益的关系
我在设置一款索尼CMOS芯片时,Again增益0db变化为6DB,画面的变化只有2倍DN的增益,比如10变为20。 这与dB和线性增益的关系以及传感器处理流程有关。以下是具体原因分析: 1. dB与线性增益的换算关系 6dB对应的理论线性增益应为&…...
Linux 内存管理实战精讲:核心原理与面试常考点全解析
Linux 内存管理实战精讲:核心原理与面试常考点全解析 Linux 内核内存管理是系统设计中最复杂但也最核心的模块之一。它不仅支撑着虚拟内存机制、物理内存分配、进程隔离与资源复用,还直接决定系统运行的性能与稳定性。无论你是嵌入式开发者、内核调试工…...
Linux安全加固:从攻防视角构建系统免疫
Linux安全加固:从攻防视角构建系统免疫 构建坚不可摧的数字堡垒 引言:攻防对抗的新纪元 在日益复杂的网络威胁环境中,Linux系统安全已从被动防御转向主动免疫。2023年全球网络安全报告显示,高级持续性威胁(APT)攻击同比增长65%,平均入侵停留时间缩短至48小时。本章将从…...
spring boot使用HttpServletResponse实现sse后端流式输出消息
1.以前只是看过SSE的相关文章,没有具体实践,这次接入AI大模型使用到了流式输出,涉及到给前端流式返回,所以记录一下。 2.resp要设置为text/event-stream resp.setContentType("text/event-stream"); resp.setCharacter…...
