• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1# HSP
2<!--Kit: Ability Kit-->
3<!--Subsystem: BundleManager-->
4<!--Owner: @wanghang904-->
5<!--Designer: @hanfeng6-->
6<!--Tester: @kongjing2-->
7<!--Adviser: @Brilliantry_Rui-->
8
9A Harmony Shared Package (HSP) is a dynamic shared package that can contain code, C++ libraries, resource files, and configuration files (also called profiles) and allows for code and resource sharing. An HSP is released with the Application Package (App Pack) of the host application, shares a process with the host application, and has the same bundle name and lifecycle as the host application.
10> **NOTE**
11>
12> In-app HSP: a type of HSP that is closely coupled with an application bundle name (**bundleName**) during compilation and can be used only by the specified application. This topic mainly describes in-app HSP.
13>
14> [Integrated HSP](integrated-hsp.md): a type of HSP that is not coupled with specific application bundle names during building and publishing. The toolchain can automatically replace the bundle name of the integrated HSP with that of the host application and generate a new HSP as the installation package of the host application. The new HSP also belongs to the in-app HSP of the host application.
15
16## Use Scenarios
17- By storing code and resource files shared by multiple HAPs/HSPs in one place, the HSP significantly improves the reusability and maintainability of the code and resource files. Better yet, because only one copy of the HSP code and resource files is retained during building and packaging, the size of the application package is effectively controlled.
18
19- The HSP is [loaded on demand](https://developer.huawei.com/consumer/en/doc/best-practices/bpta-modular-design#section28312051291) during application running, which helps improve application performance.
20
21- The integrated HSP allows for code and resource sharing across applications in the same organization.
22
23## Constraints
24
25- An HSP must be installed and run with the HAP that depends on it. It cannot be installed or run independently on a device. Since API version 18, the HAP version must be later than or equal to the HSP version. For API version 17 and earlier versions, the HSP version must be the same as the HAP version.
26- Since API version 14, HSP supports the declaration of the [UIAbility](../application-models/uiability-overview.md#declaration-configuration) component in the configuration file. However, UIAbility with entry capabilities (that is, **entity.system.home** and **ohos.want.action.home** are configured for the **skill** tag) is not supported. For details about how to configure a UIAbility, see [Adding a UIAbility to a Module](https://developer.huawei.com/consumer/en/doc/harmonyos-guides/ide-add-new-ability#section18658758104318). The method of starting a UIAbility in an HSP is the same as that described in [Starting UIAbility in the Same Application](../application-models/uiability-intra-device-interaction.md). For API version 13 and earlier versions, the [UIAbility](../application-models/uiability-overview.md#declaration-configuration) component cannot be declared in the configuration file.
27- Since API version 18, HSP supports the declaration of the [ExtensionAbility](../application-models/extensionability-overview.md) component in the configuration file. However, ExtensionAbility with entry capabilities (that is, **entity.system.home** and **ohos.want.action.home** are configured for the **skill** tag) is not supported. For details about how to configure an ExtensionAbility in an HSP, see [Adding an ExtensionAbility to a Module](https://developer.huawei.com/consumer/en/doc/harmonyos-guides/ide-add-new-ability#section18891639459). For API version 17 and earlier versions, the [ExtensionAbility](../application-models/extensionability-overview.md) component cannot be declared in the configuration file.
28- An HSP can depend on other HARs or HSPs, and can be depended on or integrated by HAPs or HSPs. However, cyclic dependency and dependency transfer are not supported.
29
30> **NOTE**
31>
32> Cyclic dependency: For example, there are three HSPs. HSP-A depends on HSP-B, HSP-B depends on HSP-C, and HSP-C depends on HSP-A.
33>
34> Dependency transfer: For example, there are three HSPs. HSP-A depends on HSP-B, and HSP-B depends on HSP-C. Dependency transfer is not supported indicating that HSP-A can use the methods and components of HSP-B, but cannot directly use that of HSP-C.
35
36
37## Creating an HSP
38Create an HSP module for calling C++ code on DevEco Studio and turn on **Enable native** on the **Configure New Module** page. For details, see [Creating an HSP Module](https://developer.huawei.com/consumer/en/doc/harmonyos-guides/ide-hsp#section7717162312546). The following uses the **library** module as an example. The basic project directory structure is as follows:
39```
40MyApplication
41├── library
42│   ├── src
43│   │   └── main
44|   |       ├── cpp
45|   |       |   ├── CMakeLists.txt    // Configuration file for compiling native C++ code
46|   |       |   └── napi_init.cpp     // C++ file for initializing the NAPI module
47│   │       ├── ets
48│   │       │   └── pages
49│   │       │       └── index.ets     // Page file of the library module
50│   │       ├── resources             // Resources of the library module
51│   │       └── module.json5          // Configuration file of the library module
52│   ├── oh-package.json5              // Module-level configuration file
53│   ├── index.ets                     // Entry file
54│   └── build-profile.json5           // Module-level configuration file
55└── build-profile.json5               // Project-level configuration file
56```
57
58## Developing an HSP
59
60
61You can export the ArkUI components, APIs, and other resources of an HSP for other HAPs or HSPs in the same application to reference.
62
63### Exporting ArkUI Components
64Use **export** to export ArkUI components. The sample code is as follows:
65```ts
66// library/src/main/ets/components/MyTitleBar.ets
67@Component
68export struct MyTitleBar {
69  build() {
70    Row() {
71      Text($r('app.string.library_title'))
72        .id('library')
73        .fontFamily('HarmonyHeiTi')
74        .fontWeight(FontWeight.Bold)
75        .fontSize(32)
76        .fontColor($r('app.color.text_color'))
77    }
78    .width('100%')
79  }
80}
81```
82Declare the APIs exposed to external systems in the entry file **index.ets**.
83```ts
84// library/index.ets
85export { MyTitleBar } from './src/main/ets/components/MyTitleBar';
86```
87
88
89### Exporting Classes and Methods
90Use **export** to export classes and methods. The sample code is as follows:
91```ts
92// library/src/main/ets/utils/test.ets
93export class Log {
94  static info(msg: string): void {
95    console.info(msg);
96  }
97}
98
99export function add(a: number, b: number): number {
100  return a + b;
101}
102
103export function minus(a: number, b: number): number {
104  return a - b;
105}
106```
107Declare the APIs exposed to external systems in the entry file **index.ets**.
108```ts
109// library/index.ets
110export { Log, add, minus } from './src/main/ets/utils/test';
111```
112### Exporting Native Methods
113The HSP can contain .so files compiled in C++. The HSP indirectly exports the native method in the .so file. In this example, the **multi** API in the **liblibrary.so** file is exported.
114```ts
115// library/src/main/ets/utils/nativeTest.ets
116import native from 'liblibrary.so';
117
118export function nativeMulti(a: number, b: number): number {
119  let result: number = native.multi(a, b);
120  return result;
121}
122```
123
124Declare the APIs exposed to external systems in the entry file **index.ets**.
125```ts
126// library/index.ets
127export { nativeMulti } from './src/main/ets/utils/nativeTest';
128```
129
130### Accessing Resources in an HSP Through $r
131More often than not, you may need to use resources, such as strings and images, in components. For components in an HSP, such resources are typically placed in the HSP package, rather than in the package where the HSP is invoked, for the purpose of complying with the principle of high cohesion and low coupling.
132
133In a project, application resources are referenced in the $r/$rawfile format. You can use **$r**/**$rawfile** to access resources in the **resources** directory of the current module. For example, you can use **$r("app.media.example")** to access the **src/main/resources/base/media/example.png** image stored in the **resources** directory. For details about how to use **$r**/**$rawfile**, see [Resource Categories and Access](./resource-categories-and-access.md).
134
135To avoid reference errors, do not use relative paths. For example,
136if you use **Image("../../resources/base/media/example.png")**, the image actually used will be the one in the directory of the module that invokes the HSP. That is, if the module that invokes the HSP is **entry**, then the image used will be **entry/src/main/resources/base/media/example.png**.
137
138```ts
139// library/src/main/ets/pages/Index.ets
140// Correct
141Image($r('app.media.example'))
142  .id('example')
143  .borderRadius('48px')
144// Incorrect
145Image("../../resources/base/media/example.png")
146  .id('example')
147  .borderRadius('48px')
148```
149
150### Exporting Resources from HSP
151When resources in an HSP need to be exported for cross-package access, it is recommended that a resource manager class be implemented to encapsulate the exported resources. In this way:
152- You can keep resources well under your control, eliminating the need for exporting resources that do not need to be exposed.
153- The invoking module does not need to be aware of the internal resource names of the HSP, or make adaptation to changes in these internal resource names.
154
155The implementation is as follows:
156
157Encapsulate the resources that need to be published into a resource management class.
158```ts
159// library/src/main/ets/ResManager.ets
160export class ResManager{
161  static getPic(): Resource{
162    return $r('app.media.pic');
163  }
164  static getDesc(): Resource{
165    return $r('app.string.shared_desc');
166  }
167}
168```
169
170Declare the APIs exposed to external systems in the entry file **index.ets**.
171```ts
172// library/index.ets
173export { ResManager } from './src/main/ets/ResManager';
174```
175
176
177
178## Using an HSP
179
180You can reference APIs in an HSP and implement page redirection in the HSP through page routing.
181
182### Referencing APIs
183To use APIs in the HSP, first configure the dependency on the HSP in the **oh-package.json5** file of the module that needs to call the APIs (called the invoking module). For details, see [Referencing a Shared Package](https://developer.huawei.com/consumer/en/doc/harmonyos-guides/ide-har-import).
184You can then call the external APIs of the HSP in the same way as calling the APIs in the HAR. In this example, the external APIs are the following ones exported from **library**:
185
186```ts
187// library/index.ets
188export { Log, add, minus } from './src/main/ets/utils/test';
189export { MyTitleBar } from './src/main/ets/components/MyTitleBar';
190export { ResManager } from './src/main/ets/ResManager';
191export { nativeMulti } from './src/main/ets/utils/nativeTest';
192```
193The APIs can be used as follows in the code of the invoking module:
194```ts
195// entry/src/main/ets/pages/index.ets
196import { Log, add, MyTitleBar, ResManager, nativeMulti } from 'library';
197import { BusinessError } from "@kit.BasicServicesKit";
198import { application} from '@kit.AbilityKit';
199
200const TAG = 'Index';
201
202@Entry
203@Component
204struct Index {
205  @State message: string = '';
206
207  build() {
208    Column() {
209      List() {
210        ListItem() {
211          MyTitleBar()
212        }
213        .margin({ left: '35px', top: '32px' })
214
215        ListItem() {
216          Text(this.message)
217            .fontFamily('HarmonyHeiTi')
218            .fontSize(18)
219            .textAlign(TextAlign.Start)
220            .width('100%')
221            .fontWeight(FontWeight.Bold)
222        }
223        .width('685px')
224        .margin({ top: 30, bottom: 10 })
225
226        ListItem() {
227          // Resource object returned by ResManager, which can be passed to a component for direct use or be extracted.
228          Image(ResManager.getPic())
229            .id('image')
230            .borderRadius('48px')
231        }
232        .width('685px')
233        .margin({ top: 10, bottom: 10 })
234        .padding({ left: 12, right: 12, top: 4, bottom: 4 })
235
236        ListItem() {
237          Text($r('app.string.add'))
238            .fontSize(18)
239            .textAlign(TextAlign.Start)
240            .width('100%')
241            .fontWeight(500)
242            .height('100%')
243        }
244        .id('add')
245        .borderRadius(24)
246        .width('685px')
247        .height('84px')
248        .backgroundColor($r('sys.color.ohos_id_color_foreground_contrary'))
249        .margin({ top: 10, bottom: 10 })
250        .padding({ left: 12, right: 12, top: 4, bottom: 4 })
251        .onClick(() => {
252          Log.info('add button click!');
253          this.message = 'result: ' + add(1, 2);
254        })
255
256        ListItem() {
257          Text(ResManager.getDesc())
258            .fontSize(18)
259            .textAlign(TextAlign.Start)
260            .width('100%')
261            .fontWeight(500)
262            .height('100%')
263        }
264        .id('getStringValue')
265        .borderRadius(24)
266        .width('685px')
267        .height('84px')
268        .backgroundColor($r('sys.color.ohos_id_color_foreground_contrary'))
269        .margin({ top: 10, bottom: 10 })
270        .padding({ left: 12, right: 12, top: 4, bottom: 4 })
271        .onClick(() => {
272          // Obtain the context of the HSP module through application.createModuleContext and the resourceManager object of the HSP module, and call the API of resourceManager to obtain resources.
273          application.createModuleContext(this.getUIContext()?.getHostContext(), "library").then((context:Context)=>{
274              context.resourceManager.getStringValue(ResManager.getDesc().id)
275              .then(value => {
276                console.log('getStringValue is ' + value);
277                this.message = 'getStringValue is ' + value;
278              })
279              .catch((err: BusinessError) => {
280                console.error('getStringValue promise error is ' + err);
281              });
282          }).catch((err: BusinessError) => {
283            console.error('createModuleContext promise error is ' + err);
284          });
285        })
286
287        ListItem() {
288          Text($r('app.string.native_multi'))
289            .fontSize(18)
290            .textAlign(TextAlign.Start)
291            .width('100%')
292            .fontWeight(500)
293            .height('100%')
294        }
295        .id('nativeMulti')
296        .borderRadius(24)
297        .width('685px')
298        .height('84px')
299        .backgroundColor($r('sys.color.ohos_id_color_foreground_contrary'))
300        .margin({ top: 10, bottom: 10 })
301        .padding({ left: 12, right: 12, top: 4, bottom: 4 })
302        .onClick(() => {
303          Log.info('nativeMulti button click!');
304          this.message = 'result: ' + nativeMulti(3, 4);
305        })
306      }
307      .alignListItem(ListItemAlign.Center)
308    }
309    .width('100%')
310    .backgroundColor($r('app.color.page_background'))
311    .height('100%')
312  }
313}
314```
315
316### Page Redirection and Return
317
318To add a button in the **entry** module to jump to the menu page (**library/src/main/ets/pages/library_menu.ets**) in the **library** module, write the following code in the **entry/src/main/ets/pages/Index.ets** file of the **entry** module:
319```ts
320// entry/src/main/ets/pages/Index.ets
321
322@Entry
323@Component
324struct Index {
325  @State message: string = '';
326  pathStack: NavPathStack = new NavPathStack();
327
328  build() {
329    Navigation(this.pathStack) {
330      Column() {
331        List() {
332          ListItem() {
333            Text($r('app.string.click_to_menu'))
334              .fontSize(18)
335              .textAlign(TextAlign.Start)
336              .width('100%')
337              .fontWeight(500)
338              .height('100%')
339          }
340          .id('clickToMenu')
341          .borderRadius(24)
342          .width('685px')
343          .height('84px')
344          .backgroundColor($r('sys.color.ohos_id_color_foreground_contrary'))
345          .margin({ top: 10, bottom: 10 })
346          .padding({
347            left: 12,
348            right: 12,
349            top: 4,
350            bottom: 4
351          })
352          .onClick(() => {
353            this.pathStack.pushPathByName('library_menu', null)
354          })
355        }
356        .alignListItem(ListItemAlign.Center)
357      }
358      .width('100%')
359      .backgroundColor($r('app.color.page_background'))
360      .height('100%')
361    }.title("Navigation_index")
362    .mode(NavigationMode.Stack)
363  }
364}
365```
366
367Add a page file (**library/src/main/ets/pages/library_menu.ets**) to the **library** module. The **back_to_index** button on the page can be used to return to the previous page.
368```
369// library/src/main/ets/pages/library_menu.ets
370@Builder
371export function PageOneBuilder() {
372  Library_Menu()
373}
374
375@Entry
376@Component
377export struct Library_Menu {
378  @State message: string = 'Hello World';
379  pathStack: NavPathStack = new NavPathStack();
380
381  build() {
382    NavDestination() {
383      Row() {
384        Column() {
385          Text(this.message)
386            .fontSize($r('app.float.page_text_font_size'))
387            .fontWeight(FontWeight.Bold)
388            .onClick(() => {
389              this.message = 'Welcome';
390            })
391          Button("back_to_index").fontSize(50).onClick(() => {
392            this.pathStack.pop();
393          })
394        }
395        .width('100%')
396      }
397      .height('100%')
398    }.title('Library_Menu')
399    .onReady((context: NavDestinationContext) => {
400      this.pathStack = context.pathStack
401    })
402  }
403}
404```
405
406Add the **route_map.json** file (**library/src/main/resources/base/profile/route_map.json**) to the **library** module.
407```
408{
409  "routerMap": [
410    {
411      "name": "library_menu",
412      "pageSourceFile": "src/main/ets/pages/library_menu.ets",
413      "buildFunction": "PageOneBuilder",
414      "data": {
415        "description": "this is library_menu"
416      }
417    }
418  ]
419}
420```
421
422Configure the **route_map.json** file in the **library/src/main/module.json5** file of the **library** module.
423```
424{
425  "module": {
426    "name": "library",
427    "type": "shared",
428    "description": "$string:shared_desc",
429    "deviceTypes": [
430      "phone",
431      "tablet",
432      "2in1"
433    ],
434    "deliveryWithInstall": true,
435    "pages": "$profile:main_pages",
436    "routerMap": "$profile:route_map" // Added file.
437  }
438}
439```
440
441The navigation feature is used for page redirection and return. For details, see [Page Navigation](../ui/arkts-navigation-navigation.md#routing-operations).
442