鸿蒙开发基础知识
一 应用程序包概述
1 应用与应用程序包
用户应用程序泛指运行在设备的操作系统之上,为用户提供特定服务的程序,简称“应用”。
一个应用所对应的软件包文件,称为“应用程序包”。
这个做客户端的同学们都应该知道吧。
当前系统提供了应用程序包开发、安装、查询、更新、卸载的管理机制,便于开发者开发和管理应用。同时,系统还屏蔽了不同的芯片平台的差异(包括x86/ARM,32位/64位等),应用程序包在不同的芯片平台都能够安装运行,这使得开发者可以聚焦于应用的功能实现。
2 应用的多Module设计机制
一个应用通常会包含多种功能,将不同的功能特性按模块来划分和管理是一种良好的设计方式。在开发过程中,我们可以将每个功能模块作为一个独立的Module进行开发,Module中可以包含源代码、资源文件、第三方库、配置文件等,每一个Module可以独立编译,实现特定的功能。这种模块化、松耦合的应用管理方式有助于应用的开发、维护与扩展。
一个应用往往需要适配多种设备类型,在采用多Module设计的应用中,每个Module都会标注所支持的设备类型。有些Module支持全部类型的设备,有些Module只支持某一种或几种型的设备(比如平板),那么在应用市场分发应用包时,也能够根据设备类型做精准的筛选和匹配,从而将不同的包合理的组合和部署到对应的设备上。
3 Module类型
3.1 Ability类型:功能和特性。编译后生成.hap(Harmony Ability Package),HAP包可独立运行和安装,一个应用可有一个或多个HAP包,是基本单位。
Ability类型又分两种类型:
- entry类型:主Module,包含应用的入口界面、入口图标和主功能特性,编译后生成entry类型的HAP。每一个应用分发到同一类型的设备上的应用程序包,只能包含唯一一个entry类型的HAP。
- feature类型:应用的动态特性模块,编译后生成feature类型的HAP。一个应用中可以包含一个或多个feature类型的HAP,也可以不包含。
3.2 Library类型:代码和资源的共享。Library类型的Module分为Static和Shared两种类型,编译后会生成共享包。
- Static Library:静态共享库。编译后会生成一个以.har为后缀的文件,即静态共享包HAR(Harmony Archive)。
- Shared Library:动态共享库。编译后会生成一个以.hsp为后缀的文件,即动态共享包HSP(Harmony Shared Package)。
说明
实际上,Shared Library编译后除了会生成一个.hsp文件,还会生成一个.har文件。这个.har文件中包含了HSP对外导出的接口,
应用中的其他模块需要通过.har文件来引用HSP的功能。为了表述方便,我们通常认为Shared Library编译后生成HSP。
HAR与HSP两种共享包的主要区别体现在:
![]()
图1 HAR和HSP在APP包中的形态示意图
![]()
二 应用程序包结构
1 Stage模型应用程序包结构
1.1 开发态包结构
![]()
工程结构主要包含的文件类型及用途如下:
说明
AppScope目录由DevEco Studio自动生成,不可更改。
Module目录名称可以由DevEco Studio自动生成(比如entry、library等),也可以自定义。为了便于说明,下表中统一采用Module_name表示。
![]()
1.2 编译态包结构
不同类型的Module编译后会生成对应的HAP、HAR、HSP等文件,开发态视图与编译态视图的对照关系如下:
图2 开发态与编译态的工程结构视图
![]()
从开发态到编译态,Module中的文件会发生如下变更:
- ets目录:ArkTS源码编译生成.abc文件。
- resources目录:AppScope目录下的资源文件会合入到Module下面资源目录中,如果两个目录下的存在重名文件,编译打包后只会保留AppScope目录下的资源文件。
- module配置文件:AppScope目录下的app.json5文件字段会合入到Module下面的module.json5文件之中,编译后生成HAP或HSP最终的module.json文件。
说明
在编译HAP和HSP时,会把他们所依赖的HAR直接编译到HAP和HSP中。
1.3 发布态包结构
每个应用中至少包含一个.hap文件,可能包含若干个.hsp文件、也可能不含,一个应用中的所有.hap与.hsp文件合在一起称为Bundle,其对应的bundleName是应用的唯一标识(详见app.json5配置文件中的bundleName标签)。
当应用发布上架到应用市场时,需要将Bundle打包为一个.app后缀的文件用于上架,这个.app文件称为App Pack(Application Package),与此同时,DevEco Studio工具自动会生成一个pack.info文件。pack.info文件描述了App Pack中每个HAP和HSP的属性,包含APP中的bundleName和versionCode信息、以及Module中的name、type和abilities等信息。
说明
App Pack是发布上架到应用市场的基本单元,但是不能在设备上直接安装和运行。
在应用签名、云端分发、端侧安装时,都是以HAP/HSP为单位进行签名、分发和安装的。
图3 编译发布与上架部署流程图
![]()
选择合适的包类型
HAP、HAR、HSP三者的功能和使用场景总结对比如下:
![]()
HAP、HSP、HAR支持的规格对比如下,其中“√”表示是,“×”表示否。
开发者可以根据实际场景所需的能力,选择相应类型的包进行开发。在后续的章节中还会针对如何使用HAP、HAR、HSP分别展开详细介绍。
![]()
说明
HAR虽然不支持在配置文件中声明pages页面,但是可以包含pages页面,并通过命名路由的方式进行跳转。
由于HSP仅支持应用内共享,如果HAR依赖了HSP,则该HAR文件仅支持应用内共享,不支持发布到二方仓或三方仓供其他应用使用,否则会导致编译失败。
HAR和HSP均不支持循环依赖,也不支持依赖传递。
2 FA模型应用程序包结构(略)
三 应用程序包开发与使用
1 HAP Harmony Ability Package
HAP(Harmony Ability Package)是应用安装和运行的基本单元。HAP包是由代码、资源、第三方库、配置文件等打包生成的模块包,其主要分为两种类型:entry和feature。
- entry:应用的主模块,作为应用的入口,提供了应用的基础功能。
- feature:应用的动态特性模块,作为应用能力的扩展,可以根据用户的需求和设备类型进行选择性安装。
应用程序包:一个entry包,可选多个功能性的feature包。
1.1 使用场景
- 单HAP场景:如果只包含UIAbility组件,无需使用ExtensionAbility组件,优先采用单HAP(即一个entry包)来实现应用开发。虽然一个HAP中可以包含一个或多个UIAbility组件,为了避免不必要的资源加载,推荐采用“一个UIAbility+多个页面”的方式。
- 多HAP场景:如果应用的功能比较复杂,需要使用ExtensionAbility组件,可以采用多HAP(即一个entry包+多个feature包)来实现应用开发,每个HAP中包含一个UIAbility组件或者一个ExtensionAbility组件。在这种场景下,可能会存在多个HAP引用相同的库文件,导致重复打包的问题。
1.2 约束限制
- 不支持导出接口和ArkUI组件,给其他模块使用。
- 多HAP场景下,App Pack包中同一设备类型的所有HAP中必须有且只有一个Entry类型的HAP,Feature类型的HAP可以有一个或者多个,也可以没有。
- 多HAP场景下,同一应用中的所有HAP的配置文件中的bundleName、versionCode、versionName、minCompatibleVersionCode、debug、minAPIVersion、targetAPIVersion、apiReleaseType相同,同一设备类型的所有HAP对应的moduleName标签必须唯一。HAP打包生成App Pack包时,会对上述参数配置进行校验。
- 多HAP场景下,同一应用的所有HAP、HSP的签名证书要保持一致。上架应用市场是以App Pack形式上架,应用市场分发时会将所有HAP从App Pack中拆分出来,同时对其中的所有HAP进行重签名,这样保证了所有HAP签名证书的一致性。在调试阶段,开发者通过命令行或DevEco Studio将HAP安装到设备上时,要保证所有HAP签名证书一致,否则会出现安装失败的问题。
1.3 创建
下面简要介绍如何通过DevEco Studio新建一个HAP模块。
-
-
在工程目录上单击右键,选择New > Module。
-
在弹出的对话框中选择Empty Ability模板,单击Next。
-
在Module配置界面,配置Module name,选择Module Type和Device Type,然后单击Next。
-
在Ability配置界面,配置Ability name,然后单击Finish完成创建。
1.4 开发
-
HAP中支持添加UIAbility组件或ExtensionAbility组件,添加pages页面。具体操作可参考
应用/服务开发。
-
1.5 调试
通过DevEco Studio编译打包,生成单个或者多个HAP,即可基于HAP进行调试。如需根据不同的部署环境、目标人群、运行环境等,将同一个HAP定制编译为不同版本,请参见
定制编译指导。
开发者可以采用DevEco Studio或者hdc工具进行调试:
// 安装、更新,多HAP可以指定多个文件路径
hdc install entry.hap feature.hap
// 执行结果
install bundle successfully.
// 卸载
hdc uninstall com.example.myapplication
// 执行结果
uninstall bundle successfully.
先执行hdc shell,再使用bm工具安装、更新HAP。
HAP的文件路径为真机上的文件路径,命令参考如下:
// 先执行hdc shell才能使用bm工具
hdc shell
// 安装、更新,多HAP可以指定多个文件路径
bm install -p /data/app/entry.hap /data/app/feature.hap
// 执行结果
install bundle successfully.
// 卸载
bm uninstall -n com.example.myapplication
// 执行结果
uninstall bundle successfully.
完成HAP安装或更新后,即可参考相关调试命令进行
调试。
2 HAR Harmony Archive
HAR(Harmony Archive)是静态共享包,可以包含代码、C++库、资源和配置文件。通过HAR可以实现多个模块或多个工程共享ArkUI组件、资源等相关代码。
2.1 使用场景
- 作为二方库,发布到OHPM私仓,供公司内部其他应用使用。
- 作为三方库,发布到OHPM中心仓,供其他应用使用。
2.2 约束限制
- HAR不支持在设备上单独安装/运行,只能作为应用模块的依赖项被引用。
- HAR不支持在配置文件中声明UIAbility组件与ExtensionAbility组件。
- HAR不支持在配置文件中声明pages页面,但是可以包含pages页面,并通过命名路由的方式进行跳转。
- HAR不支持引用AppScope目录中的资源。在编译构建时,AppScope中的内容不会打包到HAR中,因此会导致HAR资源引用失败。
- HAR可以依赖其他HAR,但不支持循环依赖,也不支持依赖传递。
2.3 创建
通过DevEco Studio创建一个HAR模块,详见
创建库模块。
2.4 开发
介绍如何导出HAR的ArkUI组件、接口、资源,供其他应用或当前应用的其他模块引用。
Index.ets文件是HAR导出声明文件的入口,HAR需要导出的接口,统一在Index.ets文件中导出。Index.ets文件是DevEco Studio默认自动生成的,用户也可以自定义,在模块的oh-package.json5文件中的main字段配置入口声明文件,配置如下所示:
2.4.1 导出ArkUI组件
ArkUI组件的导出方式与ts的导出方式一致,通过export导出ArkUI组件,示例如下:
// library/src/main/ets/components/mainpage/MainPage.ets
@Component
export struct MainPage {
@State message: string = 'HAR MainPage';
build() {
Column() {
Row() {
Text(this.message)
.fontSize(32)
.fontWeight(FontWeight.Bold)
}
.margin({ top: '32px' })
.height(56)
.width('624px')
Flex({ justifyContent: FlexAlign.Center, alignItems: ItemAlign.Center, alignContent: FlexAlign.Center }) {
Column() {
Image($r('app.media.pic_empty')).width('33%')
Text($r('app.string.empty'))
.fontSize(14)
.fontColor($r('app.color.text_color'))
}
}.width('100%')
.height('90%')
}
.width('100%')
.height('100%')
.backgroundColor($r('app.color.page_background'))
}
}
HAR对外暴露的接口,在Index.ets导出文件中声明如下所示:
// library/Index.ets
export { MainPage } from './src/main/ets/components/mainpage/MainPage';
2.4.2 导出ts类和方法
通过export导出ts类和方法,支持导出多个ts类和方法,示例如下所示:
// library/src/main/ts/test.ets
export class Log {
static info(msg: string) {
console.info(msg);
}
}
export function func() {
return 'har func';
}
export function func2() {
return 'har func2';
}
HAR对外暴露的接口,在Index.ets导出文件中声明如下所示:
// library/Index.ets
export { Log } from './src/main/ts/test';
export { func } from './src/main/ts/test';
export { func2 } from './src/main/ts/test';
2.4.3 导出native方法
在HAR中也可以包含C++编写的so。对于so中的native方法,HAR通过以下方式导出,以导出libnative.so的加法接口add为例:
// library/src/main/ets/utils/nativeTest.ts
import native from 'liblibrary.so';
export function nativeAdd(a: number, b: number): number {
let result: number = native.add(a, b);
return result;
}
HAR对外暴露的接口,在Index.ets导出文件中声明如下所示:
// library/Index.ets
export { nativeAdd } from './src/main/ets/utils/nativeTest';
2.4.4 资源
在编译构建HAP时,DevEco Studio会从HAP模块及依赖的模块中收集资源文件,如果不同模块下的资源文件出现重名冲突时,DevEco Studio会按照以下优先级进行覆盖(优先级由高到低):
- AppScope(仅API9的Stage模型支持)。
- HAP包自身模块。
- 依赖的HAR模块,如果依赖的多个HAR之间有资源冲突,会按照工程oh-package.json5中dependencies下的依赖顺序进行覆盖,依赖顺序在前的优先级较高。例如下方示例中dayjs和lottie中包含同名文件时,会优先使用dayjs中的资源。
说明
如果在AppScope/HAP模块/HAR模块的国际化目录中配置了资源,在相同的国际化限定词下,合并的优先级也遵循上述规则。同时,国际化限定词中配置的优先级高于在base中的配置。如:在AppScope的base中配置了资源字段,在HAR模块的en_US中配置了同样的资源字段,则在en_US的使用场景中,会更优先使用HAR模块中配置的资源字段。
// oh-package.json5
{
"dependencies": {
"dayjs": "^1.10.4",
"lottie": "^2.0.0"
}
}
2.5 使用
介绍如何配置HAR依赖,并引用HAR的ArkUI组件、接口、资源。
2.5.1 引用HAR的ArkUI组件
HAR的依赖配置成功后,可以引用HAR的ArkUI组件。ArkUI组件的导入方式与ts的导入方式一致,通过import引入HAR导出的ArkUI组件,示例如下所示:
// entry/src/main/ets/pages/IndexSec.ets
import { MainPage } from 'library';
@Entry
@Component
struct IndexSec {
build() {
Row() {
// 引用HAR的ArkUI组件
MainPage()
}
.height('100%')
}
}
2.5.2 引用HAR的ts类和方法
通过import引用HAR导出的ts类和方法,示例如下所示:
// entry/src/main/ets/pages/Index.ets
import { Log } from 'library';
import { func } from 'library';
@Entry
@Component
struct Index {
@State message: string = 'Hello World';
build() {
Column() {
Text(this.message)
.fontFamily('HarmonyHeiTi')
.fontWeight(FontWeight.Bold)
.fontSize(32)
.fontWeight(700)
.fontColor($r('app.color.text_color'))
.textAlign(TextAlign.Start)
.margin({ top: '32px' })
.width('624px')
//引用HAR的ts类和方法
Button($r('app.string.button'))
.id('button')
.height(48)
.width('624px')
.margin({ top: '4%' })
.type(ButtonType.Capsule)
.fontFamily('HarmonyHeiTi')
.borderRadius($r('sys.float.ohos_id_corner_radius_button'))
.backgroundColor($r('app.color.button_background'))
.fontColor($r('sys.color.ohos_id_color_foreground_contrary'))
.fontSize($r('sys.float.ohos_id_text_size_button1'))
.onClick(() => {
// 引用HAR的类和方法
Log.info('har msg');
this.message = 'func return: ' + func();
})
}
.width('100%')
.backgroundColor($r('app.color.page_background'))
.height('100%')
}
}
2.5.3 引用HAR的native方法
通过import引用HAR导出的native方法,示例如下所示:
// entry/src/main/ets/pages/Index.ets
import { nativeAdd } from 'library';
@Entry
@Component
struct Index {
@State message: string = 'Hello World';
build() {
Column() {
Text(this.message)
.fontFamily('HarmonyHeiTi')
.fontWeight(FontWeight.Bold)
.fontSize(32)
.fontWeight(700)
.fontColor($r('app.color.text_color'))
.textAlign(TextAlign.Start)
.margin({ top: '32px' })
.width('624px')
//引用HAR的native方法
Button($r('app.string.native_add'))
.id('nativeAdd')
.height(48)
.width('624px')
.margin({ top: '4%', bottom: '6%' })
.type(ButtonType.Capsule)
.fontFamily('HarmonyHeiTi')
.borderRadius($r('sys.float.ohos_id_corner_radius_button'))
.backgroundColor($r('app.color.button_background'))
.fontColor($r('sys.color.ohos_id_color_foreground_contrary'))
.fontSize($r('sys.float.ohos_id_text_size_button1'))
.onClick(() => {
this.message = 'result: ' + nativeAdd(1, 2);
})
}
.width('100%')
.backgroundColor($r('app.color.page_background'))
.height('100%')
}
}
2.5.4 引用HAR的资源
通过$r引用HAR中的资源,例如在HAR模块的src/main/resources里添加字符串资源(在string.json中定义,name:hello_har)和图片资源(icon_har.png),然后在Entry模块中引用该字符串和图片资源的示例如下所示:
// entry/src/main/ets/pages/Index.ets
@Entry
@Component
struct Index {
@State message: string = 'Hello World';
build() {
Column() {
// 引用HAR的字符串资源
Text($r('app.string.hello_har'))
.id('stringHar')
.fontFamily('HarmonyHeiTi')
.fontColor($r('app.color.text_color'))
.fontSize(24)
.fontWeight(500)
.margin({ top: '40%' })
List() {
ListItem() {
// 引用HAR的图片资源
Image($r('app.media.icon_har'))
.id('iconHar')
.borderRadius('48px')
}
.margin({ top: '5%' })
.width('312px')
}
.alignListItem(ListItemAlign.Center)
}
.width('100%')
.backgroundColor($r('app.color.page_background'))
.height('100%')
}
}
2.6 编译
HAR可以作为二方库和三方库提供给其他应用使用,如果需要对代码资产进行保护时,建议开启混淆能力。
混淆能力开启后,DevEco Studio在构建HAR时,会对代码进行编译、混淆及压缩处理,保护代码资产。
说明
仅Stage模型的ArkTS工程支持混淆。
对于API 10及以上版本,HAR模块默认开启混淆能力,可以在HAR模块的build-profile.json5文件中的ruleOptions字段下的enable进行设置,配置如下所示:
{
"apiType": "stageMode",
"buildOption": {
},
"buildOptionSet": [
{
"name": "release",
"arkOptions": {
"obfuscation": {
"ruleOptions": {
"enable": true,
"files": [
"./obfuscation-rules.txt"
]
},
"consumerFiles": [
"./consumer-rules.txt"
]
}
}
},
],
"targets": [
{
"name": "default"
}
]
}
编译生成TS文件
说明
在har中使用Sendable class时,开启该配置。
har模块中arkts文件编译后,默认产物为js文件,想要将产物修改为ts文件,可以在har模块src/main目录下的module.json5文件中的"metadata"字段下的"UseTsHar"进行设置,配置如下所示:
{
"module": {
"name": "TsClosedHar",
"type": "har",
"deviceTypes": [
"default",
"tablet",
"2in1"
],
"metadata": [
{
"name": "UseTsHar",
"value": "true"
}
]
}
}
2.7 发布
3 HSP Harmony Shared Package
HSP(Harmony Shared Package)是动态共享包,可以包含代码、C++库、资源和配置文件,通过HSP可以实现代码和资源的共享。HSP不支持独立发布,而是跟随其宿主应用的APP包一起发布,与宿主应用同进程,具有相同的包名和生命周期。
说明
应用内HSP:在编译过程中与应用包名(bundleName)强耦合,只能给某个特定的应用使用。
集成态HSP:构建、发布过程中,不与特定的应用包名耦合;使用时,工具链支持自动将集成态HSP的包名替换成宿主应用包名。
3.1 使用场景
多个HAP/HSP共用的代码和资源放在同一个HSP中,可以提高代码、资源的可重用性和可维护性,同时编译打包时也只保留一份HSP代码和资源,能够有效控制应用包大小。
同一个组织内部的多个应用之间,可以使用集成态HSP实现代码和资源的共享。
3.2 约束限制
HSP不支持在设备上单独安装/运行,需要与依赖该HSP的HAP一起安装/运行。HSP的版本号必须与HAP版本号一致。
HSP可以依赖其他HAR或HSP,但不支持循环依赖,也不支持依赖传递。
集成态HSP需要API12及以上版本,使用标准化的OHMUrl格式。
3.3 创建
通过DevEco Studio创建一个HSP模块,详见
创建HSP模块,我们以创建一个名为library的HSP模块为例。基本的工程目录结构如下:
MyApplication
├── library
│ ├── src
│ │ └── main
│ │ ├── ets
│ │ │ └── pages
│ │ │ └── index.ets
│ │ ├── resources
│ │ └── module.json5
│ ├── oh-package.json5
│ ├── index.ets
│ └── build-profile.json5 //模块级
└── build-profile.json5 //工程级
3.4 开发
介绍如何导出HSP的ArkUI组件、接口、资源,供应用内的其他HAP/HSP引用。
3.4.1 导出ArkUI组件
ArkUI组件可以通过export导出,例如:
// library/src/main/ets/components/MyTitleBar.ets
@Component
export struct MyTitleBar {
build() {
Row() {
Text($r('app.string.library_title'))
.id('library')
.fontFamily('HarmonyHeiTi')
.fontWeight(FontWeight.Bold)
.fontSize(32)
.fontColor($r('app.color.text_color'))
}
.width('100%')
}
}
对外暴露的接口,需要在入口文件index.ets中声明:
// library/index.ets
export { MyTitleBar } from './src/main/ets/components/MyTitleBar';
3.4.2 导出ts类和方法
通过export导出ts类和方法,例如:
// library/src/main/ets/utils/test.ets
export class Log {
static info(msg: string): void {
console.info(msg);
}
}
export function add(a: number, b: number): number {
return a + b;
}
export function minus(a: number, b: number): number {
return a - b;
}
对外暴露的接口,需要在入口文件index.ets中声明:
// library/index.ets
export { Log, add, minus } from './src/main/ets/utils/test';
3.4.3 导出native方法
在HSP中也可以包含C++编写的so。对于so中的native方法,HSP通过间接的方式导出,以导出liblibrary.so的乘法接口multi为例:
// library/src/main/ets/utils/nativeTest.ets
export function nativeMulti(a: number, b: number): number {
let result: number = native.multi(a, b);
return result;
}
对外暴露的接口,需要在入口文件index.ets中声明:
// library/index.ets
export { nativeMulti } from './src/main/ets/utils/nativeTest';
3.4.4 通过$r访问HSP中的资源
在组件中,经常需要使用字符串、图片等资源。HSP中的组件需要使用资源时,一般将其所用资源放在HSP包内,而非放在HSP的使用方处,以符合高内聚低耦合的原则。
在工程中,常通过$r/$rawfile的形式引用应用资源。可以用$r/$rawfile访问本模块resources目录下的资源,如访问resources目录下定义的图片src/main/resources/base/media/example.png时,可以用$r("app.media.example")。有关$r/$rawfile的详细使用方式,请参阅文档
资源分类与访问中“资源访问-应用资源”小节。
不推荐使用相对路径的方式,容易引用错误路径。例如:
当要引用上述同一图片资源时,在HSP模块中使用Image("../../resources/base/media/example.png"),实际上该Image组件访问的是HSP调用方(如entry)下的资源entry/src/main/resources/base/media/example.png。
// library/src/main/ets/pages/Index.ets
// 正确用例
Image($r('app.media.example'))
.id('example')
.borderRadius('48px')
// 错误用例
Image("../../resources/base/media/example.png")
.id('example')
.borderRadius('48px')
3.4.5 导出HSP中的资源
跨包访问HSP内资源时,推荐实现一个资源管理类,以封装对外导出的资源。采用这种方式,具有如下优点:
HSP开发者可以控制自己需要导出的资源,不需要对外暴露的资源可以不用导出。
使用方无须感知HSP内部的资源名称。当HSP内部的资源名称发生变化时,也不需要使用方跟着修改。
其具体实现如下:
将需要对外提供的资源封装为一个资源管理类:
// library/src/main/ets/ResManager.ets
export class ResManager{
static getPic(): Resource{
return $r('app.media.pic');
}
static getDesc(): Resource{
return $r('app.string.shared_desc');
}
}
对外暴露的接口,需要在入口文件index.ets中声明:
// library/index.ets
export { ResManager } from './src/main/ets/ResManager';
3.5 使用
介绍如何引用HSP中的接口,以及如何通过页面路由实现HSP的pages页面跳转与返回。
3.5.1 引用HSP中的接口
要使用HSP中的接口,首先需要在使用方的oh-package.json5中配置对它的依赖,详见
引用动态共享包。
依赖配置成功后,就可以像使用HAR一样调用HSP的对外接口了。例如,上面的library已经导出了下面这些接口:
// library/index.ets
export { Log, add, minus } from './src/main/ets/utils/test';
export { MyTitleBar } from './src/main/ets/components/MyTitleBar';
export { ResManager } from './src/main/ets/ResManager';
export { nativeMulti } from './src/main/ets/utils/nativeTest';
在使用方的代码中,可以这样使用:
// entry/src/main/ets/pages/index.ets
import { Log, add, MyTitleBar, ResManager, nativeMulti } from 'library';
import { BusinessError } from '@ohos.base';
import Logger from '../logger/Logger';
import router from '@ohos.router';
const TAG = 'Index';
@Entry
@Component
struct Index {
@State message: string = '';
build() {
Column() {
List() {
ListItem() {
MyTitleBar()
}
.margin({ left: '35px', top: '32px' })
ListItem() {
Text(this.message)
.fontFamily('HarmonyHeiTi')
.fontSize(18)
.textAlign(TextAlign.Start)
.width('100%')
.fontWeight(FontWeight.Bold)
}
.width('685px')
.margin({ top: 30, bottom: 10 })
ListItem() {
// ResManager返回的Resource对象,可以传给组件直接使用,也可以从中取出资源来使用
Image(ResManager.getPic())
.id('image')
.borderRadius('48px')
}
.width('685px')
.margin({ top: 10, bottom: 10 })
.padding({ left: 12, right: 12, top: 4, bottom: 4 })
ListItem() {
Text($r('app.string.add'))
.fontSize(18)
.textAlign(TextAlign.Start)
.width('100%')
.fontWeight(500)
.height('100%')
}
.id('add')
.borderRadius(24)
.width('685px')
.height('84px')
.backgroundColor($r('sys.color.ohos_id_color_foreground_contrary'))
.margin({ top: 10, bottom: 10 })
.padding({ left: 12, right: 12, top: 4, bottom: 4 })
.onClick(() => {
Log.info('add button click!');
this.message = 'result: ' + add(1, 2);
})
ListItem() {
Text($r('app.string.get_string_value'))
.fontSize(18)
.textAlign(TextAlign.Start)
.width('100%')
.fontWeight(500)
.height('100%')
}
.id('getStringValue')
.borderRadius(24)
.width('685px')
.height('84px')
.backgroundColor($r('sys.color.ohos_id_color_foreground_contrary'))
.margin({ top: 10, bottom: 10 })
.padding({ left: 12, right: 12, top: 4, bottom: 4 })
.onClick(() => {
// 先通过当前上下文获取hsp模块的上下文,再获取hsp模块的resourceManager,然后再调用resourceManager的接口获取资源
getContext()
.createModuleContext('library')
.resourceManager
.getStringValue(ResManager.getDesc())
.then(value => {
Logger.info(TAG, `getStringValue is ${value}`);
this.message = 'getStringValue is ' + value;
})
.catch((err: BusinessError) => {
Logger.info(TAG, `getStringValue promise error is ${err}`);
});
})
ListItem() {
Text($r('app.string.native_multi'))
.fontSize(18)
.textAlign(TextAlign.Start)
.width('100%')
.fontWeight(500)
.height('100%')
}
.id('nativeMulti')
.borderRadius(24)
.width('685px')
.height('84px')
.backgroundColor($r('sys.color.ohos_id_color_foreground_contrary'))
.margin({ top: 10, bottom: 10 })
.padding({ left: 12, right: 12, top: 4, bottom: 4 })
.onClick(() => {
Log.info('nativeMulti button click!');
this.message = 'result: ' + nativeMulti(3, 4);
})
}
.alignListItem(ListItemAlign.Center)
}
.width('100%')
.backgroundColor($r('app.color.page_background'))
.height('100%')
}
}
3.5.2 页面路由跳转
若开发者想在entry模块中,添加一个按钮跳转至library模块中的menu页面(路径为:library/src/main/ets/pages/menu.ets),那么可以在使用方的代码(entry模块下的Index.ets,路径为:entry/src/main/ets/pages/Index.ets)里这样使用:
import { Log, add, MyTitleBar, ResManager, nativeMulti } from 'library';
import { BusinessError } from '@ohos.base';
import Logger from '../logger/Logger';
import router from '@ohos.router';
const TAG = 'Index';
@Entry
@Component
struct Index {
@State message: string = '';
build() {
Column() {
List() {
ListItem() {
Text($r('app.string.click_to_menu'))
.fontSize(18)
.textAlign(TextAlign.Start)
.width('100%')
.fontWeight(500)
.height('100%')
}
.id('clickToMenu')
.borderRadius(24)
.width('685px')
.height('84px')
.backgroundColor($r('sys.color.ohos_id_color_foreground_contrary'))
.margin({ top: 10, bottom: 10 })
.padding({ left: 12, right: 12, top: 4, bottom: 4 })
.onClick(() => {
router.pushUrl({
url: '@bundle:com.samples.hspsample/library/ets/pages/Menu'
}).then(() => {
console.log('push page success');
Logger.info(TAG, 'push page success');
}).catch((err: BusinessError) => {
Logger.error(TAG, `pushUrl failed, code is ${err.code}, message is ${err.message}`);
})
})
}
.alignListItem(ListItemAlign.Center)
}
.width('100%')
.backgroundColor($r('app.color.page_background'))
.height('100%')
}
}
其中router.pushUrl方法的入参中url的内容为:
'@bundle:com.samples.hspsample/library/ets/pages/Menu'
url内容的模板为:
'@bundle:包名(bundleName)/模块名(moduleName)/路径/页面所在的文件名(不加.ets后缀)'
3.5.3 页面路由返回
如果当前处于HSP中的页面,需要返回之前的页面时,可以使用router.back方法,但是返回的页面必须是当前页面跳转路径上的页面。
import router from '@ohos.router';
@Entry
@Component
struct Index3 { // 路径为:`library/src/main/ets/pages/Back.ets
@State message: string = 'HSP back page';
build() {
Row() {
Column() {
Text(this.message)
.fontFamily('HarmonyHeiTi')
.fontWeight(FontWeight.Bold)
.fontSize(32)
.fontColor($r('app.color.text_color'))
.margin({ top: '32px' })
.width('624px')
Button($r('app.string.back_to_HAP'))
.id('backToHAP')
.fontFamily('HarmonyHeiTi')
.height(48)
.width('624px')
.margin({ top: 550 })
.type(ButtonType.Capsule)
.borderRadius($r('sys.float.ohos_id_corner_radius_button'))
.backgroundColor($r('app.color.button_background'))
.fontColor($r('sys.color.ohos_id_color_foreground_contrary'))
.fontSize($r('sys.float.ohos_id_text_size_button1'))
// 绑定点击事件
.onClick(() => {
router.back({ // 返回HAP的页面
url: 'pages/Index' // 路径为:`entry/src/main/ets/pages/Index.ets`
})
})
Button($r('app.string.back_to_HSP'))
.id('backToHSP')
.fontFamily('HarmonyHeiTi')
.height(48)
.width('624px')
.margin({ top: '4%' , bottom: '6%' })
.type(ButtonType.Capsule)
.borderRadius($r('sys.float.ohos_id_corner_radius_button'))
.backgroundColor($r('app.color.button_background'))
.fontColor($r('sys.color.ohos_id_color_foreground_contrary'))
.fontSize($r('sys.float.ohos_id_text_size_button1'))
// 绑定点击事件
.onClick(() => {
router.back({ // 返回HSP的页面
url: '@bundle:com.samples.hspsample/library/ets/pages/Menu' //路径为:`library/src/main/ets/pages/Menu.ets
})
})
}
.width('100%')
}
.backgroundColor($r('app.color.page_background'))
.height('100%')
}
}
页面返回router.back方法的入参中url说明:
如果从HSP页面返回HAP页面,url的内容为:
'pages/Index'
url内容的模板为:
'页面所在的文件名(不加.ets后缀)'
如果从HSP1的页面跳到HSP2的页面后,需要返回到HSP1的页面,url的内容为:
'@bundle:com.samples.hspsample/library/ets/pages/Menu'
url内容的模板为:
'@bundle:包名(bundleName)/模块名(moduleName)/路径/页面所在的文件名(不加.ets后缀)'
3.6 集成态HSP
3.6.1 配置HSP模块为集成态HSP
修改模块级构建配置文件build-profile.json5,设置配置项integratedHsp为true,指定构建的HSP模块为集成态HSP。
{
"apiType": "stageMode",
"buildOption": {
"arkOptions": {
"integratedHsp": true
}
}
}
3.6.2 配置工程使用标准化的OHMUrl格式
集成态HSP需要使用标准化的OHMUrl格式,修改工程级构建配置文件build-profile.json5,设置配置项useNormalizedOHMUrl为true,指定工程使用标准化的OHMUrl格式。
{
"app": {
"products": {
"name": "default",
"signingConfig": "default",
"compatibleSdkVersion": "5.0.0(12)",
"runtimeOS": "HarmonyOS",
"buildOption": {
"strictMode": {
"useNormalizedOHMUrl": true
}
}
}
}
}