
一、模块化设计在大型软件工程中一般会伴随多团队参与开发。各团队之间以弱耦合方式交互通过契约化接口定义业务之间的交互确保各团队业务独立发展互不影响实现快速迭代演进。模块化的核心价值将大型复杂系统拆解为更小、更易管理和理解的功能模块提高系统的可维护性和可扩展性每个功能模块都是独立单元具有清晰定义的接口和职责能够与其他模块交互以完成复杂任务在 HarmonyOS 应用开发中模块化是设计原则要求将应用程序拆分为多个功能模块每个模块负责特定功能或特性这些模块可以独立开发、编译和部署也可以在不同设备上灵活组合和调用二、应用程序包结构在进行模块化设计时需要考虑 HarmonyOS 的应用包结构选型。HarmonyOS 的应用包结构用于定义应用的组织方式。通过开发态、编译态、发布态阶段的应用程序包形态可以了解不同包类型的具体使用场景及规则。三、UIAbility应用组件设计HarmonyOS 应用的业务逻辑需要通过UIAbility 组件承载根据业务设备以及业务诉求不同需要考虑 UIAbility 组件的选择以及设计。在多设备背景下应用的形态不一定是传统移动设备上的单任务单窗口形式在一些场景下多任务多窗口的形态可以让用户获得更好的用户体验提升使用效率。手机设备上的多窗口场景举例应用类型场景说明笔记应用可让用户将信息从笔记的一页复制到另一页文档编辑应用可让用户同时打开编辑多个文档可让用户将内容从一个文档复制或移动到另一个文档导航/打车应用可以让导航后台运行回到主页查找新的位置信息或其它信息购物类临时客服界面可让用户通过任务管理快速从商品浏览页切换回到客服会话界面避免用户一层层打开查找支付/登录页面用户可以切换到其他页面查找并复制相关信息大屏设备上的多窗口场景举例应用类型场景说明视频播放器应用可让用户在观看播放内容的同时浏览其他可能感兴趣的视频列表电子邮件应用可让用户在撰写电子邮件的同时查看收到的邮件列表地址簿应用可让用户并排比较多个人员的联系信息阅读应用可让用户在查阅所有标题概要后打开多篇文章供稍后阅读多任务多窗口的设计影响对于类似独立应用的任务每个任务对应一个 UIAbility 组件实例每个任务可以单独显示一个窗口。用户可以在同一应用的不同任务间切换就像单独的应用一样在大屏设备上可以独立移动、调整大小、显示和隐藏应用窗口所以在进行功能设计时需考虑应用是否支持多任务多窗口这影响整体工程模块化结构。UIAbility 数量与模块选择单 Ability 的情况可以对应单窗口类型应用通过多实例实现的多任务应用通过指定实例实现的多任务应用举例普通游戏应用建议采用单 HAP来承载 UIAbility。多 Ability 的两种情况情况说明建议多窗口类型应用每个窗口对应不同的功能通过不同的 UIAbility 承载。如导航/打车应用中导航功能界面和主页属于不同的功能并且作为两个任务呈现给用户将该模块作为Feature 类型的 HAP承载相应的 UIAbility 组件应用的拓展功能如卡片和分享业务这些功能不会作为单独的任务和窗口形态运行。由于这些功能相对独立并且由系统提供的独立ExtensionAbility承载建议通过Feature 类型的 HAP承载单独的 ExtensionAbility 组件四、应用模块化选型应用架构旨在实现业务服务从技术角度思考业务实现方式工程模块化模型则基于技术架构对代码工程进行模块化选型。核心逻辑只有代码工程模型的技术选型合理了才能在包体积、性能、产品部署等取得一个最优的综合表现。业务模块与技术模块业务通常分为多个模块例如某购物软件包含主页导航商品详情购物车支付订单个人信息技术架构上这些业务模块表现为高内聚低耦合的模块Module。模块类型选择由于Entry 类型的 HAP 是工程默认存在的且不能存在多个所以主要考虑的模块类型有Feature 类型的 HAP 模块HAR 模块HSP 模块在技术架构中选择代码模块类型时需要根据业务特性和模块功能等多方面因素综合评估。几种常见业务场景场景说明共享模块某个功能模块业务模块或者能力模块需要在多个应用之间共享其代码逻辑和资源按需加载模块某个功能模块使用时由用户决定安装时机动态从应用市场下载安装使用多 HAP/HSP 引用相同 HAR 包的影响从性能角度出发需要减少多 HAP/HSP 对相同 HAR 包的引用五、共享模块对于大型软件不同业务和基础能力由多个团队开发各团队之间需要代码仓隔离。如果某个或若干个 HAR 工程模块由某个团队负责又想代码仓隔离可以在独立工程中开发这些 HAR通过公司私有的OHPM 仓发布和集成编译产物共享模块的定位这部分可以发布到 OHPM 仓的模块叫做共享模块可以将公共能力共享给多个应用使用如公司内部多个应用使用某个公共能力网络库或者将该公共能力封装成库给其他应用集成使用这种情况下这个模块也只能是 HAR 模块。六、按需加载模块随着应用业务的扩展应用为用户提供的功能不断增加。然而不是所有功能都是用户频繁使用的。根据用户运营报告的分析对于月活跃度较低的功能可以将其设计为按需加载模块。工作机制用户首次从应用市场安装时只会下载不包含按需加载模块的内容当用户需要使用特定功能时可以选择下载并安装相应的功能模块好处说明减少包体积用户从应用市场首次下载的应用不包含按需加载模块用户看到的包体积减少从而减少了用户下载和安装时间减少了用户等待时间减少系统资源应用安装之后所占用的空间也变少节省 ROM 空间应用启动时加载的特性少了节省 RAM 空间架构演进定义为按需加载的特性明确模块间耦合关系清晰有利于应用架构演进类型选择如果某个特性做成了按需加载模块该模块可以设计为Feature 类型的 HAP 或者 HSP。HAP 和 HSP 都可以实现按需加载区别在于Feature 类型的 HAP 可以包含 UIAbility 组件。结合前面的 UIAbility 应用组件设计以及业务是否需要按需加载从整体上可以划分两个大的场景场景说明单 HAP 场景如果只包含一个 UIAbility 组件包括 UIAbility 多实例/指定实例无需使用 ExtensionAbility 组件优先采用单 HAPEntry 类型的 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 包中的单例可能失效影响应用冷启动性能。示例说明工程内包含三个模块HAP 包作为应用主入口模块HSP 包作为应用主界面显示模块HAR_COMMON集成了所有通用工具类其中 funcResult 是 func 方法的执行结果当 HAP 和 HSP 模块同时引用 HAR_COMMON 模块时会破坏 HAR 的单例模式。因此HAP 和 HSP 模块在使用 HAR_COMMON 中的 funcResult 时会导致func 方法在两个模块加载时各执行一次从而增加文件的执行时间。优化方案仅从性能角度考虑可以采用以下方式进行修改以缩短冷启动阶段的耗时在多 HAP/HSP 引用相同 HAR 包的情况下如果 HSP 包和 HAR 包均能满足业务需求建议将 HSP 包改为 HAR 包。若使用的 HSP 为集成态 HSP可跳过该优化方案。性能对比数据方案阶段时长毫秒优化前使用 HSP 包3125优化后使用 HAR 代替 HSP853.9测试数据表明将 HSP 替换为 HAR 包后应用启动耗时显著缩短性能提升明显。八、单HAP工程对于单窗口应用的 APP 工程其仅包含一个Entry 类型的 HAP。划分的模块则根据是否有按需加载的需求来考虑采用 HAR 模块和 HSP 模块。场景一不包含按需加载模块对于不需要按需加载且仅包含一个 Entry 类型的 HAP 的 App可以直接全部采用 HAR进行开发设计。说明这里提到的仅有一个 HAP是指一种设备类型仅包含一个 HAP而不是指 .app 文件包中仅有一个 HAP。.app 文件包可以包含其他设备的 HAP 包例如手表和大屏设备的 HAP 包以支持多设备分发。设计成 HAR 包的优点优点说明节省成本全部编译进 HAP无额外的 HSP节省 HSP 的安装和加载成本编译优化HAR 在编译进 HAP 时可以利用 ArkTS 的语言特性和编译器功能做类型推断和编译优化架构简单代码工程架构简单后续演进较为灵活场景二包含按需加载模块在单 HAP 工程中实现按需加载功能时对应的组件需采用HSP 作为按需加载模块。核心权衡HAR 是静态共享库若多个 HAP 或 HSP 依赖同一份 HAR该 HAR 在应用内会被重复存储HSP 是动态共享库其安装和加载会有性能损失过多的 HSP 可能影响安装效率和 App 启动性能需考虑 App 占用空间是否受限及启动性能的敏感度根据业务需求在App Size 与启动性能之间做好平衡。说明这里提到的 App Size 指用户安装按需加载模块后应用的整体大小。方案一App Size 优先对于 App Size 优先的可以考虑将公共依赖的模块封装在一个 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 包。原因因为公共 HSP 包需要安装和加载所以会有一些性能损耗。对于启动性能敏感型的应用则将 hap_A 和 hsp_B 直接依赖于 har_C 和 har_D。代价最终编译产物里面有 2 个hap_A.hap 和 hsp_B.hsp但是这两个编译产物里面均会包含 har_C 和 har_DApp Size 会比采用公共 HSP 模型大。九、多HAP工程对于同一个设备类型如果要实现不同的独立功能模块并且相对独立以及具有单独的入口的功能特性建议做成一个独立特性的 HAP按需下载安装。此时一个 App 包中就会有多个 HAP 包其中有且仅有一个Entry 类型的 HAP其他的均是Feature 类型的 HAP。多 HAP 之间业务独立但是可能会有业务能力共享所以在进行模块化设计时需要根据是否具有公共能力来进行选择。场景一包含公共能力模块对于具备公共能力模块的工程和上述HAPHSP 组合是类似的需要考虑在App Size 与启动性能之间做平衡。方案一性能优先一般多 HAP 应用架构普适性采用以下模型除了产品组件中存在 HAP 包之外其余的是 HAR 包。特点编译产物中多个 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。说明需要注意在应用间共享的 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 最小化。