in-app-hsp.md 8.3 KB
Newer Older
J
jsjzju 已提交
1 2
# 应用内HSP开发指导

H
Haoming Luo 已提交
3 4
应用内HSP指的是专门为某一应用开发的HSP,只能被该应用内部其他HAP/HSP使用,用于应用内部代码、资源的共享。
应用内HSP跟随其宿主应用的APP包一起发布,与宿主应用同进程,具有相同的包名和生命周期。
J
jsjzju 已提交
5 6 7

## 开发应用内HSP

8
通过DevEco Studio创建一个HSP模块,创建方式可[参考](https://developer.harmonyos.com/cn/docs/documentation/doc-guides-V3/hsp-0000001521396322-V3#section7717162312546),我们以创建一个名为`library`的HSP模块为例。基本的工程目录结构如下:
J
jsjzju 已提交
9 10 11 12 13 14 15 16 17
```
library
├── src
│   └── main
│       ├── ets
│       │   ├── pages
│       │   └── index.ets
│       ├── resources
│       └── module.json5
Y
yangmingliang 已提交
18
└── oh-package.json5
J
jsjzju 已提交
19 20 21 22 23 24 25
```

### 导出ts类和方法
通过`export`导出ts类和方法,例如:
```ts
// library/src/main/ets/utils/test.ts
export class Log {
J
junyi233 已提交
26
    static info(msg: string) {
J
jsjzju 已提交
27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69
        console.info(msg);
    }
}

export function add(a: number, b: number) {
  return a + b;
}

export function minus(a: number, b: number) {
  return a - b;
}
```
对外暴露的接口,需要在入口文件`index.ets`中声明:
```ts
// library/src/main/ets/index.ets
export { Log, add, minus } from './utils/test'
```

### 导出ArkUI组件
ArkUI组件也可以通过`export`导出,例如:
```ts
// library/src/main/ets/components/MyTitleBar.ets
@Component
export struct MyTitleBar {
  build() {
    Row() {
      Text($r('app.string.library_title'))
        .fontColor($r('app.color.white'))
        .fontSize(25)
        .margin({left:15})
    }
    .width('100%')
    .height(50)
    .padding({left:15})
    .backgroundColor('#0D9FFB')
  }
}
```
对外暴露的接口,需要在入口文件`index.ets`中声明:
```ts
// library/src/main/ets/index.ets
export { MyTitleBar } from './components/MyTitleBar'
```
H
Haoming Luo 已提交
70

H
Haoming Luo 已提交
71
### 通过$r访问HSP中资源
72 73
在组件中,经常需要使用字符串、图片等资源。HSP中的组件需要使用资源时,一般将其所用资源放在HSP包内,而非放在HSP的使用方处,以符合高内聚低耦合的原则。

J
junyi233 已提交
74
在工程中,常通过`$r`/`$rawfile`的形式引用应用资源。可以用`$r`/`$rawfile`访问本模块`resources`目录下的资源,如访问`resources`目录下定义的图片`src/main/resources/base/media/example.png`时,可以用`$r("app.media.example")`。有关`$r`/`$rawfile`的详细使用方式,请参阅文档[资源分类与访问](./resource-categories-and-access.md)中“资源访问-应用资源”小节。
H
Haoming Luo 已提交
75 76

不推荐使用相对路径的方式,容易引用错误路径。例如:
77
当要引用上述同一图片资源时,在HSP模块中使用`Image("../../resources/base/media/example.png")`,实际上该`Image`组件访问的是HSP调用方(如`entry`)下的资源`entry/src/main/resources/base/media/example.png`
J
jsjzju 已提交
78

79 80 81 82 83 84 85 86 87 88
```ts
// library/src/main/ets/pages/Index.ets
// 正确用例
Image($r("app.media.example"))
  .width("100%")
// 错误用例
Image("../../resources/base/media/example.png")
  .width("100%")
```

H
Haoming Luo 已提交
89
### 导出HSP中资源
90 91 92
跨包访问HSP内资源时,推荐实现一个资源管理类,以封装对外导出的资源,通过该方式:
- HSP开发者可以控制自己需要导出的资源,不需要对外暴露的资源可以不用导出;
- 使用方无须感知HSP内部的资源名称,HSP内部的资源名称变化时也不需要使用方跟着修改。
H
Haoming Luo 已提交
93 94 95

其具体实现如下:

96
封装对外提供资源的资源管理类:   
H
Haoming Luo 已提交
97 98 99
```ts
// library/src/main/ets/ResManager.ets
export class ResManager{
100
  static getPic(): Resource{
H
Haoming Luo 已提交
101 102
    return $r("app.media.pic");
  }
103
  static getDesc(): Resource{
H
Haoming Luo 已提交
104 105
    return $r("app.string.shared_desc");
  }
H
Haoming Luo 已提交
106 107 108
}
```

H
Haoming Luo 已提交
109
对外暴露的接口,需要在入口文件`index.ets`中声明:
H
Haoming Luo 已提交
110 111 112 113 114
```ts
// library/src/main/ets/index.ets
export { ResManager } from './ResManager'
```

J
jsjzju 已提交
115
### 导出native方法
H
Haoming Luo 已提交
116
在HSP中也可以包含C++编写的`so`。对于`so`中的`native`方法,HSP通过间接的方式导出,以导出`libnative.so`的乘法接口`multi`为例:
J
jsjzju 已提交
117
```ts
J
junyi233 已提交
118
// library/src/main/ets/utils/nativeTest.ts
J
jsjzju 已提交
119 120 121
import native from "libnative.so"

export function nativeMulti(a: number, b: number) {
J
junyi233 已提交
122 123
    let result: number = native.multi(a, b);
    return result;
J
jsjzju 已提交
124 125 126 127 128 129 130 131 132 133
}
```

对外暴露的接口,需要在入口文件`index.ets`中声明:
```ts
// library/src/main/ets/index.ets
export { nativeMulti } from './utils/nativeTest'
```

## 使用应用内HSP
J
junyi233 已提交
134 135 136
要使用HSP中的接口,首先需要在使用方的oh-package.json5中配置对它的依赖,配置方式可[参考](https://developer.harmonyos.com/cn/docs/documentation/doc-guides-V3/hsp-0000001521396322-V3#section6161154819195)
依赖配置成功后,就可以像使用HAR一样调用HSP的对外接口了。 例如,上面的library已经导出了下面这些接口:

J
jsjzju 已提交
137 138 139 140
```ts
// library/src/main/ets/index.ets
export { Log, add, minus } from './utils/test'
export { MyTitleBar } from './components/MyTitleBar'
H
Haoming Luo 已提交
141
export { ResManager } from './ResManager'
J
jsjzju 已提交
142 143 144 145 146
export { nativeMulti } from './utils/nativeTest'
```
在使用方的代码中,可以这样使用:
```ts
// entry/src/main/ets/pages/index.ets
147
import { Log, add, MyTitleBar, ResManager, nativeMulti } from "library"
J
junyi233 已提交
148
import { BusinessError } from '@ohos.base';
J
jsjzju 已提交
149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165

@Entry
@Component
struct Index {
  @State message: string = 'Hello World'
  build() {
    Row() {
      Column() {
        MyTitleBar()
        Text(this.message)
          .fontSize(30)
          .fontWeight(FontWeight.Bold)
        Button('add(1, 2)')
          .onClick(()=>{
            Log.info("add button click!");
            this.message = "result: " + add(1, 2);
          })
166
        // ResManager返回的Resource对象,可以传给组件直接使用,也可以从中取出资源来使用
H
Haoming Luo 已提交
167 168
        Image(ResManager.getPic())
          .width("100%")
169 170
        Button('getStringValue')
          .onClick(()=> {
171 172
            // 先通过当前上下文获取hsp模块的上下文,再获取hsp模块的resourceManager,然后再调用resourceManager的接口获取资源
            getContext().createModuleContext('library').resourceManager.getStringValue(ResManager.getDesc())
173
              .then(value => {
174
                console.log("getStringValue is " + value);
175
              })
J
junyi233 已提交
176
              .catch((err: BusinessError) => {
177
                console.log("getStringValue promise error is " + error);
178 179 180
              });
          })
          .width("50%")
J
jsjzju 已提交
181 182 183 184 185 186 187 188 189 190 191
        Button('nativeMulti(3, 4)')
          .onClick(()=>{
            Log.info("nativeMulti button click!");
            this.message = "result: " + nativeMulti(3, 4);
          })
      }
      .width('100%')
    }
    .height('100%')
  }
}
X
xsz233 已提交
192 193
```

X
xsz233 已提交
194
### 页面路由跳转
X
xsz233 已提交
195 196 197 198

若开发者想在entry模块中,添加一个按钮跳转至library模块中的menu页面(路径为:`library/src/main/ets/pages/menu.ets`),那么可以在使用方的代码(entry模块下的Index.ets,路径为:`entry/src/main/ets/MainAbility/Index.ets`)里这样使用:
```ts
import router from '@ohos.router';
J
junyi233 已提交
199
import { BusinessError } from '@ohos.base';
X
xsz233 已提交
200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230

@Entry
@Component
struct Index {
    @State message: string = 'Hello World'

    build() {
    Row() {
        Column() {
        Text(this.message)
            .fontSize(50)
            .fontWeight(FontWeight.Bold)
        // 添加按钮,以响应用户点击
        Button() {
            Text('click to menu')
            .fontSize(30)
            .fontWeight(FontWeight.Bold)
        }
        .type(ButtonType.Capsule)
        .margin({
            top: 20
        })
        .backgroundColor('#0D9FFB')
        .width('40%')
        .height('5%')
        // 绑定点击事件
        .onClick(() => {
            router.pushUrl({
              url: '@bundle:com.example.hmservice/library/ets/pages/menu'
            }).then(() => {
              console.log("push page success");
J
junyi233 已提交
231
            }).catch((err: BusinessError) => {
X
xsz233 已提交
232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248
              console.error(`pushUrl failed, code is ${err.code}, message is ${err.message}`);
            })
        })
      .width('100%')
    }
    .height('100%')
    }
  }
}
```
其中`router.pushUrl`方法的入参中`url`的内容为:
```ets
'@bundle:com.example.hmservice/library/ets/pages/menu'
```
`url`内容的模板为:
```ets
'@bundle:包名(bundleName)/模块名(moduleName)/路径/页面所在的文件名(不加.ets后缀)'
J
jsjzju 已提交
249
```