拆解一个鸿蒙化插件:CPF-Ionic 是如何把 43 个 Capacitor 插件搬上 OpenHarmony 的
本文换一个角度------贡献者/适配者视角 ,深入一个插件的内部:以
@capacitor-ohos/device(https://atomgit.com/cpf-ionic 组织下的 capacitor-device)为解剖标本,逐文件分析 CPF-Ionic 的插件工程模板、plugin.xml 安装协议、ArkTS+C++ 双层实现,最后总结出一套"如何为任意 Capacitor 插件做鸿蒙化适配"的可复用方法论。所有代码路径、行号均来自本机
node_modules/@capacitor-ohos/device实际解包验证。
一、为什么值得解剖一个插件
CPF-Ionic 组织(https://atomgit.com/cpf-ionic )适配了 43 个插件,但逐个读一遍不现实。幸运的是,这批插件是同一个模板批量化生产的------从组织仓库列表可以验证这一点:
| 证据 | 数据(实测组织 API) |
|---|---|
| 主语言分布 | ArkTS / C++ 两种,且按能力域规律分布(传感器类 ArkTS、网络/IO 类 C++) |
| 创建时间 | 大量插件集中在 2026-01-27 ~ 03-03 两个批次创建(capacitor-* 一批、ionic-native-* 一批) |
| 目录结构 | capacitor-device 与 capacitor-geolocation、capacitor-haptics 等同构 |
一个插件 = 全部插件的缩影。读懂 device 的源码结构,剩下 42 个就是换 API 而已。
二、解剖标本:@capacitor-ohos/device
2.1 npm 包形态
node_modules/@capacitor-ohos/device/
├── package.json # npm 包描述(name/version/peerDependencies)
├── plugin.xml # ★ 安装协议(本文核心,见第三节)
├── LICENSE / OAT.xml # MIT 协议 / 开源声明
├── README.md(.en.md) # 使用文档
└── src/ # 源码
└── main/
├── cpp/Device/ # C++ 层
│ ├── Device.h
│ ├── Device.cpp
│ └── CMakeLists.txt
└── ets/components/Device/
└── Device.ets # ArkTS 层
两个关键设计决策:
决策 1:Web API 层复用官方 npm 包。 @capacitor/device(官方)提供 TS 类型与 Promise API,@capacitor-ohos/device 只提供原生实现,两者通过 Capacitor 的插件注册机制对接。适配者不 fork 官方 TS 代码,官方升级时零成本跟进。
决策 2:源码分发而非 HAR 二进制。 与框架层 @capacitor-ohos/ohos(HAR)不同,插件包直接携带 .ets/.cpp 源文件,安装时由 CLI 拷入宿主工程参与编译。好处是插件与宿主工程的 CMake/ArkTS 编译体系无缝融合,坏处是需要一套可靠的"安装协议"------这就是 plugin.xml。
2.2 ArkTS 层(Device.ets)
Device.ets 实现的是 Capacitor 的插件基类契约(@capacitor-ohos/ohos 导出的 CapacitorPlugin),把系统 API 包成桥接方法:
@capacitor/Device.getInfo()
→ native-bridge.js → ArkWeb 容器
→ Device.ets(注解注册的方法)
→ @ohos.deviceInfo / @ohos.batteryInfo / i18n.System
以 getInfo 为例,实测返回值映射(模拟器验证):
| 官方字段 | OHOS 数据源 | 模拟器返回 |
|---|---|---|
| platform / operatingSystem | 固定 "HarmonyOS" | HarmonyOS |
| osVersion | deviceInfo.distribution 系列 |
OpenHarmony-7.0.0.105 |
| model / name | deviceInfo.productModel / marketName |
emulator |
| isVirtual | deviceInfo.productModel 模拟器判定 |
true |
| memUsed | process.ProcessStatus(本应用内存) |
336972 bytes |
| identifier(getId) | ODID(OpenHarmony 匿名设备标识) | 非 null 字符串 |
注意两个语义对齐的细节(适配质量的分水岭):
getId()在 Android 返回 ANDROID_ID、iOS 返回 UIDevice identifier,OHOS 用 ODID 对位------三者的共同语义是"可重置的匿名设备标识",而不是设备序列号,隐私级别一致;iOSVersion/androidSDKVersion字段在 OHOS 保留但无意义(README 明确标注"仅跨平台兼容"),保证 TS 类型签名不变。
2.3 C++ 层(Device.cpp)
C++ 层通过 NAPI 把 ArkTS 方法暴露进 libcapacitor.so,并被 CMake 链接进插件库。为什么简单如 device 也要 C++?------因为插件的安装协议统一走 capacitor 模块的 CMakeLists(见下节),保持"所有插件一个编译体系",与 framework 层的 C++ 网络栈(Socket/SSLSocket/openssl)共享构建管线。观察组织仓库的主语言分布可以印证分工规律:
| 主语言 | 插件类型 | 例 |
|---|---|---|
| C++ | 网络/IO/编解码密集 | capacitor-clipboard、capacitor-preferences、ionic-native-network、ionic-native-file-opener、ionic-native-pdf-generator |
| ArkTS | 系统 API 调用密集 | capacitor-geolocation、capacitor-haptics、capacitor-motion、capacitor-screen-orientation、ionic-native-camera |
三、plugin.xml:一套声明式的插件安装协议
这是 CPF-Ionic 工程化中最精巧的部分。Capacitor 原生的插件安装靠 npm + 注解扫描,但鸿蒙壳工程的 CMakeLists / capacitor.plugins.json / runtimeOnly.sources 需要手工修改------plugin.xml 就是把这些"手改动作"声明式描述出来,让 hionic 自动执行。
实测 device 的 plugin.xml(行号来自实际文件):
xml
<!-- L29-34:插件注册表注入 -->
<config-json target="src/main/resources/rawfile/capacitor.plugins.json" parent="/*" modules-targets-name="default">
{"pkg": "@capacitor/device", "classpath": "Device"}
</config-json>
<!-- L36-38:CMake 工程注入 -->
<CMakeLists target="src/main/cpp/CMakeLists.txt" modules-name="capacitor">
add_subdirectory(Device) + target_link_libraries(capacitor ... Device ...)
</CMakeLists>
<!-- L40-42:C++ 源文件拷贝 -->
<source-file type="h" src="src/main/cpp/Device/Device.h" target-dir="src/main/cpp/Device" modules-name="capacitor"/>
<source-file type="cpp" src="src/main/cpp/Device/Device.cpp" target-dir="src/main/cpp/Device" .../>
<source-file type="txt" src="src/main/cpp/Device/CMakeLists.txt" target-dir="src/main/cpp/Device" .../>
<!-- L43:ArkTS 源文件拷贝(runtimeOnly 标记) -->
<source-file type="ets" src="src/main/ets/components/Device/Device.ets" target-dir="src/main/ets/components/Device" modules-name="capacitor" runtimeOnly="true"/>
四类指令对应四个注入点:
| 指令 | 注入目标 | 对应手改步骤 |
|---|---|---|
config-json |
rawfile/capacitor.plugins.json | 运行时插件注册表(pkg→classpath 映射) |
CMakeLists |
capacitor 模块 CMakeLists.txt | add_subdirectory + 链接 |
source-file (h/cpp/txt) |
cpp/Device/ | NAPI 编译单元 |
source-file (ets, runtimeOnly) |
ets/components/Device/ + build-profile 的 sources | ArkTS 编译范围 |
这套协议的价值 :插件作者只需写一个 plugin.xml 描述"我要怎么装",安装/卸载全部由 hionic 完成(hionic plugin add/remove)。对比 Cordova 生态的 plugin.xml------这正是对 Cordova 安装协议的致敬与移植(Cordova 的 plugin.xml 同样声明 source-file、config-file、framework),存量插件作者迁移到鸿蒙时心智模型不变。
四、可复用的适配方法论:如何鸿蒙化一个新插件
综合框架层(@capacitor-ohos/ohos)与 device 标本,CPF-Ionic 的插件适配可以归纳为五步法:
第 1 步:API 盘点 ── 列出官方插件全部导出方法与类型签名(TS .d.ts)
第 2 步:系统映射 ── 每个方法找 OHOS 对应 API,标注语义差异
第 3 步:双层实现 ── Device.ets(ArkTS 调系统 API)+ Device.cpp(NAPI 导出)
第 4 步:安装协议 ── 写 plugin.xml 声明四类注入点
第 5 步:README 对齐 ── 按 ionic-readme 模板写中英文档(API 签名表 + OHOS 差异说明)
第 2 步是真正的难点,附一张常见映射速查表(从 43 个插件的 README 归纳):
| Capacitor API 域 | Android 来源 | OHOS 对应 |
|---|---|---|
| 设备信息 | android.os.Build | @ohos.deviceInfo |
| 电量 | BatteryManager | @ohos.batteryInfo |
| 定位 | LocationManager | @ohos.geoLocationManager(需声明权限 + reason) |
| 传感器 | SensorManager | @ohos.sensor |
| 剪贴板 | ClipboardManager | @ohos.pasteboard |
| 振动 | Vibrator | @ohos.vibrator |
| 文件 | java.io / SAF | @ohos.file.fs + Application Context |
| 通知 | NotificationManager | @ohos.notificationManager(需 notificationSlots) |
| 屏幕方向 | ActivityInfo | @ohos.window.setPreferredOrientation |
| 浏览器打开 | Intent ACTION_VIEW | @ohos.router / Web 组件 / startAbility |
语义对齐三原则(从 device 标本提炼):
- 返回值结构不变------TS 类型签名一个字段都不能少,无对应概念的填"仅跨平台兼容"占位;
- 隐私级别对等------标识符类 API 选同等隐私强度的实现(如 ODID 对 ANDROID_ID);
- 权限声明显式化------OHOS 的权限模型比 Android 严格(reason 必须是 $string 资源引用),插件 README 必须写清权限要求,否则运行时静默失败。
五、从组织仓库列表看到的生态规律
把 43 个插件按创建时间与主语言排布,能读出团队的适配节奏:
| 阶段 | 时间 | 内容 |
|---|---|---|
| 框架期 | 2026-01 下旬 | capacitor-cli(27 star,生态最热)、框架层、首批插件(haptics/geolocation/clipboard/app-launcher,负责人 li_in) |
| 批量期 | 2026-01-29 | capacitor-* 系列一夜补齐(preferences/motion/local-notifications/file-*/privacy-screen/screen-orientation 等 15+,负责人 gcw_MsFzKdqw) |
| Ionic 兼容期 | 2026-03-03 | ionic-native-* 系列 14 个集中创建(status-bar/splash-screen/file/in-app-browser/camera/clipboard...),服务 Ionic Angular 存量用户 |
| 长尾期 | 2026-03 后 | capacitor-native-settings 等特色插件 |
两个负责人、四次集中创建------典型的"模板 + 批量生产 + 双轨兼容(Capacitor API 与 Ionic Native API 各一套)"打法。这也解释了为什么 ionic-native-* 系列 14 个插件与 capacitor-* 功能高度重叠:Ionic Angular 项目的存量代码 import 的是 @ionic-native/xxx,为这部分用户单独维护一层兼容壳,避免强制用户改 import。
六、总结
从一个插件标本看到的是 CPF-Ionic 的适配流水线:
plugin.xml(声明式安装协议)
× 双层实现(ArkTS 调 API + C++ NAPI 编译)
× 语义对齐三原则(类型不变 / 隐私对等 / 权限显式)
× 双轨 API(capacitor-* 与 ionic-native-* 各一套)
= 43 个插件批量鸿蒙化
对想参与生态共建的开发者,这篇解剖给出的行动路径是:挑一个列表里没有的官方插件 → 套五步法 → 参考 device 的 plugin.xml 写安装协议 → 提 PR 到 CPF-Ionic。模板已备好,缺的只是你的那一个插件。