【鸿蒙开发】第三十一章 应用架构汇总

目录

1 应用架构模式

 2 分层架构设计

2.1 逻辑模型

2.2 开发模型

2.3 部署模型

3 应用模块化选型

3.1 共享模块

3.2 按需加载模块

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

3.3 单HAP工程

1、不包含按需加载模块

2、包含按需加载模块

3.4 多HAP工程

1、包含公共能力模块

2、不包含公共能力模块

3.5 总结


1 应用架构模式

  • 分层架构设计:将应用划分为产品定制层、基础特性层和公共能力层,可以降低层间的依赖性,从而提升代码的可维护性。通过分层架构设计进一步明确了每层的职责和层间的交互机制,为开发者呈现了一个清晰且结构化的开发框架。
  • 模块化设计:将应用分解为多个功能模块,其中每个模块负责执行特定的功能。通过模块化设计提高了代码的可理解性和可复用性,使应用的扩展和维护变得更为简便,同时降低了系统各部分之间的耦合度。

 2 分层架构设计

HarmonyOS应用的分层架构设计以一套代码工程为基础,旨在为华为的手机、2in1等1+8全场景设备提供支持,实现了“一次开发,多端部署”的开发理念。

HarmonyOS应用的分层架构主要包括三个层次:产品定制层、基础特性层和公共能力层,为开发者构建了一个清晰、高效、可扩展的设计架构。

2.1 逻辑模型

图1 分层架构逻辑模型

  • 产品定制层

    产品定制层专注于满足不同设备或使用场景(如应用)的个性化需求,包括UI设计、资源和配置,以及针对特定场景的交互逻辑和功能特性。

    产品定制层的功能模块独立运作,同时依赖基础特性层和公共能力层来实现具体功能。

    作为应用的入口,产品定制层是用户直接互动的界面。为满足特定产品需求,产品定制层可灵活地调整和扩展,从而满足各种使用场景。

  • 基础特性层

    基础特性层位于公共能力层之上,用于存放基础特性集合,例如相对独立的功能UI和业务逻辑实现。该层的每个功能模块都具有高内聚、低耦合、可定制的特点,以支持产品的灵活部署。

    基础特性层为上层的产品定制层提供稳健且丰富的基础功能支持,包括UI组件、基础服务等。同时依赖于下层的公共能力层为其提供通用功能和服务。

    为了增强系统的可扩展性和维护性,基础特性层将功能进行模块化处理。例如,一个应用的底部导航栏中的每个选项都可能是一个独立的业务模块。

  • 公共能力层

    公共功能层用于存放公共基础能力,集中了例如公共UI组件、数据管理、外部交互以及工具库等的共享功能。应用可以共享和调用这些公共能力。

    公共能力层为上层的基础特性层和产品定制层提供稳定可靠的功能支持,确保整个应用的稳定性和可维护性。

    公共能力层包括但不限于以下组成:

    • 公共UI组件:这些组件被设计成通用且高度可复用的,确保在不同的应用程序模块间保持一致的用户体验。公共UI组件提供了标准化且友好的界面,帮助开发者快速实现常见的用户交互需求,例如提示、警告、加载状态显示等,从而提高开发效率和用户满意度。
    • 数据管理:负责应用程序中数据的存储和访问,包括应用数据、系统数据等,提供了统一的数据管理接口,简化数据的读写操作。通过集中式的数据管理方式不仅使得数据的维护更为简单,而且能够保证数据的一致性和安全性。
    • 外部交互:负责应用程序与外部系统的交互,包括网络请求、文件I/O、设备I/O等,提供统一的外部交互接口,简化应用程序与外部系统的交互。开发者可以更为方便地实现应用程序的网络通信、数据存储和硬件接入等功能,从而加速开发流程并保证程序的稳定性和性能。
    • 工具库:提供一系列常用工具函数和类,例如字符串处理、日期时间处理、加密解密、数据压缩解压等,帮助开发者提高效率和代码质量。

2.2 开发模型

图2 分层架构开发模型

  • 产品定制层

    产品定制层的各个子目录会被编译成一个Entry类型的HAP,作为应用的主入口。该层主要针对跨多种设备,为各种设备形态集成相应的功能和特性。产品定制层被划分为多个功能模块,每个功能模块都针对特定的设备或使用场景设计,并根据具体的产品需求进行功能及交互的定制开发。

    说明

    • 在产品定制层,开发者可以从不同设备对应的应用UX设计和功能两个维度,结合具体的业务场景,选择一次编译生成相同或者不同的HAP(或其组合)
    • 通过使用定制多目标构建产物的定制功能,可以将应用所对应的HAP编译成各自的.app文件,用于上架到应用市场。
  • 基础特性层

    在基础特性层中,功能模块根据部署需求被分为两类。对于需要通过Ability承载的功能,可以设计为Feature类型的HAP,而对于不需要通过Ability承载的功能,根据是否需要实现按需加载,可以选择设计为HAR模块或者HSP模块,编译后对应HAR包或者HSP包。

  • 公共能力层

    公共能力层的各子目录将被编译成HAR包,而他们只能被产品定制层和基础特性层所依赖,不允许存在反向依赖。该层旨在提取模块化公共基础能力,为上层提供标准接口和协议,从而提高整体的复用率和开发效率。

2.3 部署模型

图3 分层架构部署模型(不同设备的定制)

应用程序(.app文件)在流水线或应用市场上被解包为n * Entry类型的HAP + n * Feature类型的HAP,根据设备类型和使用场景将应用部署到不同类型的设备上,实现多端的统一用户体验。

说明

当Entry类型的HAP和Feature类型的HAP被分发并部署到相应的设备时,他们所依赖的HSP也会一同被分发并部署到相应的设备上。

在部署模型中,每个Entry类型的HAP代表了应用的入口点,而Feature类型的HAP则包含了应用的特定功能模块。允许应用能够以模块化的方式适配和部署,从而满足不同设备和场景的需求。

该部署模型不仅优化了应用的组织结构,也为保持应用在各种设备和场景中的一致性提供了支持。通过按照设备类型和使用场景来区分和部署不同的HAP,能确保无论在何种设备或场景中,用户都能获得统一且高质量的体验。

3 应用模块化选型

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

3.1 共享模块

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

图1 多工程合作模式

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

3.2 按需加载模块

随着应用业务扩大,应用给用户提供的功能越来越多,但是并非所有的功能都是用户常用的,根据用户运营报告分析,对于某些用户月活比较低的特性,可以将该特性做成按需加载模块。用户首次从应用市场安装时,仅会下载不包含按需加载模块的内容,需要使用到对应功能时,由用户选择使用时下载安装对应的功能模块。

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

  • 减少包体积:用户从应用市场首次下载的应用不包含按需加载模块,用户看到的包体积减少,从而减少了用户下载和安装时间,减少了用户等待时间。
  • 减少系统资源:应用安装之后所占用的空间也变少(节省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包的影响

在应用开发的过程中,可以使用HSPHAR的共享包方式将同类的模块进行整合,用于实现多个模块或多个工程间共享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包中写入功能示例。

// har_common/src/main/ets/utils/Utils.ets
const LARGE_NUMBER = 100000000;
function func(): number {
  let count = 0;
  while (count < LARGE_NUMBER) {
    count++;
  }
  return count;
}
export let funcResult = func();

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

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

    HAP包引用HAR_COMMON包中的功能。

// entry/src/main/ets/pages/Index.ets
import { MainPage } from 'hsp_library';
import { funcResult } from 'har_common';

         HSP包引用HAR_COMMON包中的功能。

// hsp_library/src/main/ets/pages/MainPage.ets
import { funcResult } from 'har_common';
  • 使用HAP包和HAR包引用该HAR_COMMON包中的功能。

        HAP包引用HAR_COMMON包中的功能。

// entry/src/main/ets/pages/Index.ets
import { MainPage } from 'har_library';
import { funcResult } from 'har_common';

        HAR包引用HAR_COMMON包中的功能。

// har_library/src/main/ets/pages/MainPage.ets
import { funcResult } from '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包后,应用启动的阶段耗时明显缩短。

3.3 单HAP工程

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

1、不包含按需加载模块

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

说明

这里说的仅有一个HAP指的是一种设备类型仅有一个HAP,而不是.app文件包里面仅有一个HAP。因为.app里面可以包含其他设备的HAP包,如手表、大屏,进行多设备分发。

图6 非按需加载工程模型

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

设计成HAR包有如下好处:

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

2、包含按需加载模块

在单HAP工程内,如果要实现按需加载功能,那么对应的组件需要采用HSP作为按需加载组件模块。在这种情况下,由于HAR是静态共享库,多个HAP/HSP如果依赖于同一份HAR,则该HAR在应用内会被存在多份。而HSP是动态共享库,其安装和加载均会有一些性能损失(相比于HAR),所以过多的HSP可能会影响安装效率和App启动性能,需要考虑目前App占用空间(App Size)是否瓶颈以及对启动性能的敏感程度,根据业务实际情况,在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模型大。

3.4 多HAP工程

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

1、包含公共能力模块

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

性能优先

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

图9 多HAP工程模块示意图

从编译产物上看,多HAP之间是存在相同的HAR包的(如har_2、har_3、har_C、har_D、harE),这样App Size可能会比较大,对于App Size不是瓶颈点的应用,或者HAR包的大小比较小,对App Size的影响可控,可以采用这种模型,减少了动态加载的性能损耗。

App Size 优先

上述的重复HAR包,其本质的问题就是HAR包如何在HAP、HSP之间分布可以最小化App Size,减少HAR的重复编译打包,大的思路就是公共能力模块可以封装为公共HSP,最小化App Size,如下图所示:

图10 多HAP工程模块示意图

说明

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

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

上述的模型中通过HSP将HAR包合理地分配到编译产物中,使每个HAR包在App编译产物中仅存在一份,从而达到App Size最小,而模块壳不能过多,否则可能会影响安装速度和启动性能。

这两种模型都是理想模型,更多的业务模型是两者的平衡态或者两种模型的组合体,比如某个共享库本身代码和资源比较少,占用的空间不大,例如打印日志模块,那么将该模块编译进所有的编译产物中,增大的App Size比较少,同时性能会相对好一些。

2、不包含公共能力模块

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

3.5 总结

应用开发者需要根据自身技术架构选择适合的工程模块化模型,工程模块化模型也不是一成不变的,需要根据业务和技术架构的演进而演进。根据诉求在HAP、HAR和HSP三种类型中进行选择使用。

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

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值