当前位置:   article > 正文

聊聊鸿蒙中 HAR 和 HSP 的使用场景_鸿蒙hsp

鸿蒙hsp

软件开发的各个领域,都少不了共享库的存在, Java 中的 JAR ,Android 中的 AAR,Linux 中的动态链接库和静态链接库等等。鸿蒙也提供了两种共享库,一个是 HAR(Harmony Archive)静态共享库,另一个是 HSP(Harmony Shared Package)动态共享库

官网上提供了一张关于 HAR 和 HSP 使用场景的对比图片。

图片

这张图片给我的第一印象就是,HAR 会造成代码重复,增大应用体积,应该优先使用 HSP。正是基于这样的有失偏颇的理解,在实际开发中踩了一些坑。

这篇文章就来聊一聊 HAR 和 HSP 的正确使用场景。

什么是 HAP

在聊 HAR 和 HSP 之前,有一个概念必须弄清楚,就是 HAP 。

HarmonyOS 的核心理念之一是 一次开发,多端部署。多 HAP 就是为此而生的。

图片

这里引用鸿蒙白皮书中的图片,一个 App Pack 中的多个 HAP 可以自由组合在不同的设备上按需分发。

图片

entry 类型的 HAP 在 module.json5中的 type 类型是 entry

feature 类型的 HAP 在 module.json5 中的 type 类型 feature 。

最终编译生成的 .app 包在同一设备类型中必须有且只有一个 entry 类型的 HAP,feature 类型的 HAP 可以没有,也可以有一个或者多个,根据自己实际的业务需求来。

feature 类型的 HAP 的 module.json5 中有一个属性 deliveryWithInstall ,用来标识是否在用户下载应用时下载该 HAP,做到按需下载。

综上所述,多 HAP 的应用场景是两种。第一,多设备类型上不同功能模块的按需分发。第二,单设备上不同功能模块的按需下载,类似 Google Play 提供的 Dynamic Feature。

什么是 HAR

静态共享库 HAR 对标 Android 工程里的 JAR/AAR。

我们可以把公共工具类打包成 HAR ,也可以把单独的业务模块(携带 Page)打包成 HAR。只要是需要在团队内或者公开共享的 代码/Page/Component,都可以通过 HAR 的形式共享。

如果是公司内部共享,可以自行搭建 OHPM 私仓。

如果需要对外开放,可以发布到官方的 OpenHarmony三方库中心仓[1] 。

DevEco Studio 中新建 HAR 的操作路径是 New -> Module -> 选择 Static Library 。

HAR 无法在 /profile/main_pages.json 中声明 Page,但并不代表不能使用 Page,我们仍然可以在 Index.ets 中 export 出 Page,供外部使用或者跳转。同样,还可以 export 出 组件/类/接口/方法/资源等等。

什么是 HSP

在 DevEco Studio 中新建 HSP 的操作路径是 New -> Module -> 选择 Shared Library 。

和 HAR 不同的是,HSP 可以在 /profile/main_pages.json 中声明 Page。导出组件/类/方法/资源 和 HAR 保持一致。

HSP 不支持发布到 OH 中心仓,但支持发布到私仓。这里要注意,即使发布到私仓,也只是应用内共享,HSP 无法独立运行,而是和依赖它的 HAP 一起运行,且 HSP 的版本号必须和 HAP 的版本号保持一致。

用 HAR 还是用 HSP ?

结合官网对 HAR 的描述,不同的 HAP 模块引用同一个 HAR,该 HAR 在不同 HAP 中都会存在,造成代码和资源重复的问题。

如果多 HAP 是为了不同设备类型按需分发,HAR 被打包进每一个 HAP 肯定是合理的。但是如果多 HAP 仅仅是为了单个应用中功能模块的按需下载,确实会存在代码和资源重复的问题。

而 HSP 不会存在这样的问题,单设备同一个 HSP 在不同的 HAP 中是共享的。但也有一定限制,当前仅限于应用内共享,暂不支持应用间共享。

这样看起来,理想的模块化/组件化的开发模式是 根据业务划分多 HAP + 基础公共库 多 HSP 。

这样的推导建立在我对 HSP 的不完整认知上。HSP 其实是一种比较特殊的共享库,在安卓中很难找到一个完全对标的概念。我们从 HSP 使用中很容易遇到的一个问题来探索 HSP 的真实场景。

再看 HSP

我第一次使用 HSP 的时候,就把自己引入了一个大坑里。

utils 模块是一个 HAR 类型的工具库,提供了一个 单例 preference 工具类 PreferenceUtil :

  1. export class PreferenceUtil {
  2.   private preferences?: data_preferences.Preferences
  3.   private static instance: PreferenceUtil
  4.   private preferenceName = "HarmonyWorld"
  5.   private constructor() {
  6.   }
  7.   // 单例
  8.   public static getInstance(): PreferenceUtil {
  9.     if (!PreferenceUtil.instance) {
  10.       PreferenceUtil.instance = new PreferenceUtil()
  11.     }
  12.     return PreferenceUtil.instance
  13.   }
  14.   // 传入 context 对 preferences 进行初始化
  15.   init(context: Context, preferenceName: string) {
  16.     try {
  17.       this.preferences = data_preferences.getPreferencesSync(context, { name: preferenceName })
  18.     } catch (err) {
  19.       Log.error("Failed to get preferences. code =" + err.code + ", message =" + err.message)
  20.     }
  21.   }
  22.   ...
  23. }

我在 entry HAP 的 EntryAbility 中进行初始化:

  1. onWindowStageCreate(windowStage: window.WindowStage): void {
  2.   PreferenceUtil.getInstace.init(this.context, "HarmonyWorld)
  3. }

这样在 entry 模块中进行键值对的存储都是可以正常使用的。但是当我在另一个 HSP 模块中使用时,却遇到了一些问题。

简化的项目架构是这样的。

图片

我在 HSP 中引用 PreferenceUtil 无法获取之前存储的值,断点调试甚至发现 this.preferences 并没有被初始化,连 HSP 中的 PreferenceUtil 都是一个全新的对象,并不是之前的单例对象,在 entry 模块的初始化操作在这里没有产生任何作用。

由此可见,HAR 不仅在 HAP 中会有代码重复的问题,在 HSP 中也是。

没错,官网的图片,一开始我就看漏了细节。我提供一个重绘版本,用背景色区分一下 HAP 和 HSP。

图片

HAR 会被编译打包到所有依赖该模块的 HAP 和 HSP 。

HSP 在所有依赖该模块的 HAP 和 HSP 中共享。

这就直接拔高了 HSP 的地位,它的使用场景也显然并不是和 HAR 一样仅仅作为公共依赖库。我们再来看一下 HSP 的 module.json5 :

  1. {
  2.   "module": {
  3.     "name""hspOne",
  4.     "type""shared",
  5.     "description""$string:shared_desc",
  6.     "deviceTypes": [
  7.       "phone",
  8.       "tablet",
  9.       "2in1"
  10.     ],
  11.     "deliveryWithInstall"true,
  12.     "pages""$profile:main_pages"
  13.   }
  14. }

deliveryWithInstall ,可以配置安装时是否下载。这其实就满足了 Dynamic Feature 的特性,让 HSP 也可以设计为 按需加载 的模块。

同样具备按需加载能力,它和 HAP 最大的区别是,HAP 具备独立运行和安装的能力,可以使用 Ability,可以有独立任务栈,而 HSP 只能跟随宿主 HAP 进行分发。

所以 HSP 的真实使用场景其实是 不需要 Ability 能力的按需加载模块 。

HSP 模块过多也会存在一定问题,带来了额外的动态加载的性能损耗。

架构设计原则

厘清了 HAP/HSP/HAR 的特性之后,我们对项目的架构设计原则也有了初步的认识。

首先要确定项目采取 单 HAP 还是 多 HAP 。

如果需要多设备类型按需分发,可以选择多 HAP。

如果业务设计上可以是完全独立的功能模块,且需要 Ability 来提供独立的任务窗口,例如微信小程序,可以选择多 HAP。

在多 HAP 场景下,需要考虑相同 HAR 包重复引用的问题。在包体积可控的情况下,可以完全采用全 HAR 包的模式。如果比较介意包体积的话,可以考虑使用 HSP 来共享多 HAP 之间的公共模块。但是一般不是对所有公共模块都使用 HSP,而是提供一个 HSP 壳,来包装需要公共使用的 HAR 模块。

对于没有那么复杂的应用来说,单 HAP 已经完全可以满足需求。这时候 HSP 的作用就只有 按需加载 了。如果不需要按需加载,可以直接采用全 HAR 的模式,简单高效,无代码重复问题。如果需要按需加载,和多 HAP 模式一样,HAR 会被打包进依赖它的 HAP/HSP,处理方式同多 HAP,要么直接全 HAR,要么通过 HSP 壳包装公共 HAR。

以上设计模式并非什么金科玉律,HAR 重复引用带来的代码重复问题,HSP 动态加载带来的安装速度和性能问题,在实际开发过程中都会面临不同的取舍。

一张图总结一下:

图片

对于现阶段的鸿蒙,还存在一定的不确定性。HSP 不支持应用间共享,单应用虚拟机中多 HSP 之间的 HAR 重复看起来并没有什么意义,不知道官方后续会不会有什么修 改。另外大多数团队都不太会上来直接做一个完全复刻 Android 的版本,业务上也会做一些取舍。

如果你比较纠结模块设计方案的话,初期不妨从 单 HAP + 全 HAR 开始,简单高效,后期改造也比较方便。


公众号历史文章无法修改,我会把鸿蒙系列文章整理到语雀知识库 https://www.yuque.com/mobiledeveloper/harmonyos 

参考资料

[1]OpenHarmony三方库中心仓: https://ohpm.openharmony.cn/#/cn/home

转自:聊聊鸿蒙中 HAR 和 HSP 的使用场景

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/Guff_9hys/article/detail/985517
推荐阅读