【Android】布局优化方案

布局优化的核心问题就是要解决因布局渲染性能不佳而导致应用卡顿的问题。

绘制流程

绘制流程:安卓应用的绘制流程是在UI线程上执行的,主要包括以下几个步骤:

  • 测量(Measure):从视图树的根节点开始,递归地对每个视图进行测量,确定视图的大小和位置。
  • 布局(Layout):根据测量得到的视图大小和位置,递归地对每个视图进行布局,确定视图的具体位置。
  • 绘制(Draw):根据布局得到的视图位置,依次从视图树的根节点开始,递归地对每个视图进行绘制。绘制包括背景绘制、绘制自身内容和绘制子视图等操作。
  • 重绘(Invalidate):如果视图的状态发生改变或需要更新界面,可以调用invalidate()方法,通知系统重新执行绘制流程。

绘制原理

  1. CPU(Central Processing Unit):
    • 视图测量(Measure):CPU负责对视图进行测量,计算出每个视图的大小和位置。这个过程是递归进行的,从根视图开始,通过调用视图的measure()方法来确定视图的测量规格。
    • 视图布局(Layout):CPU根据测量的结果,对视图进行布局,即确定视图的具体位置。同样是递归过程,从根视图开始,通过调用视图的layout()方法来确定视图的位置。
  2. GPU(Graphics Processing Unit):
    • 绘制(Draw):GPU负责对视图进行绘制,将视图的内容渲染到屏幕上。绘制的过程是在GPU上进行的,而不是在CPU上。绘制操作包括背景绘制、绘制自身内容和绘制子视图等。绘制过程中,GPU会根据绘制指令和纹理数据进行图形计算和渲染操作。
  3. 刷新机制(Refresh Mechanism):
    • 触发刷新:当视图发生变化时,可以通过调用invalidate()方法来触发刷新操作。这会导致视图树的绘制无效,需要重新进行视图测量、布局和绘制。
    • 刷新流程:刷新操作一般在UI线程上执行,即主线程。在下一次UI线程的绘制周期内,会依次执行视图的测量、布局和绘制操作,最终将变化的内容渲染到屏幕上。

耗时原因

  1. 视图层级复杂度:视图层级的复杂度是指界面中视图的数量和嵌套关系的复杂程度。当视图层级较深或者视图数量较多时,绘制过程需要进行更多的测量、布局和绘制操作,从而导致耗时增加。
  2. 视图绘制内容复杂度:视图绘制内容的复杂度是指视图的绘制操作涉及的图形计算和渲染的复杂程度。如果视图的绘制内容包含复杂的图形、动画、滤镜等效果,或者需要进行大量的文本渲染,会增加绘制过程的计算和渲染时间。
  3. 频繁的刷新操作:如果界面频繁地进行刷新操作,即触发了多次的invalidate()方法,会导致绘制过程频繁地执行,增加了CPU和GPU的工作负载,从而导致绘制耗时增加。
  4. 异步绘制和布局计算:有些情况下,绘制和布局计算可以在后台线程进行,以减轻主线程的工作负载。但是,如果异步绘制和布局计算的时间较长或者频繁地执行,可能会导致主线程等待绘制结果,从而导致绘制耗时增加。
  5. 不合理的绘制优化:在绘制过程中,可以通过一些优化手段来减少绘制的耗时,如使用绘制缓存、避免不必要的绘制操作、利用硬件加速等。如果在绘制过程中没有合理地进行优化,可能会导致绘制耗时增加。

优化方式

使用标签

merge可以有效的解决布局的层级关系,可以减少一个层级,在布局中可替代为根视图,可减少内存使用。

因为Activity内容视图的父View即Decor View继承于FrameLayout,所以对于我们来说无意中已经多了一层毫无意义的布局。

  • 创建一个新的XML布局文件,并在根布局中使用<merge>标签

    <!-- merge_layout.xml -->
    <merge xmlns:android="http://schemas.android.com/apk/res/android"><!-- 子视图元素,子视图将会被用于添加到父布局 -->
    </merge>
    
  • 使用<include>标签引用merge_layout.xml文件

    <!-- main_layout.xml -->
    <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"android:layout_width="match_parent"android:layout_height="match_parent"><include layout="@layout/merge_layout" /><!-- 其他视图元素 -->
    </LinearLayout>
    

    需要注意的是,使用<merge>标签时,必须确保根布局是LinearLayoutRelativeLayoutFrameLayout等可以容纳子视图的布局容器,而不能是ConstraintLayout等不支持<merge>标签的布局容器。此外,<merge>标签不能设置任何布局属性,如layout_widthlayout_height等,因为<merge>标签本身不会显示在界面上,它只是用于优化布局结构。

使用ViewStub

ViewStub标签与include一样可以用来引入一个外部布局,但是Viewstub引入的布局默认不会解析与显示,宽高为0,View也为null,这样就会在解析layout时节省cpu和内存。

简单的理解就是ViewStubinclude加上GONE.ViewStub常用来引入那些默认不会显示,只在特殊情况下显示的布局,如进度布局、网络失败显示的刷新布局、信息出错出现的提示布局等.

	<ViewStubandroid:id="@+id/network_unreachble"android:layout_width="match_parent"android:layout_height="match_parent"android:layout="@layout/viewStub" />//

在代码中通过(ViewStub)findViewById(id)找到ViewStub,使用inflate()展开ViewStub,然后得到子View,如下:

	ViewStub stub = (ViewStub)findViewById(R.id.viewStub);// 解析并且显示该部分,返回值就是解析后的该`View`viewStubView = stub.inflate();//展开为实际视图Button xxx = (Button)viewStubView.findViewById(R.id.xxx);//获取子View

使用AsyncLayoutInflater异步加载对应的布局

默认情况下 setContentView() 函数在UI线程执行,其中有一系列的耗时动作:Xml的解析、View的反射创建等过程同样是在UI线程执行的,AsyncLayoutInflater 就是来帮我们把这些过程以异步的方式执行,保持UI线程的高响应。

  • 工作线程加载布局
  • 回调主线程
  • 节省主线程时间,将UI线程迁移到了子线程,牺牲了易用性。
AsyncLayoutInflater inflater = new AsyncLayoutInflater(context);
inflater.inflate(R.layout.your_layout, parent, new AsyncLayoutInflater.OnInflateFinishedListener() {@Overridepublic void onInflateFinished(@NonNull View view, int resid, @Nullable ViewGroup parent) {// 在这里处理加载完成后的视图// 可以将视图添加到指定的父容器中if (parent != null) {parent.addView(view);}}
});

需要替换 R.layout.your_layout 为你想要异步加载的布局文件的资源ID,parent 是你希望将加载完成的视图添加到的父容器。

AsyncLayoutInflater是通过侧面缓解的方式去缓解布局加载过程中的卡顿,但是它依然存在一些问题:

  • 不能设置LayoutInflater.Factory,需要通过自定义AsyncLayoutInflater的方式解决,由于它是一个final,所以需要将代码直接拷处进行修改。
  • 因为是异步加载,所以需要注意在布局加载过程中不能有依赖于主线程的操作。
  • 可能引起UI不一致:由于异步加载,视图的加载和渲染可能会出现延迟,导致UI在加载期间出现不一致的状态。这可能会影响用户体验,特别是在需要快速响应的界面上。
  • 难以调试:由于异步加载是在后台线程执行的,因此在调试时可能会难以追踪和调试布局加载过程中的问题。这增加了调试和排查错误的难度。

X2C方案(XML to Code)

  1. 引入X2C插件:首先需要在项目中引入X2C插件,该插件会将XML布局文件转换为对应的Java代码。

  2. 创建布局:在项目中创建一个XML布局文件,定义所需的布局结构和属性。

  3. 生成Java代码:使用X2C插件将XML布局文件转换为Java代码。可以通过自动构建或手动触发插件来执行此转换。转换后,将在生成的Java类中找到与XML布局文件对应的代码。

    X2C方案需要进行额外的配置和构建过程,并且生成的Java类与XML布局文件是一一对应的。因此,在使用X2C方案时,需要确保生成的Java类与对应的XML布局文件是匹配的,否则可能会出现布局错误或找不到控件的情况。

其他优化方案

使用ConstraintLayout降低布局嵌套层级
  • 实现几乎完全扁平化的布局

  • 构建复杂布局性能更高

Compose方案

还没学。。。。

使用RecyclerView代替ListView

RecyclerView相对于ListView具有更高的灵活性和性能。它使用了ViewHolder的复用机制,减少了视图的创建和销毁,提高了滚动的平滑度。

多使用include

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

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

相关文章

【06】ES6:数组的扩展

一、扩展运算符 扩展运算符&#xff08;spread&#xff09;是三个点&#xff08;…&#xff09;。它是 rest 参数的逆运算&#xff0c;将一个数组转为用逗号分隔的参数序列。 1、基本语法 [...array] // array表示要转换的数组console.log(...[1, 2, 3]) // 1 2 3 console.l…

Python操作合并单元格

如何使用python操作Excel实现对合并单元格的一系列操作 01、准备工作&#xff08;使用镜像下载&#xff09; pip install openpyx -i https://pypi.tuna.tsinghua.edu.cn/simple 02、简单示例 简单创建一个工作簿进行示范&#xff1a; from openpyxl import Workbook from o…

波奇学C++:智能指针(二):auto_ptr, unique_ptr, shared_ptr,weak_ptr

C98到C11&#xff1a;智能指针分为auto_ptr, unique_ptr, shared_ptr&#xff0c;weak_ptr,这几种智能都是为了解决指针拷贝构造和赋值的问题 auto_ptr&#xff1a;允许拷贝&#xff0c;但只保留一个指向空间的指针。 管理权转移&#xff0c;把拷贝对象的资源管理权转移给拷贝…

vue v-permission权限指令

控制页面及按钮的显示隐藏 src/directive/permission/index.js import permission from ./permissionconst install function(Vue) {Vue.directive(permission, permission) }if (window.Vue) {window[permission] permissionVue.use(install); // eslint-disable-line }per…

vue中实现纯数字键盘

一、完整 代码展示 <template><div class"login"><div class"login-content"><img class"img" src"../../assets/image/loginPhone.png" /><el-card class"box-card"><div slot"hea…

4.11 计算文件的大小(C语言实现)

【题目描述】编写一个C程序&#xff0c;用来计算指定文件的大小。 【题目分析】计算指定文件大小的方法很多。最直观的方法是通过扫描整个文件计算出文件的字节数。但是这种方法对系统的开销很大&#xff0c;比较浪费时间。可以巧妙地利用I/O库中提供的函数来进行文件大小的判定…

Pytorch中有哪些损失函数?

Pytorch中有哪些损失函数? 一、常见的损失1. **均方误差损失&#xff08;Mean Squared Error Loss&#xff09;&#xff1a;nn.MSELoss**2. **交叉熵损失&#xff08;Cross-Entropy Loss&#xff09;&#xff1a;nn.CrossEntropyLoss**3. **二分类交叉熵损失&#xff08;Binar…

阵列信号处理---频率-波数响应和波束方向图

波束延迟求和器 阵列是由一组全向阵元组成&#xff0c;阵元的位置为 p n p_n pn​&#xff0c;如下图所示&#xff1a; 阵元分别在对应的位置对信号进行空域采样&#xff0c;这样就产生了一组信号信号为 f ( t , p ) f(t,p) f(t,p),具体表示如下&#xff1a; f ( t , p ) [ f…

C++入门篇(零) C++入门篇概述

目录 一、C概述 1. 什么是C 2. C的发展史 3. C的工作领域 4. C关键字(C98) 二、C入门篇导论 一、C概述 1. 什么是C C是基于C语言而产生的计算机程序设计语言&#xff0c;支持多重编程模式&#xff0c;包括过程化程序设计、数据抽象、面向对象程序设计、泛型程序设计和设计模式…

SQL Server 2016(创建数据库)

1、实验环境。 某公司有一台已经安装了SQL Server 2016的服务器&#xff0c;现在需要新建数据库。 2、需求描述。 创建一个名为"db_class"的数据库&#xff0c;数据文件和日志文件初始大小设置为10MB&#xff0c;启用自动增长&#xff0c;数据库文件存放路径为C:\db…

Ubuntu系统CLion安装与Ubuntu下菜单启动图标设置

Ubuntu系统CLion安装 pycharm 同理。 参考官网安装过程&#xff1a;官网安装过程 下载linux tar.gz包 # 解压 sudo tar -xzvf CLion-*.tar.gz -C /opt/ sh /opt/clion-*/bin/clion.sh其中第二个命令是启动CLion命令 clion安装完以后&#xff0c;不会在桌面或者菜单栏建立图…

大学里学编程,为什么这么难?

在大学学习计算机专业&#xff0c;为何很多同学觉得编程学得不顺心呢&#xff1f;许多同学会有这种感觉&#xff0c;在上大学里的计算机专业课程时&#xff0c;听得头都大了&#xff0c;但是真正要写代码&#xff0c;却不知道从哪里开始&#xff0c;或是觉得&#xff0c;大学里…

线性表 力扣67. 二进制求和

题目 67. 二进制求和 翻译 主要思路 核心思路是像竖式计算一样&#xff0c;不过需要将字符串a和b反转后逐位进行二进制计算得到字符串c&#xff0c;最后再将c反转就是答案 逐位计算的时候利用count&#xff0c;在将a和b当前位置数字相加后通过模2来决定字符串c对应位置的数…

05:2440----代码重定义

目录 一&#xff1a;引入 1&#xff1a;基本概念 2&#xff1a;NAND启动 3&#xff1a;NOR启动 4:变量 5&#xff1a;实验证明 A:代码makefile B:NOR启动 C:NAND启动 D:内存空间 二&#xff1a;链接脚本 1:NOR 2:NAND 3:解决方法 A:尝试解决 B:方法一解决 A:简…

【SparkSQL】SparkSQL的运行流程 Spark On Hive 分布式SQL执行引擎

【大家好&#xff0c;我是爱干饭的猿&#xff0c;本文重点介绍、SparkSQL的运行流程、 SparkSQL的自动优化、Catalyst优化器、SparkSQL的执行流程、Spark On Hive原理配置、分布式SQL执行引擎概念、代码JDBC连接。 后续会继续分享其他重要知识点总结&#xff0c;如果喜欢这篇文…

Echarts大屏可视化_05 折线图的定制开发

继续跟着pink老师学习Echarts相关内容&#xff01;&#xff01;&#xff01;&#xff01;&#xff01;&#xff01;&#xff01;&#xff01;&#xff01; 折线图1 1.引入 折线图选取示例地址 标题没有用到就给他删了 直接引入 注意这里是line下面的chart 获取dom元素一定不…

吉他初学者学习网站搭建系列(4)——如何查询和弦图

文章目录 背景实现ChordDbvexchords 背景 作为吉他初学者&#xff0c;如何根据和弦名快速查到和弦图是一个必不可少的功能。以往也许你会去翻和弦的书籍查询&#xff0c;像查新华字典那样&#xff0c;但是有了互联网后我们不必那样&#xff0c;只需要在网页上输入和弦名&#…

读书笔记:《More Effective C++》

More Effective C Basics reference & pointer reference 必定有值&#xff0c;pointer 可以为空reference 声明时必须定义&#xff0c;必须初始化reference 无需测试有效性&#xff0c;pointer 必须测试是否为 nullreference 可以更改指向对象的值&#xff0c;但是无法…

POSTGRESQL中如何利用SQL语句快速的进行同环比?

1. 引言 在数据驱动的时代&#xff0c;了解销售、收入或任何业务指标的同比和环比情况对企业决策至关重要。本文将深入介绍如何利用 PostgreSQL 和 SQL 语句快速、准确地进行这两种重要分析。 2. 数据准备 为了演示&#xff0c;假设我们有一张 sales 表&#xff0c;存储了销…

【PyTorch】线性回归

文章目录 1. 代码实现1.1 一元线性回归模型的训练 2. 代码解读2.1. tensorboardX2.1.1. tensorboardX的安装2.1.2. tensorboardX的使用 1. 代码实现 波士顿房价数据集下载 1.1 一元线性回归模型的训练 import numpy as np import torch import torch.nn as nn from torch.ut…