做鸿蒙开发的朋友,大概率都听过那句程序员界的至理名言:“Copy & Paste 是万恶之源”。

当你的项目里充斥着三个以上的业务模块,或者你同时在维护两个极其相似的 APP 时,你会发现,把通用的工具类、精美的 UI 组件甚至高性能的 C++ 算法隔离开来,变成一个个独立的标准件,是多么的重要。

这时候,HAP(业务包)显然承载不了这种跨工程的野心,HSP(HarmonyOS Shared Package,共享包) 才是真正的破局者。

今天,咱们不拽枯燥的概念,直接从底层链路代码实战最新版本适配,把 HSP 如何导出 TS 方法、类、ArkUI 组件以及让无数人头疼的 Native 方法,彻底盘透。


一、 虾米原理:HSP 是如何实现“一处打包,多处运行”的?

在深入写代码之前,我们得先搞清楚 HSP 在整个 ArkUI 编译体系中的生态位。

HSP 本质上是一种动态共享包。与主工程(HAP)编译成一体不同,HSP 会被编译成独立的 .shared 文件,在应用启动时或运行时按需加载。它的导出/导入机制,依赖于一套严密的路径映射与接口暴露协议

1. 入口管制:Index.ets 的唯一性

每一个 HSP 模块,都必须有一个顶层的 Index.ets(或 Index.ts)。它就是这个 HSP 对外的“门面”(Facade 模式)。外部工程绝不能直接穿透到 HSP 的内部目录去拿文件,只能通过 Index.ets 暴露的接口进行间接引用。

2. 依赖寻址:oh-package.json5 的桥梁作用

主工程通过 oh-package.json5 声明对 HSP 的依赖,DevEco Studio 在编译和打包时,会根据这个配置文件建立模块间的软链接,确保运行时的代码寻址不出错。

为了更直观地理解主工程调用 HSP 资源的完整链路,我画了一张流转图:

1. import 引用

2. 路径解析

3. 寻址门面

导出 TS 方法/类

导出 ArkUI 组件

导出 Native 接口

ArkTS 引擎执行

ArkUI 框架渲染

NAPI 桥接层

主工程 Main.ets

oh-package.json5 依赖声明

HSP 模块 hsp-library

Index.ets 统一导出

utils/Calculator.ets

components/SharedButton.ets

cpp/types/libentry/index.d.ts

返回计算结果

显示共享 UI

C++ 底层逻辑 So 库

核心原理一句话总结哦:
主工程发请求 →\rightarrow 配置文件指路 →\rightarrow HSP 门面接客 →\rightarrow 具体实现干活。这套漏斗模型,既保证了模块的独立性,又实现了完美的职责分离。


二、 代码实战:HSP 的四种“硬核”导出姿势

光说不练假把式。咱们新建一个名为 hsp-library 的共享包模块,直接看怎么把不同类型的资源优雅地暴露出去。

1. 导出基础 TS 方法与类(最常用)

这是日常开发中最轻量、也是最高频的操作。比如咱们封装一个数学计算工具。

hsp-library/src/main/ets/utils/MathUtil.ets

// 导出普通的 TS 方法
export function add(a: number, b: number): number {
  return a + b;
}

// 导出 TS 类
export class Calculator {
  multiply(a: number, b: number): number {
    return a * b;
  }
}

2. 导出 ArkUI 组件(跨端 UI 复用)

想把你们团队精心调校过的“渐变按钮”或“增强输入框”共享给全公司?包在 HSP 身上。

hsp-library/src/main/ets/components/SharedButton.ets

@Component
export struct SharedButton {
  @Prop text: string = '确认';
  @BuilderParam onClick: () => void;

  build() {
    Button(this.text)
      .width(200)
      .height(50)
      .backgroundColor(Color.Orange)
      .borderRadius(25)
      .onClick(() => {
        this.onClick();
      })
  }
}

3. 导出 Native 方法(高性能/存量库复用)

这才是真正的重头戏。鸿蒙允许 HSP 内部集成 C++ 代码,通过 NAPI 桥接后暴露给上层 ArkTS 调用。

hsp-library/src/main/cpp/types/libentry/index.d.ts

// 声明 Native 方法的接口
export const nativeAdd: (a: number, b: number) => number;

hsp-library/src/main/ets/utils/NativeBridge.ets

// 导入同模块下的 Native 库
import native from 'libentry.so';

// 包装一层导出,便于后期替换或加日志
export function nativeAddWrapper(a: number, b: number): number {
  return native.nativeAdd(a, b);
}

4. 组装门面:Index.ets 的配置

有了上面的零件,我们在 HSP 的根目录把它们一股脑导出去。

hsp-library/src/main/ets/Index.ets

// 1. 导出 TS 资源
export { add, Calculator } from './utils/MathUtil';

// 2. 导出 UI 组件
export { SharedButton } from './components/SharedButton';

// 3. 导出 Native 包装器
export { nativeAddWrapper } from './utils/NativeBridge';

三、 主工程调用与差异对比

在主线中引用这些内容。

主工程任意 Page

// 这里的路径对应 oh-package.json5 中配置的依赖别名
import { add, Calculator, SharedButton, nativeAddWrapper } from 'hsp-library';

@Entry
@Component
struct MainPage {
  build() {
    Column({ space: 20 }) {
      Text(`TS 方法计算结果: ${add(1, 2)}`)
      
      // 使用 HSP 导出的类
      Text(`TS 类计算结果: ${new Calculator().multiply(3, 4)}`)

      // 使用 HSP 导出的组件
      SharedButton({ text: "我是 HSP 的按钮", onClick: () => {
        // 调用 HSP 导出的 Native 方法
        const res = nativeAddWrapper(5, 6);
        console.log(`Native 方法计算结果: ${res}`);
      }})
    }
    .width('100%')
    .padding(20)
  }
}

避坑四种导出方式的差异对比

导出类型 底层机制 适用场景 常见坑点
TS 方法/类 ArkTS 虚拟机组播 纯逻辑、数据结构、工具类 注意闭包内不要持有外部非共享上下文
ArkUI 组件 组件工厂模式注册 跨项目 UI 标准化 组件内部状态管理需谨慎,避免和外部强耦合
Native 方法 NAPI 符号表导出 音视频处理、加密算法、游戏引擎 ABI 架构匹配(arm64-v8a),API 版本对齐

四、HSP 新挑战

随着 HarmonyOS 6.0.2 (API 22) 的发布,底层的编译工具和权限管控迎来了史诗级更新。如果你正在准备将 HSP 迁移到 API 22,这几个痛点你必须知道:

1. 更严格的 NAPI 接口兼容性检查

在 API 22 中,系统对 NAPI 接口的向后兼容性审查达到了变态级别。如果你在 HSP 中使用的某些 NAPI 接口在 API 22 已被标记为废弃(Deprecated),编译器会直接阻断打包。
适配策略:必须在 HSP 的 Index.ets 中加入版本守卫(Version Guard),正如前文重构篇所述,通过 canIUsedeviceInfo.sdkApiVersion 做运行时分流。

2. 构建工具链 (Hvigor) 的校验强化

API 22 配套的 DevEco Studio 6 大幅增强了 Hvigor 构建脚本的校验能力。
现在,如果你的 HSP 试图导出一个未被 Index.ets 声明的内部文件,或者 oh-package.json5 中的 main 字段指向错误,IDE 会直接报出红色的 Cyclic dependency(循环依赖)Module not found 错误,并且在编译期就会失败,而不是像以前那样等到运行时才 Crash。

3. 跨 HSP 的 ArkUI 状态管理限制

在最新的 API 22 渲染引擎中,为了提高大型 WaterFlowList 的滚动帧率,框架对跨包(HSP)传递 @ObjectLink@Link 等深层响应式装饰器做出了更严苛的限制。
实战建议:在 API 22 环境下,HSP 导出的组件,其对外暴露的属性尽量使用 @Prop(深拷贝)或基础的 @State 配合回调函数(EventEmitter 模式)来进行通信,以减少跨包序列化的性能损耗。


总结一下下

HSP 绝对是鸿蒙生态里最迷人的架构利器之一。

它就像是给了你一个“代码集装箱”,你可以把公司内部沉淀的算法、UI 规范甚至第三方 SDK 统统打包成 HSP。主工程就像搭积木一样,需要什么就 import 什么。

不过,权力越大,责任越大。过度拆分 HSP 会导致包体积管理复杂度直线上升,甚至出现依赖地狱(Dependency Hell)。我的建议是:在项目初期适度冗余,当某段逻辑或 UI 第三次被复用时,果断将其抽离为 HSP。

希望这篇解析能帮你打通鸿蒙模块化开发的任督二脉。如果在配置 build-profile.json5 或编写 NAPI 时遇到奇葩的 Link 错误,欢迎随时交流,我们一起在鸿蒙的浪潮里乘风破浪!

Logo

作为“人工智能6S店”的官方数字引擎,为AI开发者与企业提供一个覆盖软硬件全栈、一站式门户。

更多推荐