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

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

## 开发应用内HSP

J
junyi233 已提交
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 26
```
模块`module.json5`中的`"type"`标识模块类型,`HSP``"type"``"shared"`
```json
{
    "type": "shared"
}
```

Y
yangmingliang 已提交
27
`HSP`通过在入口文件中导出接口,对外提供能力。入口文件在模块`oh-package.json5``"main"`中配置。例如:
J
jsjzju 已提交
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 70 71 72 73 74 75 76 77 78 79 80 81 82
```json
{
    "main": "./src/main/ets/index.ets"
}
```

### 导出ts类和方法
通过`export`导出ts类和方法,例如:
```ts
// library/src/main/ets/utils/test.ts
export class Log {
    static info(msg) {
        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 已提交
83 84

#### 通过$r访问HSP中资源
85
在组件中,经常需要使用字符串、图片等资源。`HSP`中的组件需要使用资源时,一般将其所用资源放在HSP包内,以符合高内聚低耦合的原则。可以通过`$r`/`$rawfile`访问本模块`resources`目录下的资源。
H
Haoming Luo 已提交
86 87

不推荐使用相对路径的方式,容易引用错误路径。例如:
J
jsjzju 已提交
88 89
`HSP`模块中使用`Image("common/example.png")`,实际上该`Image`组件访问的是`HSP调用方`(如`entry`)下的资源`entry/src/main/ets/common/example.png`

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

其具体实现如下:

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

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

J
jsjzju 已提交
116 117 118
### 导出native方法
`HSP`中也可以包含`C++`编写的`so`。对于`so`中的`native`方法,`HSP`通过间接的方式导出,以导出`libnative.so`的乘法接口`multi`为例:
```ts
J
junyi233 已提交
119
// library/src/main/ets/utils/nativeTest.ts
J
jsjzju 已提交
120 121 122 123 124 125 126 127 128 129 130 131 132 133
import native from "libnative.so"

export function nativeMulti(a: number, b: number) {
    return native.multi(a, b);
}
```

对外暴露的接口,需要在入口文件`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
jsjzju 已提交
148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164

@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);
          })
H
Haoming Luo 已提交
165 166
        Image(ResManager.getPic())
          .width("100%")
167 168
        Button('getStringValue')
          .onClick(()=> {
169 170 171
            // 先通过当前上下文获取hsp模块的上下文,再获取hsp模块的resourceManager,然后再调用resourceManager的接口获取资源
            // 注:用`$r`方法获得的是Resource对象形式的资源,其可被组件直接使用,但在此须对其进行解封装才可使用
            getContext().createModuleContext('library').resourceManager.getStringValue(ResManager.getDesc())
172
              .then(value => {
173
                console.log("getStringValue is " + value);
174 175
              })
              .catch(error => {
176
                console.log("getStringValue promise error is " + error);
177 178 179
              });
          })
          .width("50%")
J
jsjzju 已提交
180 181 182 183 184 185 186 187 188 189 190
        Button('nativeMulti(3, 4)')
          .onClick(()=>{
            Log.info("nativeMulti button click!");
            this.message = "result: " + nativeMulti(3, 4);
          })
      }
      .width('100%')
    }
    .height('100%')
  }
}
X
xsz233 已提交
191 192
```

X
xsz233 已提交
193
### 页面路由跳转
X
xsz233 已提交
194 195 196 197 198 199 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 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246

若开发者想在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';

@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");
            }).catch(err => {
              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 已提交
247
```