当前位置: 首页 > ai >正文

HarmonysOS 模块化设计理念

HarmonysOS 模块化设计理念

在大型软件工程中,一般会伴随多团队参与开发。各团队之间以弱耦合方式交互,通过契约化接口定义业务之间的交互,定义业务件的接口,确保各团队业务独立发展,互不影响,实现快速迭代演进。业务模块化是现代软件工程的核心原则之一,通过将大型复杂系统拆解为更小、更易管理和理解的功能模块,提高系统的可维护性和可扩展性。每个功能模块都是独立单元,具有清晰定义的接口和职责,能够与其他模块交互以完成复杂任务。

在 HarmonyOS 应用开发中,模块化既是设计原则,也是开发实践。该原则要求将应用程序拆分为多个功能模块,每个模块负责特定功能或特性。这些模块可以独立开发、编译和部署,也可以在不同设备上灵活组合和调用。

应用程序包结构概念

在进行模块化设计时,需要考虑 HarmonyOS 的应用包结构选型,HarmonyOS 的应用包结构用于定义应用的组织方式。通过开发态、编译态、发布态阶段的应用程序包形态,可以了解不同包类型的具体使用场景及规则。详细请参见Stage 模型应用程序包结构。

Ability 应用组件设计

HarmonyOS 应用的业务逻辑需要通过Ability 组件承载,根据业务设备以及业务诉求不同,需要考虑 Ability 组件的选择以及设计。在多设备的背景下,应用的形态不一定是传统移动设备上的单任务单窗口形式,在一些场景下,多任务多窗口的形态可以让用户获得更好的用户体验,提升使用效率。

例如在手机设备上:

  • 笔记应用,可让用户将信息从笔记的一页复制到另一页。
  • 文档编辑应用,可让用户同时打开编辑多个文档,可让用户将内容从一个文档复制或移动到另一个文档。
  • 导航/打车应用,可以让导航后台运行,回到主页查找新的位置信息或其它信息。
  • 购物类临时客服界面,可让用户通过任务管理快速从商品浏览页切换回到客服会话界面,避免用户一层层打开查找。
  • 在应用支付/登录页面,用户可以切换到其他页面查找并复制相关信息。

在大屏设备上,应用内的多个任务可以以多窗口的形式存在,用户可以并行操作应用的不同功能。

  • 视频播放器应用,可让用户在观看播放内容的同时浏览其他可能感兴趣的视频列表。
  • 电子邮件应用,可让用户在撰写电子邮件的同时查看收到的邮件列表。
  • 地址簿应用,可让用户并排比较多个人员的联系信息。
  • 阅读应用,可让用户在查阅所有标题概要后,打开多篇文章供稍后阅读。

对于这种类似独立应用的任务,每个任务对应一个 UIAbility 组件实例,每个任务可以单独显示一个窗口。用户可以在同一应用的不同任务间切换,就像单独的应用一样。在大屏设备上,可以独立移动、调整大小、显示和隐藏应用窗口。所以在进行功能设计时,需考虑应用是否支持多任务多窗口,这影响整体工程模块化结构。

  • 对于单 Ability 的情况,可以对应单窗口类型应用,或者通过多实例或指定实例实现的多任务应用。例如,普通游戏应用建议采用单 HAP 来承载 UIAbility。
  • 对于多 Ability 有两种情况:
    • 对于多窗口类型的应用,每个窗口对应不同的功能,通过不同的 UIAbility 承载。如上述例子中导航/打车应用中,导航功能界面和主页属于不同的功能,并且作为两个任务呈现给用户。可以将该模块作为 Feature 类型的 HAP 承载相应的 UIAbility 组件。
    • 对于应用的拓展功能,如卡片和分享业务,这些功能不会作为单独的任务和窗口形态运行。由于这些功能相对独立,并且由系统提供的独立ExtensionAbility承载,从更好地拆分业务的角度考虑,建议通过 Feature 类型的 HAP 承载单独的 ExtensionAbility 组件。

应用模块化选型

应用架构旨在实现业务服务,从技术角度思考业务实现方式;工程模块化模型则基于技术架构,对代码工程进行模块化选型,需要考虑技术如何在代码工程中落地,只有代码工程模型的技术选型合理了,才能在包体积、性能、产品部署等取得一个最优的综合表现。

业务通常分为多个模块,例如某购物软件,包含主页导航、商品详情、购物车、支付、订单、个人信息等模块。技术架构上,这些业务模块表现为高内聚低耦合的模块(Module)。在代码工程模块化的技术选型中,由于 Entry 类型的 HAP 是工程默认存在的且不能存在多个,所以主要考虑的模块类型有:Feature 类型的 HAP 模块、HAR 模块和 HSP 模块。

在技术架构中选择代码模块类型时,需要根据业务特性和模块功能等多方面因素综合评估。基于常见的应用模块化模型,以下是几种实际业务中可能遇到的情况:

  1. 共享模块:某个功能模块(业务模块或者能力模块)需要在多个应用之间共享其代码逻辑和资源。
  2. 按需加载模块:某个功能模块,使用时由用户决定安装时机,动态从应用市场下载安装使用。
  3. 多 HAP/HSP 引用相同 HAR 包的影响:从性能角度出发,需要减少多 HAP/HSP 对相同 HAR 包的引用。

共享模块

对于大型软件,不同业务和基础能力由多个团队开发,各团队之间需要代码仓隔离。如果某个或若干个 HAR 工程模块由某个团队负责,又想代码仓隔离,可以在独立工程中开发这些 HAR,并通过公司私有的 OHPM 仓发布和集成编译产物。如下图所示。

图 1 多工程合作模式

这部分可以发布到 OHPM 仓的模块,叫做共享模块,可以将公共能力共享给多个应用使用,如公司内部多个应用使用某个公共能力网络库;或者也可以将该公共能力封装成库贡献给社区,给其他应用集成使用,这样的话这个模块也只能是 HAR 模块。

按需加载模块

随着应用业务的扩展,应用为用户提供的功能不断增加。然而,不是所有功能都是用户频繁使用的。根据用户运营报告的分析,对于月活跃度较低的功能,可以将其设计为按需加载模块。用户首次从应用市场安装时,只会下载不包含按需加载模块的内容。当用户需要使用特定功能时,可以选择下载并安装相应的功能模块。

按需加载模块有以下好处:

  • 减少包体积:用户从应用市场首次下载的应用不包含按需加载模块,用户看到的包体积减少,从而减少了用户下载和安装时间,减少了用户等待时间。
  • 减少系统资源:应用安装之后所占用的空间也变少(节省 ROM 空间),应用启动时加载的特性少了(节省了 RAM 空间)。
  • 架构演进:定义为按需加载的特性明确,模块间耦合关系清晰,有利于应用架构演进。

如果某个特性做成了按需加载模块,该模块可以设计为 Feature 类型的 HAP 或者 HSP,HAP 和 HSP 都可以实现按需加载,区别在于 Feature 类型的 HAP 可以包含 Ability 组件,结合前面的Ability 应用组件设计以及业务是否需要按需加载,从整体上可以划分两个大的场景如下:

  • 单 HAP 场景:如果只包含一个 UIAbility 组件(包括 UIAbility 多实例/指定实例),无需使用 ExtensionAbility 组件,优先采用单 HAP(Entry 类型的 HAP)来实现应用开发,其中根据是否需要实现按需加载的来决定选择 HSP 或者 HAR 作为模块。
  • 多 HAP 场景:要实现多任务承载多个 UIAbility 组件以及使用 ExtensionAbility 组件实现扩展功能,可以采用多 HAP(即一个 Entry 类型的 HAP 和多个 Feature 类型的 HAP)来开发应用。每个 HAP 包含一个 UIAbility 组件或一个 ExtensionAbility 组件。在多 HAP 情况下,根据是否具有公共能力选择模块类型。

应用组件的设计决定了模块化设计是采用单 HAP 工程还是多 HAP 工程。设计初期需考虑应用的任务形态,以确定合适的模块化结构。

多 HAP/HSP 引用相同 HAR 包的影响

在应用开发的过程中,可以使用HSP或HAR的共享包方式将同类的模块进行整合,用于实现多个模块或多个工程间共享 ArkUI 组件、资源等相关代码。

在多 HAP/HSP 引用相同 HAR 包时,由于共享包的动态和静态差异,HAR 包中的单例可能失效,影响应用冷启动性能。

图 2 HAP 包和 HSP 包分别引用相同 HAR 包

如上图所示,工程内包含三个模块:HAP 包作为应用主入口模块,HSP 包作为应用主界面显示模块,HAR_COMMON 集成了所有通用工具类,其中 funcResult 是 func 方法的执行结果。

当 HAP 和 HSP 模块同时引用 HAR_COMMON 模块时,会破坏 HAR 的单例模式。因此,HAP 和 HSP 模块在使用 HAR_COMMON 中的 funcResult 时,会导致 func 方法在两个模块加载时各执行一次,从而增加文件的执行时间。

仅从性能角度考虑,可以采用以下方式进行修改,以缩短冷启动阶段的耗时。

图 3 切换为 HAP 包和 HAR 包分别引用相同 HAR 包

说明

  • 在多 HAP/HSP 引用相同 HAR 包的情况下,如果 HSP 包和 HAR 包均能满足业务需求,建议将 HSP 包改为 HAR 包。
  • 若使用的 HSP 为集成态 HSP,可跳过该优化方案。
  1. 在被引用 HAR_COMMON 包中写入功能示例。

  2. 分别通过使用 HSP 包和 HAR 包来引用该 HAR_COMMON 包中的功能进行性能对比实验。

    • 使用 HAP 包和 HSP 包引用该 HAR_COMMON 包中的功能。

      HAP 包引用 HAR_COMMON 包中的功能。

    • 使用 HAP 包和 HAR 包引用该 HAR_COMMON 包中的功能。

      HAP 包引用 HAR_COMMON 包中的功能。

使用Launch 模板,对优化前后启动性能进行对比分析。

分析阶段的起点为启动 Ability(即 H:void OHOS::AppExecFwk::MainThread::HandleLaunchAbility 的开始点),阶段终点为应用第一次接到 vsync(即 H:ReceiveVsync dataCount:24Bytes now:timestamp expectedEnd:timestamp vsyncId:int 的开始点)。

图 4 优化前,使用 HSP 包

图 5 优化后,使用 HAR 代替 HSP

优化前后的对比数据如下:

|
方案

|

阶段时长(毫秒)

| |
| |

|

(优化前)使用 HSP 包

|

3125

|
|

(优化后)使用 HAR 代替 HSP

|

853.9

|

说明

上述示例为凸显出差异,func 执行函数循环次数为 100000000,开发者实际修改后收益需根据实际情况测试。

测试数据表明,将 HSP 替换为 HAR 包后,应用启动耗时明显缩短。

单 HAP 工程

对于单窗口应用的 APP 工程,其仅包含一个 Entry 类型的 HAP。划分的模块则根据是否有按需加载的需求,来考虑采用 HAR 模块和 HSP 模块。

不包含按需加载模块

对于不需要按需加载且仅包含一个 Entry 类型的 HAP 的 App,可以直接全部采用 HAR 进行开发设计。如下图所示:

说明

这里提到的“仅有一个 HAP”是指一种设备类型仅包含一个 HAP,而不是指.app 文件包中仅有一个 HAP。.app 文件包可以包含其他设备的 HAP 包,例如手表和大屏设备的 HAP 包,以支持多设备分发。

图 6 非按需加载工程模型

上图工程架构中,除了产品模块层中与设备相关的 HAP 外,其他模块均为 HAR。这些被依赖的 HAR 最终都会被编译进 HAP 中。

设计成 HAR 包有以下优点:

  1. 全部编译进 HAP,无额外的 HSP,节省 HSP 的安装和加载成本。
  2. HAR 在编译进 HAP 时,可以利用 ArkTS 的语言特性和编译器功能,做类型推断和编译优化。
  3. 代码工程架构简单,后续演进较为灵活。

包含按需加载模块

在单 HAP 工程中实现按需加载功能时,对应的组件需采用 HSP 作为按需加载模块。HAR 是静态共享库,若多个 HAP 或 HSP 依赖同一份 HAR,该 HAR 在应用内会被重复存储。HSP 是动态共享库,其安装和加载会有性能损失,过多的 HSP 可能影响安装效率和 App 启动性能。需考虑 App 占用空间是否受限及启动性能的敏感度,根据业务需求在 App Size 与启动性能之间做好平衡。

说明

这里提到的 App Size 指用户安装按需加载模块后,应用的整体大小。

App Size 优先

对于 App Size 优先的,可以考虑将公共依赖的模块封装在一个 HSP 模块壳中,如下图所示:

图 7 公共依赖模块通过 HSP 模块壳承载

hap_A 依赖于独有的共享库 har_A,同时需要依赖于 har_C 和 har_D;而按需加载模块 hsp_B 依赖于独有的共享库 har_B,同时需要依赖于 har_C 和 har_D。

说明

这里的共享库 har_A、har_B、har_C、har_D 不一定本地工程,有可能是从 ohpm 仓上依赖下载的。

因为 har_C 和 har_D 同时被 hap_A 和 hsp_B 工程所依赖,所以为了节省 App Size,可以将其封装到名为“common_hsp”的 Module 中,对外暴露 har_C 和 har_D 的接口,将 har_C 和 har_D 打包到 common_hsp 中,最后让 hap_A 和 hsp_B 依赖于 common_hsp 工程。common_hsp 工程是无实际意义的,它仅是一个“模块壳”,是为了最小化 App Size 而存在的。

性能优先

对于性能优先的,则不需要再封装一个公共的 HSP 模块,直接依赖公共 HAR 包:

图 8 公共依赖模块使用 HAR 模块承载

因为公共 HSP 包需要安装和加载,所以会有一些性能损耗。对于启动性能敏感型的应用,则将 hap_A 和 hsp_B 直接依赖于 har_C 和 har_D。最终编译产物里面有 2 个,hap_A.hap 和 hsp_B.hsp,但是这两个编译产物里面均会包含 har_C 和 har_D,App Size 会比采用公共 HSP 模型大。

多 HAP 工程

对于同一个设备类型,如果要实现不同的独立功能模块,并且相对独立,以及具有单独的入口的功能特性,建议做成一个独立特性的 HAP,按需下载安装。此时一个 App 包中,就会有多个 HAP 包,其中有且仅有一个 Entry 类型的 HAP,其他的均是 Feature 类型的 HAP。多 HAP 之间业务独立,但是可能会有业务能力共享,所以在进行模块化设计时,需要根据是否具有公共能力来进行选择。

包含公共能力模块

对于具备公共能力模块的工程,和上述 HAP+HSP 组合是类似的,需要考虑在 App Size 与启动性能之间做平衡。

性能优先

一般多 HAP 应用架构普适性采用以下模型,除了产品组件中存在 HAP 包之外,其余的均是 HAR 包,如下图所示:

图 9 多 HAP 工程模块示意图

编译产物中,多个 HAP 之间存在相同的 HAR 包(如 har_2、har_3、har_C、har_D、har_E)。这种情况下,App Size 可能会增大。如果 App Size 不是应用的瓶颈,或者 HAR 包的大小较小,对 App Size 的影响可控,可以采用这种模型,从而减少动态加载的性能损耗。

App Size 优先

上述问题的本质在于如何在 HAP 和 HSP 之间分布 HAR 包,以最小化 App 的大小并减少 HAR 的重复编译和打包。主要思路是将公共能力模块封装为公共 HSP,从而最小化 App Size。如下图所示:

图 10 多 HAP 工程模块示意图

说明

需要注意,在应用间共享的 HAR 包,原则上是不允许依赖 HSP 包,因为 HSP 包是专属于应用,和 bundleName 进行了绑定,一旦 HAR 包依赖于应用内 HSP,该 HAR 包就丢失了共享性,无法再给其他应用共享。

如上图所示,有 3 个 HAP 包(1 个 entry 和 2 个 feature),将公共的 HAR 包封装到 HSP 工程中,例如 common_wrap_hsp 和 feature_wrap_hsp。这两个 HSP 从严格意义上讲,不能称为模块,仅称为模块壳,用于合理放置模块在编译产物中的位置,不具备模块功能,不能共享,仅能在 App 应用内使用,依赖这些模块壳的模块也无法在应用间共享。

上述的模型通过 HSP 将 HAR 包合理分配到编译产物中,确保每个 HAR 包在 App 编译产物中仅出现一次,从而减小 App Size。模块壳数量不宜过多,否则可能影响安装速度和启动性能。

这两种模型都是理想模型,业务模型通常是两者的平衡态或组合。例如,某个共享库代码和资源较少,占用空间较小,如打印日志模块。将该模块编译进所有编译产物中,App Size 增加较少,同时性能较好。

不包含公共能力模块

这种应用较少,即使有的话也是一些规模较小的应用,可以参考单 HAP 的场景。

总结

应用开发者需根据技术架构选择适合的工程模块化模型。工程模块化模型需根据业务和技术架构演进而演进。根据诉求在 HAP、HAR 和 HSP 中选择使用。

对于具备独立运行和安装的模块只能选择 HAP 包,并将其作为 Feature 类型的 HAP 存在于 App 中;对于不具备独立特性部分,用户使用频率较少的模块,将其做成 HSP 按需加载模块存在于 App 中。对于需要共享的模块,只能采用 HAR 包,将其通过 OHPM 仓共享给其他工程使用。而 HAR 是静态共享库,在多 HAP 或者按需加载场景下,在编译后可能会在物理上存在多份,所以需要合理采用公共 HSP 模块壳,使 App Size 最小化。

http://www.xdnf.cn/news/13671.html

相关文章:

  • Jsoup解析商品详情时,有哪些常见的标签和属性?
  • 网络安全之CTF专题赛RE题解
  • Python训练营打卡Day49
  • 在QtCreator中使用GitHubCopilot
  • UML和模式应用(软件分析设计与建模期末复习)
  • 华为:eSight网管平台使用snmp纳管交换机
  • 利用Snowflake与SNP Glue揭示数据集成新潜力
  • Ozon欧亚仓网战略解析与中国卖家机遇
  • GUI丝滑教程-python tinker
  • Middleware
  • 力扣HOT100之技巧:287. 寻找重复数
  • 安装配置以太链钱包工具
  • 好用的培训教务管理系统哪个用的最多?
  • 68元开启智能硬件新纪元——明远智睿SSD2351开发板引领创新浪潮
  • java_api路径_@Parameter与@RequestParam区别
  • 【hadoop】疫情离线分析案例
  • 关于使用EasyExcel、 Vue3实现导入导出功能
  • 系统功耗管理
  • 25年春招:米哈游运维开发一面总结
  • Java反射机制深度解析与实战应用
  • C# net8生成excel,并设置列规则导出文件
  • 【Linux】Linux基础I/O
  • 织梦dedecms内容页调用seotitle标题的写法
  • Python训练营---DAY52
  • day01 ——Java基础入门
  • 135. Candy
  • C# 界面检测显示器移除并在可用显示器上显示
  • 关键领域软件测试新范式:如何在安全合规前提下提升效率?
  • 14.FTP传输分析
  • 云安全【阿里云ECS攻防】