鸿蒙NEXT开发【合理使用布局】性能分析

ArkUI框架执行流程

在使用ArkUI开发中,我们通过布局组件和基础组件进行界面描述,这些描述会呈现出一个组件树的结构,基础组件在其中为叶子结点,布局组件则是中间节点,可以把这棵树称之为应用组件树。当用户执行交互(滑动,点击等行为)时会触发界面修改,界面的修改本质上是通过触发这棵组件树的重新渲染,来实现应用界面更新的过程。

1

应用界面更新的过程主要分为两个过程:数据处理过程和UI更新过程

1、数据处理过程中主要是对状态数据进行更新,状态数据指得是所定义的@State等相关的数据。数据变化时,会有一定的更新耗时,并且数据关联的组件数量,也影响下一步UI更新的耗时,那么对于开发过程需要关注的,就是避免无效的数据更新导致冗余的UI更新操作。

2、UI更新过程中则是对需要更新的元素进行更新操作,对应的元素会经历Build、Measure、Layout和Render等阶段。其中Build是执行组件创建和组件标脏的过程,Measure是对组件的宽高进行测量的阶段,Layout是对元素进行在屏幕上位置进行摆放的阶段,而Render则是根据测量和布局得到的大小位置等信息,进行提交绘制的过程。

说明

在初次进入页面的时候,所有的组件都会参与到界面的渲染中(换句说法,初次渲染的时候,可以认为所有的组件都需要更新)。

UI更新过程

UI更新过程包含组件标脏过程以及布局过程。在初次加载时,所有的组件都会经历这几个阶段(除去if/else条件为否的分支以及LazyForEach中未在可视区的内容),而在界面更新时(如列表滑动,切换显示隐藏状态,触发页面元素内容样式位置大小等变化等场景下),并不需要把页面所有的组件对象重新创建一遍,而是只需要对需要更新部分进行更新,而需要更新部分则是脏节点数组里包含的内容,UI线程处理过程会先将脏节点进行Build,Build的过程会按照组件id,依次更新组件设置的属性,如果属性发生改变,则进行组件标脏,其中布局属性(width、height、padding、margin等)发生改变会标记为布局脏,找到布局边界,进行子树更新,而非布局(Color、BackgroundColor、opacity等)属性仅会影响自身属性,不会进行子树查找。

多数情况下,需要我们关注的是重新布局带来的影响。如果某个组件的布局发生变化,一般也会对其他组件的布局也会产生影响,所以当有组件的布局发生变化,最简单的办法就是对整棵树进行重新布局,但是这样对整棵树进行重新布局的代价太大,所以需要降低重新布局带来的成本。实际上在一些特定场景下,组件发生变化后只需要对部分组件进行重新布局。标脏过程就是用来确定布局最小影响范围,来减少对整棵树进行重新布局的代价,而这个影响范围就是布局边界以内。

一般来讲,如果一个组件设置了固定的宽高尺寸,那这个组件就是布局边界。其内部组件布局的变化,不会影响到此布局边界外部的布局情况,那么在查找的时候,只需要在布局边界内部判断哪些组件的布局会受到影响,可以避免在整棵树结构的查找过程。

2

确定实际的脏节点数组后,根据脏节点数组来拿到对应的脏节点对象,通过递归遍历children进行Measure过程,如果该对象布局参数没有发生变化,就会跳过对应的Measure阶段。当Measure执行完成后,进行layout阶段。

3

从以上的过程可以看出,影响UI更新过程的主要因素是参与更新的节点数量。

在初次加载的时候,由于所有的节点都要参与全过程,那么如果对首帧渲染的速度有要求,就需要降低整体页面的组件节点数量。

在页面内容更新过程中,由于状态变量的变化导致UI的更新,可以利用布局边界减少子树更新的数量以及减少布局的计算。

精简节点数

布局阶段是采用递归遍历所有节点的方式进行组件位置和大小的计算, 如果嵌套层级过深,将带来了更多的中间节点,在布局测算阶段下,额外的节点数将导致更多的计算过程,造成性能劣化。我们通过模拟了10、100、500、1000层Row嵌套的情况下,通过Profiler工具抓取Launch数据查看对应的首帧绘制,以及页面Measure/Layout时间进行对比。

      Row() {
        ... // 10、100、500、1000层Row容器嵌套
        Row() {
          Text('Inner Text')
        } 
        ... 
      }

然后进一步对比了在平铺的情况下,Row内组件个数在10、100、500、1000的条件下,使用Profiler工具抓取Launch的数据情况,得到如下结果如表1所示。

 Row() {
    Row() {} 
    ... // 10、100、500、1000层Row容器并排
    Text('Inner Text')
  }
对比指标 10 100 500 1000
嵌套/层 首帧绘制 3.2ms 5.8ms 17.3ms 32ms
Measure 1.88ms 2.89ms 5.93ms 10.46ms
Layout 0.38ms 1.12ms 5.26ms 10.88ms
平铺 /个 首帧绘制 3.6ms 4.5ms 14ms 24.3ms
Measure 2.15ms 2.31ms 5.61ms 9.26ms
Layout 0.39ms 1.38ms 4.74ms 9.92ms

说明

以上数据来源均为版本DevEco Studio 4.0.3.415、SDK 4.0.10.9条件下测试得到,不同设备类型数据可能存在差异,测试数据旨在体现性能优化趋势,仅供参考。

4

根据以上数据对比发现,组件平铺和嵌套在相同组件个数的情况下,其性能差异不大,并且整体上趋势保持一致,随着组件数量增加呈现线性增长的劣化,由此可以得到结论,真正影响布局性能的因素是参与布局的节点数量。所以在进行布局时,应该尽量减少整体的节点数,来减少布局的性能劣化。

针对减少总节点,主要有两个方向:1、移除冗余的节点 2、使用扁平化布局减少节点数。

移除冗余节点

对于常出现冗余的情况,例如可能会在Row容器包含一个同样也是Row容器的子级。这种嵌套实际是多余的,并且会给布局层次结构造成不必要的开销。


                
### HarmonyOS NEXT 性能优化技巧最佳实践 HarmonyOS NEXT性能优化开发者在实际项目中必须关注的重点之一。以下将从多个方面详细介绍性能优化的技巧和最佳实践。 #### 1. 数据结构优化属性访问 在 HarmonyOS NEXT 开发中,数据结构的选择和属性访问方式对性能有显著影响。通过合理设计数据结构,减少不必要的计算和内存占用,可以有效提升应用性能[^1]。例如,在日历组件案例中,开发者可以通过以下方法优化数据处理: - 使用高效的数组或对象结构替代复杂的数据模型。 - 减少深层嵌套的对象访问,尽量扁平化数据结构。 - 在属性访问时,优先使用本地缓存变量,避免频繁访问全局或远程对象。 #### 2. 主线程优化异步处理 主线程的流畅性直接影响用户体验。为了避免主线程阻塞,应尽量将耗时操作移至后台线程执行[^3]。常见的优化手段包括: - 使用 `Worker` 或 `TaskDispatcher` 处理异步任务。 - 对于需要大量计算的场景,采用分批加载或懒加载策略。 - 通过事件驱动机制替代直接调用,降低主线程负担。 #### 3. 渲染优化 渲染性能是 HarmonyOS NEXT 应用优化的核心环节之一。为了提高渲染效率,可以采取以下措施: - 减少布局层级,避免过度嵌套的 UI 结构。 - 使用虚拟列表技术(Virtual List)优化长列表渲染。 - 启用 GPU 加速,减少 CPU 渲染压力。 - 在动画效果中,尽量使用硬件加速的属性(如 `translate`、`scale`),避免触发重绘。 #### 4. 资源管理代码复用 资源管理和代码复用是提升应用性能的重要手段[^2]。开发者可以通过以下方式减少资源消耗并提高代码质量: - 合理压缩图片、音频等多媒体资源,避免加载过大的文件。 - 使用模块化开发,提高代码复用率,减少冗余逻辑。 - 定期清理无用的依赖库和资源文件,减少包体积。 #### 5. 系统架构核心技术支持 了解 HarmonyOS NEXT 的系统架构和技术特性有助于开发者制定更有效的优化策略[^4]。例如: - 利用分布式软总线技术,优化多设备间的通信效率。 - 借助 FA(Feature Ability)和 PA(Particle Ability)的设计理念,实现轻量化功能模块。 - 结合 ArkTS 和 eTS 的语言特性,编写高性能代码。 #### 6. 性能分析工具的应用 性能优化离不开科学的分析工具。HarmonyOS NEXT 提供了多种性能分析工具,帮助开发者定位瓶颈并改进代码[^2]: - 使用 DevEco Studio 的性能分析器(Profiler)检测 CPU、内存、网络等方面的性能问题。 - 针对特定场景,采用方法级性能分析工具,深入挖掘代码中的潜在问题。 ```python # 示例:使用 TaskDispatcher 执行异步任务 import ability_slice from '@ohos.ability.slice'; import taskDispatcher from '@ohos.taskDispatcher'; export default class MainAbilitySlice extends ability_slice { onForeground() { const worker = taskDispatcher.createWorker(); worker.postMessage('Start background task'); worker.onmessage = (event) => { console.log('Background task result:', event.data); }; } } ``` ###
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值