# HSP HSP(Harmony Shared Package)是动æ€å…±äº«åŒ…,å¯ä»¥åŒ…å«ä»£ç ã€C++库ã€èµ„æºå’Œé…置文件,通过HSPå¯ä»¥å®žçް代ç 和资æºçš„共享。HSP䏿”¯æŒç‹¬ç«‹å‘布,而是跟éšå…¶å®¿ä¸»åº”用的APP包一起å‘布,与宿主应用åŒè¿›ç¨‹ï¼Œå…·æœ‰ç›¸åŒçš„包å和生命周期。 > **说明:** > > 应用内HSP:在编译过程ä¸ä¸Žåº”用包å(bundleName)强耦åˆï¼Œåªèƒ½ç»™æŸä¸ªç‰¹å®šçš„应用使用。 > > [é›†æˆæ€HSP](integrated-hsp.md):构建ã€å‘布过程ä¸ï¼Œä¸ä¸Žç‰¹å®šçš„应用包å耦åˆï¼›ä½¿ç”¨æ—¶ï¼Œå·¥å…·é“¾æ”¯æŒè‡ªåŠ¨å°†é›†æˆæ€HSPçš„åŒ…åæ›¿æ¢æˆå®¿ä¸»åº”用包å。 ## 使用场景 - 多个HAP/HSP共用的代ç å’Œèµ„æºæ”¾åœ¨åŒä¸€ä¸ªHSPä¸ï¼Œå¯ä»¥æé«˜ä»£ç ã€èµ„æºçš„å¯é‡ç”¨æ€§å’Œå¯ç»´æŠ¤æ€§ï¼ŒåŒæ—¶ç¼–译打包时也åªä¿ç•™ä¸€ä»½HSP代ç 和资æºï¼Œèƒ½å¤Ÿæœ‰æ•ˆæŽ§åˆ¶åº”用包大å°ã€‚ - HSP在è¿è¡Œæ—¶æŒ‰éœ€åŠ è½½ï¼Œæœ‰åŠ©äºŽæå‡åº”用性能。 - åŒä¸€ä¸ªç»„织内部的多个应用之间,å¯ä»¥ä½¿ç”¨é›†æˆæ€HSP实现代ç 和资æºçš„共享。 ## 约æŸé™åˆ¶ - HSP䏿”¯æŒåœ¨è®¾å¤‡ä¸Šå•独安装/è¿è¡Œï¼Œéœ€è¦ä¸Žä¾èµ–该HSPçš„HAP一起安装/è¿è¡Œã€‚HSP的版本å·å¿…须与HAP版本å·ä¸€è‡´ã€‚ - HSP䏿”¯æŒåœ¨é…置文件ä¸å£°æ˜Ž[ExtensionAbility](../application-models/extensionability-overview.md)组件,但支æŒ[UIAbility](../application-models/uiability-overview.md)(除入å£ability外)组件。 - HSPå¯ä»¥ä¾èµ–å…¶ä»–HAR或HSPï¼Œä½†ä¸æ”¯æŒå¾ªçޝä¾èµ–ï¼Œä¹Ÿä¸æ”¯æŒä¾èµ–ä¼ é€’ã€‚ ## 创建 通过DevEco Studio创建一个HSP模å—,详è§[创建HSP模å—](https://developer.huawei.com/consumer/cn/doc/harmonyos-guides-V13/ide-hsp-V13#section7717162312546),我们以创建一个å为`library`çš„HSP模å—为例。基本的工程目录结构如下: ``` MyApplication ├── library │ ├── src │ │ └── main │ │ ├── ets │ │ │ └── pages │ │ │ └── index.ets │ │ ├── resources │ │ └── module.json5 │ ├── oh-package.json5 │ ├── index.ets │ └── build-profile.json5 //模å—级 └── build-profile.json5 //工程级 ``` ## å¼€å‘ ä»‹ç»å¦‚何导出HSPçš„ArkUIç»„ä»¶ã€æŽ¥å£ã€èµ„æºï¼Œä¾›åº”用内的其他HAP/HSP引用。 ### 导出ArkUI组件 ArkUI组件å¯ä»¥é€šè¿‡`export`导出,例如: ```ts // 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`ä¸å£°æ˜Žï¼š ```ts // library/index.ets export { MyTitleBar } from './src/main/ets/components/MyTitleBar'; ``` ### 导出ts类和方法 通过`export`导出ts类和方法,例如: ```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`ä¸å£°æ˜Žï¼š ```ts // library/index.ets export { Log, add, minus } from './src/main/ets/utils/test'; ``` ### 导出native方法 在HSPä¸ä¹Ÿå¯ä»¥åŒ…å«C++编写的`so`。对于`so`ä¸çš„`native`方法,HSP通过间接的方å¼å¯¼å‡ºï¼Œä»¥å¯¼å‡º`liblibrary.so`的乘法接å£`multi`为例: ```ts // library/src/main/ets/utils/nativeTest.ets import native from 'liblibrary.so'; export function nativeMulti(a: number, b: number): number { let result: number = native.multi(a, b); return result; } ``` 对外暴露的接å£ï¼Œéœ€è¦åœ¨å…¥å£æ–‡ä»¶`index.ets`ä¸å£°æ˜Žï¼š ```ts // library/index.ets export { nativeMulti } from './src/main/ets/utils/nativeTest'; ``` ### 通过$r访问HSPä¸çš„èµ„æº åœ¨ç»„ä»¶ä¸ï¼Œç»å¸¸éœ€è¦ä½¿ç”¨å—符串ã€å›¾ç‰‡ç‰èµ„æºã€‚HSPä¸çš„组件需è¦ä½¿ç”¨èµ„æºæ—¶ï¼Œä¸€èˆ¬å°†å…¶æ‰€ç”¨èµ„æºæ”¾åœ¨HSPåŒ…å†…ï¼Œè€Œéžæ”¾åœ¨HSP的使用方处,以符åˆé«˜å†…èšä½Žè€¦åˆçš„原则。 在工程ä¸ï¼Œå¸¸é€šè¿‡`$r`/`$rawfile`的形å¼å¼•用应用资æºã€‚å¯ä»¥ç”¨`$r`/`$rawfile`访问本模å—`resources`目录下的资æºï¼Œå¦‚访问`resources`目录下定义的图片`src/main/resources/base/media/example.png`时,å¯ä»¥ç”¨`$r("app.media.example")`。有关`$r`/`$rawfile`的详细使用方å¼ï¼Œè¯·å‚阅文档[资æºåˆ†ç±»ä¸Žè®¿é—®](./resource-categories-and-access.md)ä¸â€œèµ„æºè®¿é—®-应用资æºâ€å°èŠ‚ã€‚ 䏿ލè使用相对路径的方å¼ï¼Œå®¹æ˜“引用错误路径。例如: 当è¦å¼•用上述åŒä¸€å›¾ç‰‡èµ„æºæ—¶ï¼Œåœ¨HSP模å—ä¸ä½¿ç”¨`Image("../../resources/base/media/example.png")`,实际上该`Image`组件访问的是HSP调用方(如`entry`)下的资æº`entry/src/main/resources/base/media/example.png`。 ```ts // 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') ``` ### 导出HSPä¸çš„èµ„æº è·¨åŒ…è®¿é—®HSPå†…èµ„æºæ—¶ï¼ŒæŽ¨è实现一个资æºç®¡ç†ç±»ï¼Œä»¥å°è£…对外导出的资æºã€‚é‡‡ç”¨è¿™ç§æ–¹å¼ï¼Œå…·æœ‰å¦‚下优点: - HSPå¼€å‘者å¯ä»¥æŽ§åˆ¶è‡ªå·±éœ€è¦å¯¼å‡ºçš„资æºï¼Œä¸éœ€è¦å¯¹å¤–暴露的资æºå¯ä»¥ä¸ç”¨å¯¼å‡ºã€‚ - ä½¿ç”¨æ–¹æ— é¡»æ„ŸçŸ¥HSP内部的资æºå称。当HSP内部的资æºåç§°å‘生å˜åŒ–时,也ä¸éœ€è¦ä½¿ç”¨æ–¹è·Ÿç€ä¿®æ”¹ã€‚ 其具体实现如下: 将需è¦å¯¹å¤–æä¾›çš„资æºå°è£…为一个资æºç®¡ç†ç±»ï¼š ```ts // 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`ä¸å£°æ˜Žï¼š ```ts // library/index.ets export { ResManager } from './src/main/ets/ResManager'; ``` ## 使用 介ç»å¦‚何引用HSPä¸çš„æŽ¥å£ï¼Œä»¥åŠå¦‚何通过页é¢è·¯ç”±å®žçްHSPçš„pages页é¢è·³è½¬ä¸Žè¿”回。 ### 引用HSPä¸çš„æŽ¥å£ è¦ä½¿ç”¨HSPä¸çš„æŽ¥å£ï¼Œé¦–先需è¦åœ¨ä½¿ç”¨æ–¹çš„oh-package.json5ä¸é…置对它的ä¾èµ–,详è§[引用动æ€å…±äº«åŒ…](https://developer.huawei.com/consumer/cn/doc/harmonyos-guides-V13/ide-har-import-V13)。 ä¾èµ–é…ç½®æˆåŠŸåŽï¼Œå°±å¯ä»¥åƒä½¿ç”¨HARä¸€æ ·è°ƒç”¨HSP的对外接å£äº†ã€‚例如,上é¢çš„libraryå·²ç»å¯¼å‡ºäº†ä¸‹é¢è¿™äº›æŽ¥å£ï¼š ```ts // 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'; ``` 在使用方的代ç ä¸ï¼Œå¯ä»¥è¿™æ ·ä½¿ç”¨ï¼š ```ts // entry/src/main/ets/pages/index.ets import { Log, add, MyTitleBar, ResManager, nativeMulti } from 'library'; import { BusinessError } from '@ohos.base'; 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 => { console.log('getStringValue is ' + value); this.message = 'getStringValue is ' + value; }) .catch((err: BusinessError) => { console.error('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%') } } ``` ### 页é¢è·¯ç”±è·³è½¬ 若开å‘者想在entry模å—ä¸ï¼Œæ·»åŠ ä¸€ä¸ªæŒ‰é’®è·³è½¬è‡³library模å—ä¸çš„menu页é¢ï¼ˆè·¯å¾„为:`library/src/main/ets/pages/menu.ets`),那么å¯ä»¥åœ¨ä½¿ç”¨æ–¹çš„代ç (entry模å—下的Index.ets,路径为:`entry/src/main/ets/pages/Index.ets`ï¼‰é‡Œè¿™æ ·ä½¿ç”¨ï¼š ```ts import { Log, add, MyTitleBar, ResManager, nativeMulti } from 'library'; import { BusinessError } from '@ohos.base'; 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'); }).catch((err: BusinessError) => { console.error('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`的内容为: ```ets '@bundle:com.samples.hspsample/library/ets/pages/Menu' ``` `url`内容的模æ¿ä¸ºï¼š ```ets '@bundle:包å(bundleName)/模å—å(moduleName)/路径/页颿‰€åœ¨çš„æ–‡ä»¶å(ä¸åŠ .etsåŽç¼€)' ``` ### 页é¢è·¯ç”±è¿”回 如果当å‰å¤„于HSPä¸çš„页é¢ï¼Œéœ€è¦è¿”回之å‰çš„页颿—¶ï¼Œå¯ä»¥ä½¿ç”¨`router.back`方法,但是返回的页é¢å¿…须是当å‰é¡µé¢è·³è½¬è·¯å¾„上的页é¢ã€‚ ```ts 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的内容为: ```ets 'pages/Index' ``` `url`内容的模æ¿ä¸ºï¼š ```ets '页颿‰€åœ¨çš„æ–‡ä»¶å(ä¸åŠ .etsåŽç¼€)' ``` * 如果从HSP1的页é¢è·³åˆ°HSP2的页é¢åŽï¼Œéœ€è¦è¿”回到HSP1的页é¢ï¼Œurl的内容为: ```ets '@bundle:com.samples.hspsample/library/ets/pages/Menu' ``` `url`内容的模æ¿ä¸ºï¼š ```ets '@bundle:包å(bundleName)/模å—å(moduleName)/路径/页颿‰€åœ¨çš„æ–‡ä»¶å(ä¸åŠ .etsåŽç¼€)' ```