网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。
一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!
在聊 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三方库中心仓 。
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
:
export class PreferenceUtil {
private preferences?: data_preferences.Preferences
private static instance: PreferenceUtil
private preferenceName = “HarmonyWorld”
private constructor() {
}
// 单例
public static getInstance(): PreferenceUtil {
if (!PreferenceUtil.instance) {
PreferenceUtil.instance = new PreferenceUtil()
}
return PreferenceUtil.instance
}
// 传入 context 对 preferences 进行初始化
init(context: Context, preferenceName: string) {
try {
this.preferences = data_preferences.getPreferencesSync(context, { name: preferenceName })
} catch (err) {
Log.error(“Failed to get preferences. code =” + err.code + “, message =” + err.message)
}
}
…
}
我在 entry
HAP 的 EntryAbility
中进行初始化:
onWindowStageCreate(windowStage: window.WindowStage): void { PreferenceUtil.getInstace.init(this.context, "HarmonyWorld) }
这样在 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
:
{
“module”: {
“name”: “hspOne”,
“type”: “shared”,
“description”: “
s
t
r
i
n
g
:
s
h
a
r
e
d
d
e
s
c
"
,
"
d
e
v
i
c
e
T
y
p
e
s
"
:
[
"
p
h
o
n
e
"
,
"
t
a
b
l
e
t
"
,
"
2
i
n
1
"
]
,
"
d
e
l
i
v
e
r
y
W
i
t
h
I
n
s
t
a
l
l
"
:
t
r
u
e
,
"
p
a
g
e
s
"
:
"
string:shared_desc", "deviceTypes": [ "phone", "tablet", "2in1" ], "deliveryWithInstall": true, "pages": "
string:shareddesc","deviceTypes":["phone","tablet","2in1"],"deliveryWithInstall":true,"pages":"profile:main_pages”
}
}
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 开始,简单高效,后期改造也比较方便。
最后
有很多小伙伴不知道学习哪些鸿蒙开发技术?不知道需要重点掌握哪些鸿蒙应用开发知识点?而且学习时频繁踩坑,最终浪费大量时间。所以有一份实用的鸿蒙(HarmonyOS NEXT)资料用来跟着学习是非常有必要的。
这份鸿蒙(HarmonyOS NEXT)资料包含了鸿蒙开发必掌握的核心知识要点,内容包含了(**ArkTS、ArkUI开发组件、Stage模型、多端部署、分布式应用开发、音频、视频、WebGL、OpenHarmony****多媒体技术、Napi组件、OpenHarmony内核、Harmony南向开发、鸿蒙项目实战等等)鸿蒙(HarmonyOS NEXT)**技术知识点。
网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。
一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!
网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。
一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!