Capacitor 插件鸿蒙化实战:@capacitor/device 从安装到模拟器验证全记录
本文聚焦插件层 :以 CPF-Ionic 鸿蒙化的
@capacitor/device插件为例,完整走通"安装插件 → React 调用 → 构建签名 → 模拟器运行验证"全链路,所有步骤实测。
一、插件生态背景
1.1 CPF-Ionic 插件体系
Capacitor 的设备能力全部通过插件(@capacitor/xxx)提供。华为 CPF-Ionic 团队按"原插件包名不变、原生实现替换为 OHOS "的原则完成了 29 个官方插件 + 14 个 Ionic 三方插件的鸿蒙化,完整清单见 ionic-readme(已同步至 AtomGit,组织主页 https://atomgit.com/cpf-ionic ):
- Capacitor 官方插件:app、browser、camera、device、filesystem、keyboard、barcode-scanner、app-launcher、clipboard、geolocation、haptics、push-notifications、network、share、status-bar、text-zoom、action-sheet、dialog、screen-reader、splash-screen、toast、file-transfer、file-viewer、inappbrowser、local-notifications、motion、preferences、privacy-screen、screen-orientation
- Ionic 三方插件 (
@ionic-native/xxx对应):status-bar、splash-screen、file、in-app-browser、device、file-transfer、app-version、camera、clipboard、file-opener、keyboard、network、android-permissions、pdf-generator
1.2 鸿蒙化插件的双包结构
每个鸿蒙化插件由两个 npm 包组成:
| 包 | 角色 | 安装位置 |
|---|---|---|
@capacitor/device(官方原包) |
Web/API 层:TS 类型定义 + Promise API,Web 层调用的入口 | 前端工程 node_modules |
@capacitor-ohos/device |
OHOS 原生实现:ArkTS + C++(Device.ets / Device.cpp / Device.h) | 由 hionic 拷入 openharmony/capacitor/ 参与编译 |
Web 层调用 Device.getInfo() 时代码与 Android/iOS 完全一致------插件鸿蒙化的全部意义就在于此:API 契约不变,原生实现替换。
1.3 插件接入鸿蒙工程需要做的四件事
查看插件的 plugin.xml 可知,一个 OHOS Capacitor 插件接入壳工程需要四处修改:
- 插件注册表 :
entry/src/main/resources/rawfile/capacitor.plugins.json加{"pkg": "@capacitor/device", "classpath": "Device"}; - CMake 编译 :
capacitor/src/main/cpp/CMakeLists.txt加add_subdirectory(Device)并链入Device库; - 源码拷贝 :C++(Device.h/.cpp/CMakeLists.txt)→
cpp/Device/,ArkTS(Device.ets)→ets/components/Device/; - ArkTS 编译范围 :
capacitor/build-profile.json5的buildOption.arkOptions.runtimeOnly.sources加入 Device.ets。
好消息:这四步 hionic 全部自动化,一条命令搞定(见下文)。
二、环境
延续上一篇的 capacitorMyApp 项目(hionic 2.1.16 / Capacitor 8.5.2 / @capacitor-ohos/ohos 8.0.2 / openssl 已集成 / 模拟器 127.0.0.1:5555 在线)。
三、逐步操作过程
第 1 步:安装插件
bash
hionic plugin add @capacitor/device
一条命令,hionic 自动完成了上面第一节的全部四件事,关键输出:
log: ✓ Added runtimeOnly source: ./src/main/ets/components/Device/Device.ets
log: Installing CMakeLists configuration to: src/main/cpp/CMakeLists.txt
log: Updated CMakeLists.txt: src/main/cpp/CMakeLists.txt
log: - add_subdirectory(Device)
log: Installing config-json to: src/main/resources/rawfile/capacitor.plugins.json
log: Successfully added plugin: @capacitor/device
验证四处修改都已落盘:
bash
# ① 注册表
$ cat openharmony/entry/src/main/resources/rawfile/capacitor.plugins.json
[
{ "pkg": "@capacitor/CapacitorPlugin", "classpath": "CapacitorPlugin" },
{ "pkg": "@capacitor/device", "classpath": "Device" } ← 新增
]
# ② CMake
$ grep Device openharmony/capacitor/src/main/cpp/CMakeLists.txt
add_subdirectory(Device) ← 第 20 行
Device ← target_link_libraries 中链入
# ③ 源码
$ ls openharmony/capacitor/src/main/cpp/Device/
CMakeLists.txt Device.cpp Device.h
$ ls openharmony/capacitor/src/main/ets/components/Device/
Device.ets
# ④ npm 双包
$ grep device package.json
"@capacitor-ohos/device": "^8.0.2" ← 鸿蒙原生实现
"@capacitor/device": "^8.0.3" ← 官方 API 层
卸载 同样一条命令:
hionic plugin remove @capacitor/device。
第 2 步:React 层调用插件
修改 src/App.jsx(与 Android/iOS 上的写法完全一致):
jsx
import { useState, useEffect } from 'react'
import { Device } from '@capacitor/device'
function App() {
const [device, setDevice] = useState(null)
useEffect(() => {
const load = async () => {
try {
const [info, id, battery, lang] = await Promise.all([
Device.getInfo(),
Device.getId(),
Device.getBatteryInfo(),
Device.getLanguageTag(),
])
setDevice({ info, id, battery, lang })
console.log('Device info:', info)
console.log('Device id:', id)
} catch (e) {
console.error('Device plugin error:', e)
}
}
load()
}, [])
// ... 渲染部分追加设备信息区块
return (
<>
{/* 原有 UI */}
{device && (
<div className="device-info">
<h2>Device Info (Capacitor OHOS)</h2>
<ul>
<li>name: {device.info.name}</li>
<li>model: {device.info.model}</li>
<li>platform: {device.info.platform}</li>
<li>operatingSystem: {device.info.operatingSystem}</li>
<li>osVersion: {device.info.osVersion}</li>
<li>manufacturer: {device.info.manufacturer}</li>
<li>isVirtual: {String(device.info.isVirtual)}</li>
<li>memUsed: {device.info.memUsed} bytes</li>
<li>identifier (ODID): {device.id.identifier}</li>
<li>batteryLevel: {device.battery.batteryLevel ?? 'N/A'}</li>
<li>isCharging: {String(device.battery.isCharging)}</li>
<li>languageTag: {device.lang.value}</li>
</ul>
</div>
)}
</>
)
}
一次并发调用四个 API,覆盖插件的全部五类能力中的四类(getInfo / getId / getBatteryInfo / getLanguageTag)。
第 3 步:构建 → 同步 → 编译 HAP
Capacitor 标准三连(注意每次改完前端都要重新 buildui + sync):
bash
hionic buildui # vite build → dist/(233KB JS)
hionic sync openharmony # dist → rawfile/www/,同时刷新插件注册
# 编译鸿蒙工程
cd openharmony
export DEVECO_SDK_HOME=/Applications/DevEco-Studio.app/Contents/sdk
export PATH=".../tools/hvigor/bin:.../tools/node/bin:.../tools/ohpm/bin:$PATH"
hvigorw assembleHap --mode module -p module=entry@default -p product=default \
-p requiredDeviceType=phone --no-daemon
输出:
> hvigor Finished :entry:default@SignHap... after 917 ms
> hvigor BUILD SUCCESSFUL in 4 s 896 ms
41 tasks in total: 32 executed, 9 up-to-date
注意这次编译比上次多了 Device.cpp 的 NAPI 编译任务(32 executed vs 上次 26),签名配置沿用 DevEco 自动签名,直接产出 signed HAP。
第 4 步:安装到模拟器并运行
bash
hdc list targets # 127.0.0.1:5555
hdc install -r openharmony/entry/build/default/outputs/default/entry-default-signed.hap
# [Info] install bundle successfully
hdc shell aa start -a EntryAbility -b com.nutpi.MyApp
# start ability successfully.
第 5 步:验证
日志验证 (hdc shell hilog -x | grep ARKWEB-CONSOLE)------插件调用真实发生并返回:
ARKWEB-CONSOLE: "Device info: [object Object]" ← Device.getInfo() 成功
ARKWEB-CONSOLE: "Device id: [object Object]" ← Device.getId() 成功
截图验证 (hdc shell snapshot_display -f /data/local/tmp/cap_device.jpeg + hdc file recv):

页面完整渲染出设备信息:
| 字段 | 模拟器返回值 | 说明 |
|---|---|---|
| name / model | emulator | |
| platform / operatingSystem | HarmonyOS | 插件正确上报鸿蒙平台 |
| osVersion | OpenHarmony-7.0.0.105 | 模拟器 ROM 版本 |
| manufacturer | HUAWEI | |
| isVirtual | true | 正确识别模拟器(真机应为 false) |
| memUsed | 336972 bytes | 应用内存占用 |
四、桥接链路解析
一次 Device.getInfo() 调用的完整数据流:
React (App.jsx)
│ Device.getInfo() ← @capacitor/device 官方 API 层,跨平台一致
▼
capacitor.js / native-bridge.js ← rawfile/www/ 下的桥接脚本
│ JSON 消息 {plugin:"Device", method:"getInfo"}
▼
ArkWeb Web 组件拦截 → ArkTS 容器层
▼
capacitor.plugins.json 注册表 → 找到 classpath "Device"
▼
Device.ets(ArkTS)→ Device.cpp(C++/NAPI,编译进 libcapacitor.so)
│ 调用 OpenHarmony 系统 API(@ohos.deviceInfo / batteryInfo / i18n)
▼
结果 JSON 回传 → Promise resolve → React setState → 页面渲染
关键点:
- API 契约不变 :
getId()在 OHOS 上返回 ODID(OpenHarmony 设备标识),Android 返回 GUID,iOS 返回 UIDevice identifier------语义等价,Web 层无感; - 注册表驱动 :
capacitor.plugins.json的pkg/classpath映射是插件查找的依据,这就是为什么手动接入时漏掉第①步会导致插件调用无响应; - 编译双链 :ArkTS 文件要进
runtimeOnly.sources,C++ 要进 CMake,漏一个都会在运行时报 "plugin not found" 或编译期报符号缺失。
五、踩坑复盘
本项目插件环节很顺利(hionic 自动化程度高),但有几个易错点值得记录:
| # | 易错点 | 现象 | 规避方法 |
|---|---|---|---|
| 1 | 改了前端忘记 sync | 模拟器上跑的还是旧页面 | 每次 buildui 后必须 hionic sync openharmony,再重新 hvigorw 编译 |
| 2 | 混淆 hionic 插件安装方式 | 以为要按 README"手动引入"四步走 | 命令行安装(hionic plugin add)会自动完成四步,手动引入仅用于 hionic 无法处理的特殊插件 |
| 3 | console.log 看不到输出 | ArkWeb 的 console 走 hilog | 用 `hdc shell hilog -x |
| 4 | 截图时机 | 应用启动有加载过程 | aa start 后 sleep 3 秒再 snapshot_display |
| 5 | 前置条件 | Device.cpp 编译失败 | 确认上一篇的 openssl(libs + 头文件)已集成------capacitor 原生库与插件共享同一 CMake 工程 |
六、总结
在鸿蒙上给 Capacitor 应用装插件,核心路径可以概括为三步走:
1. 一条命令 ── hionic plugin add @capacitor/xxx,四项工程修改全自动
2. 零改动 ── React 层 import 原包调用,API 与 Android/iOS 完全一致
3. 三连循环 ── buildui → sync → hvigorw,hilog + snapshot 验证
以 @capacitor/device 为样板验证后,CPF-Ionic 清单里的其余 40+ 插件(camera、geolocation、filesystem......)都可按同样路径接入。唯一需要留意的差异是各插件 README 中标注的权限要求(如 geolocation 需在 module.json5 声明定位权限及 reason)------device 插件无需权限,是最适合作为首个验证的插件。