开源鸿蒙平台 KMP/CMP 三方库「Okio」适配全流程
本文记录
okio接入 OpenHarmony 的完整过程,覆盖 KMP/CMP 工程盘点、ohosArm64目标、Kotlin/Native 动态库、C ABI/N-API 桥接、ArkUI 真机页面、HAP 签名和真机验收。本次适配复用 Okio 的 Kotlin Multiplatform 公共 API 和 Buffer 实现,再由 OpenHarmony Stage 宿主通过 C ABI 和 N-API 调用 Kotlin/Native 动态库。这样验证的是同一份 KMP 代码在 OpenHarmony ARM64 设备上的运行结果,而不是在 ArkTS 页面中重新实现一套字符串处理逻辑。
项目地址: AtomGit/oh-tpc/okio
开发工具: 华为云码道
一、背景
1.1 为什么做开源鸿蒙平台 KMP/CMP 适配
Okio 是 Kotlin 生态中用于字节流、缓冲区、文件系统和数据处理的基础库。KMP/CMP 应用通常把业务和数据处理放在 commonMain,再由不同平台提供底层实现。要让 OpenHarmony 应用继续使用同一套 Okio API,关键是让核心库真正进入 ohosArm64 Kotlin/Native 运行时,并完成从 Native 到 ArkUI 的调用链路。
如果只在 ArkTS 页面中拼接一个“通过”文本,无法证明 Okio 的 Buffer、UTF-8 编解码和 Kotlin/Native 内存边界已经工作。完整适配需要解决下面几个问题:
| 障碍 | 具体问题 |
|---|---|
| 目标缺失 | KMP 模块默认没有 OpenHarmony 目标,必须增加 ohosArm64() 才能生成 OpenHarmony KLIB。 |
| 平台实现差异 | Okio 的 POSIX 文件系统代码依赖 DEFFILEMODE、statx 等 Unix 接口,OpenHarmony 头文件并不完全提供这些符号。 |
| 工具链不一致 | Kotlin/Native、OpenHarmony LLVM、Native SDK、CMake、Ninja 和 Gradle 插件必须使用匹配版本。 |
| 语言边界不同 | ArkTS 不能直接持有 Kotlin Buffer 或 Kotlin data class,需要经过 C ABI、C++ N-API 和字符串边界。 |
| 内存生命周期 | Kotlin/Native 返回的 C 字符串必须由明确的 Free 入口释放,否则页面反复点击会造成 Native 堆泄漏。 |
| 交付链路复杂 | Native 动态库、CMake、HAP、签名、设备安装和 ARM64 ABI 需要分别验证。 |
因此,本项目把适配边界放在三个地方:Okio 的 ohosArm64 平台配置、Kotlin/Native C ABI/N-API 桥接层和 ArkUI 验收页面。Okio 的公共 API 保持不变,ArkTS 只负责宿主生命周期和结果展示。
1.2 库提供的能力
本次示例使用 Okio 的基础 Buffer API 验证跨语言链路:
Buffer():创建 Okio 缓冲区;writeUtf8():把 UTF-8 文本写入缓冲区;readUtf8():从缓冲区读取完整文本;- KMP
commonMain代码:由 JVM 和ohosArm64共用; OkioExamples.checks():在共享 Kotlin 代码中返回验收结果;- Kotlin/Native C ABI:返回 JSON 字符串并提供显式释放函数。
示例的结果约定如下:
| 字段 | 示例值 | 含义 |
|---|---|---|
value | OpenHarmony / Okio | Buffer UTF-8 写入和读取后的文本。 |
checks | passed | 共享 Kotlin 验收逻辑通过。 |
示例只验证基础 Buffer 路径。文件系统、压缩、同步、Socket 和其他 Okio API 仍应结合实际业务补充专项测试。
1.3 实现适配
| 维度 | 要求 |
|---|---|
| 代码复用 | Okio 的公共 API和 commonMain 实现继续由 KMP 管理。 |
| 平台目标 | 增加 ohosArm64,生成 OpenHarmony KLIB 和 libokio.so。 |
| 平台兼容 | 在 ohosMain 提供 OpenHarmony POSIX 兼容实现。 |
| 桥接稳定 | 使用少量 C ABI 函数和 JSON,避免把 Kotlin 对象地址交给 ArkTS。 |
| UI 完整 | 页面支持 Native 调用、结果展示和错误显示。 |
| 可测试 | JVM 测试、Kotlin/Native 链接、HAP 构建、签名安装和真机页面分别验收。 |
| 签名安全 | 仓库只保留签名配置入口,证书和密钥材料由开发者本机配置。 |
| 仓库规范 | 项目说明、文章、效果图和源码链接统一使用 AtomGit。 |
本项目的 ArkUI 页面是独立的真机验收宿主,Okio 公共 API 仍然保持平台无关。CMP 应用可以在
commonMain中继续依赖 Okio,再由自己的 OpenHarmony 宿主决定 UI 和系统能力封装方式。
二、实现路线图
第 1 阶段:项目初始化 ── 盘点 Okio KMP 模块、公共 API 和 OpenHarmony 示例边界
第 2 阶段:目标与依赖打通 ── 加入 ohosArm64、独立 example 工程和 Native 构建任务
第 3 阶段:平台兼容处理 ── 增加 ohosMain,兼容 OpenHarmony POSIX 头文件差异
第 4 阶段:原生桥接 ── Kotlin/Native C ABI、C++ N-API、JSON 和内存释放
第 5 阶段:ArkUI 宿主 ── Stage 工程、CMake、资源、页面和 Native 模块注册
第 6 阶段:示例与验证 ── HAP 构建、签名、设备安装、Okio Buffer 和效果图
每个阶段都使用真实产物作为下一阶段输入:先用 JVM 验证共享示例,再把 Okio 链接为 ARM64 动态库,随后由 CMake 和 N-API 加载到 Stage 工程,最后安装签名 HAP 观察页面返回结果。
三、逐步实现过程
第 1 阶段:项目初始化
1.1 盘点公共 API 和工程边界
项目采用与参考 KMP/CMP 工程一致的分层:
okio/ Okio 核心 KMP 模块
okio/src/ohosMain/ OpenHarmony 平台兼容实现
example/shared/ 共享示例门面和 JVM 验收测试
example/nativeApp/ ohosArm64 Kotlin/Native 动态库
example/ohosApp/ DevEco Stage 工程和 ArkUI 页面
scripts/ OpenHarmony Native 构建辅助脚本
docs/openharmony/ 验收记录和真机效果图
example 是独立 Gradle 工程,不把 DevEco 工程作为 Kotlin 子模块。这样可以分别执行 Gradle 和 Hvigor,也可以将 ohosApp 复制到其他目录后在 DevEco Studio 中配置签名。
1.2 固定工具链和版本矩阵
本项目使用以下版本约定:
| 项目 | 配置 | 用途 |
|---|---|---|
| Kotlin Multiplatform | 仓库 libs.versions.toml 中的版本 | JVM、Kotlin/Native 和 KLIB |
| OpenHarmony 目标 | ohosArm64 | ARM64 真机动态库 |
| DevEco product | 6.0.0(20) | 工程兼容和目标版本 |
| ABI | arm64-v8a | HAP 原生库架构 |
| Native 构建 | CMake + Ninja | 构建 N-API 入口和链接 Okio |
执行 Gradle 脚本前先确认 JDK 和 DevEco SDK:
java -version
/Applications/DevEco-Studio.app/Contents/tools/node/bin/node \\
/Applications/DevEco-Studio.app/Contents/tools/hvigor/bin/hvigorw.js --version
OpenHarmony SDK 的实际路径以本机 DevEco Studio 配置为准。工具链可用只说明工程可以编译,不能替代 ARM64 真机上的运行验证。
1.3 创建 OpenHarmony 示例目录
示例页面围绕“共享 Okio 结果、Native 调用和验收状态”组织:
标题区 KMP Okio OpenHarmony
结果区 {"value":"OpenHarmony / Okio","checks":"passed"}
操作区 运行 Okio 检查
按钮调用真实的 libentry.so N-API 方法,Native 方法继续调用 Kotlin/Native 导出的 OkioText,而不是在 ArkTS 页面中直接写死结果。
第 2 阶段:目标与依赖打通
2.1 加入 ohosArm64 目标
Okio 模块在保留现有平台目标的基础上增加 OpenHarmony:
kotlin {
ohosArm64()
sourceSets {
val ohosMain by getting {
dependsOn(nonJvmMain)
}
}
}
ohosArm64 继承公共 KMP 源集和非 JVM 实现,平台差异集中放在 okio/src/ohosMain。JVM 和其他平台的 API 不需要因为 OpenHarmony 示例而改变。
核心目标可以单独编译:
./gradlew :okio:compileKotlinOhosArm64
2.2 Native focused build 的作用
example/nativeApp 只负责生成受 linker map 约束的 shared library:
kotlin {
ohosArm64 {
binaries.sharedLib {
baseName = "okio"
linkerOpts(
"--entry=0",
"--version-script=${project.file("src/ohosArm64Main/linker/shared-library.map")}",
"-lace_napi.z",
"-luv",
"-lhilog_ndk.z",
)
}
}
}
示例只导出两个业务相关 C ABI 符号和一个释放符号:
OkioText
OkioFree
减少 ABI 符号可以降低跨语言生命周期和兼容风险。Okio 的内部类、Buffer 对象和 Kotlin 运行时对象不会暴露给 ArkTS。
2.3 配置仓库和独立消费工程
example/settings.gradle.kts 使用 Maven Local、Maven Central 和 OpenHarmony Kotlin 插件仓库:
pluginManagement {
repositories {
maven("https://maven.eazytec-cloud.com/nexus/repository/maven-public/")
mavenCentral()
gradlePluginPortal()
}
}
dependencyResolutionManagement {
repositories {
mavenLocal()
maven("https://maven.eazytec-cloud.com/nexus/repository/maven-public/")
mavenCentral()
}
}
共享示例使用 OpenHarmony 版本的 Okio:
sourceSets {
commonMain.dependencies {
api("com.squareup.okio:okio:3.19.0-ohos.1")
}
}
发布到本机 Maven Local 后,也可以让独立示例消费最新构建产物:
./gradlew :okio:publishToMavenLocal -PsigningEnabled=false
2.4 通过构建产物消费共享库
prepareOhos 在 Native 链接成功后复制动态库和 C 头文件:
val prepareOhos by tasks.registering(Copy::class) {
dependsOn("linkDebugSharedOhosArm64")
from(layout.buildDirectory.dir("bin/ohosArm64/debugShared")) {
include("libokio.so")
into("libs/arm64-v8a")
}
from(layout.buildDirectory.dir("bin/ohosArm64/debugShared")) {
include("libokio_api.h")
into("src/main/cpp/include")
}
into(rootProject.layout.projectDirectory.dir("ohosApp/entry"))
}
动态库和头文件来自同一次 Native 构建,避免头文件与 .so 版本不一致。准备完成后应存在:
example/ohosApp/entry/libs/arm64-v8a/libokio.so
example/ohosApp/entry/src/main/cpp/include/libokio_api.h
第 3 阶段:平台兼容处理
3.1 OpenHarmony POSIX 兼容边界
Okio 的 Unix 实现依赖系统文件模式和文件状态查询。OpenHarmony Native SDK 中部分头文件与常见 Linux/macOS 头文件不同,因此不能简单复制 Linux 实现。
本次适配采用以下边界:
| 代码 | OpenHarmony 处理 |
|---|---|
| 文件创建默认模式 | 使用等价的 0666 权限值,避免依赖不存在的 DEFFILEMODE。 |
| 文件状态查询 | 使用 lstat/stat 组合实现 statx 所需的最小字段。 |
| 其他 Okio 公共逻辑 | 继续复用 commonMain 和已有平台实现。 |
兼容代码位于:
okio/src/ohosMain/kotlin/okio/OhosPosixVariant.kt
这样做只解决 OpenHarmony SDK 的声明差异,不改变 Okio 对外 API,也不把 OpenHarmony 特有类型泄漏到公共源集。
3.2 平台代码的编译验证
先编译 Okio 目标:
./gradlew :okio:compileKotlinOhosArm64
再链接示例动态库:
cd example
./gradlew :nativeApp:prepareOhos --no-daemon
如果平台兼容代码有错误,失败会出现在 Kotlin/Native 编译阶段;如果 C ABI、CMake 或链接器有错误,失败会出现在 linkDebugSharedOhosArm64 或 DevEco 的 Ninja 阶段。分别保留这两个边界,便于定位问题。
第 4 阶段:原生桥接(技术难点)
4.1 Kotlin/Native 对象不能直接交给 ArkTS
Kotlin/Native 的 Buffer 和 Kotlin 对象属于 Kotlin 运行时对象。ArkTS 只能接收 JavaScript 值,C++ 也不能把 Kotlin 对象地址直接当成 JavaScript 对象。
最终采用三层桥接:
ArkTS
│ JSON string
▼
C++ N-API entry
│ const char*
▼
Kotlin/Native C ABI
│ Okio Buffer
▼
UTF-8 JSON + explicit free
4.2 方案对比
| 方案 | 优点 | 缺点 | 选用 |
|---|---|---|---|
| 直接导出 Kotlin 对象 | 代码少 | ABI、生命周期和类型不可控 | ❌ |
| 只导出文本常量 | 页面调用简单 | 无法证明 Okio 实际执行 | ❌ |
| C ABI + JSON | 边界清晰、易扩展、易调试 | 有一次序列化开销 | ✅ |
| 在 ArkTS 重写 Buffer 逻辑 | 不需要 Native 桥 | KMP 和 ArkTS 逻辑容易分叉 | ❌ |
4.3 Kotlin/Native 导出函数
桥接文件位于 example/nativeApp/src/ohosArm64Main/kotlin/OkioNative.kt:
@CName("OkioText")
public fun okioTextNative(): CPointer<ByteVar> = response {
"{\"value\":\"${OkioExamples.roundTrip()}\",\"checks\":\"${OkioExamples.checks()}\"}"
}
@CName("OkioFree")
public fun freeNative(pointer: CPointer<ByteVar>?) {
if (pointer != null) nativeHeap.free(pointer.rawValue)
}
返回值是 Native heap 分配的、以 0 结尾的 UTF-8 C 字符串。每一次调用都由 OkioFree 释放返回缓冲区,避免按钮重复点击造成 Native 堆泄漏。
4.4 C++ N-API 方法分发
C++ 入口位于 example/ohosApp/entry/src/main/cpp/napi_init.cpp,注册一个 okioText 方法:
static napi_value Text(napi_env env, napi_callback_info) {
void* raw = OkioText();
const char* value = static_cast<const char*>(raw);
napi_value result = nullptr;
napi_create_string_utf8(env, value, NAPI_AUTO_LENGTH, &result);
OkioFree(raw);
return result;
}
C++ 只负责调用 Native 函数、创建 ArkTS 字符串和释放 Native 缓冲区。Okio 的 Buffer 逻辑仍然位于共享 Kotlin 代码。
4.5 N-API 生命周期
ArkTS okio.okioText()
│
▼
N-API Text()
│
▼
OkioText()
│
▼
napi_create_string_utf8(...)
│
▼
OkioFree(raw)
│
▼
return JS string
N-API 不保存 Native 指针,也不把 Kotlin 对象放进全局缓存。这样页面重建或按钮多次点击时,不需要额外处理跨线程对象生命周期。
第 5 阶段:ArkUI 宿主封装
5.1 ArkTS 调用 N-API 模块
页面通过类型声明加载 libentry.so:
import okio from 'libentry.so'
this.value = okio.okioText()
类型声明位于:
example/ohosApp/entry/src/main/cpp/types/libentry/index.d.ts
5.2 CMake 导入 Kotlin/Native 动态库
add_library(okio SHARED IMPORTED)
set_target_properties(okio PROPERTIES
IMPORTED_LOCATION "${CMAKE_CURRENT_SOURCE_DIR}/../../../libs/arm64-v8a/libokio.so")
add_library(entry SHARED napi_init.cpp)
target_include_directories(entry PRIVATE "${CMAKE_CURRENT_SOURCE_DIR}/include")
target_link_libraries(entry PRIVATE okio libace_napi.z.so)
libokio.so 作为 imported library 参与 entry 链接,HAP 最终携带 arm64-v8a 原生库。若库文件未执行 prepareOhos,Ninja 会报告 libokio.so 缺失。
5.3 ArkUI 页面状态
Index.ets 保存 Native 调用结果和错误文本:
@State value: string = 'Okio OpenHarmony'
Button('运行 Okio 检查').onClick(() => {
try {
this.value = okio.okioText()
} catch (error) {
this.value = `调用失败: ${String(error)}`
}
})
页面不复制 Okio 的 UTF-8 实现,也不根据字符串内容伪造 passed。结果只能来自 libokio.so 的 Kotlin/Native 调用。
5.4 页面交互预设
页面提供一个真实操作按钮:
- 运行 Okio 检查:触发 N-API、Kotlin/Native 和 Okio Buffer 全链路。
- 结果文本:显示 Native 返回的 JSON。
- 错误文本:显示 N-API 或 Native 异常转换后的信息。
这样页面尽量保持简单,把重点放在三方库实际功能是否通过。
第 6 阶段:示例与验证
6.1 example 工程结构
example/
├── shared/
│ ├── src/commonMain/.../OkioExamples.kt
│ └── src/commonTest/.../OkioExamplesTest.kt
├── nativeApp/
│ ├── src/ohosArm64Main/kotlin/OkioNative.kt
│ └── src/ohosArm64Main/linker/shared-library.map
└── ohosApp/
├── AppScope/
├── entry/src/main/cpp/
│ ├── CMakeLists.txt
│ └── napi_init.cpp
└── entry/src/main/ets/
├── entryability/EntryAbility.ets
└── pages/Index.ets
shared 验证公共 Okio 调用,nativeApp 产生 Native 动态库,ohosApp 负责 ArkUI 页面和 N-API。三者边界清晰,任何一层失败都能单独定位。
6.2 原生模块注册
napi_init.cpp 通过 napi_module_register 注册 entry 模块,ArkTS 使用:
import okio from 'libentry.so'
模块类型声明位于:
example/ohosApp/entry/src/main/cpp/types/libentry/index.d.ts
6.3 Native 动态库准备
执行:
./gradlew :okio:publishToMavenLocal -PsigningEnabled=false
cd example
./gradlew :shared:jvmTest :nativeApp:prepareOhos --no-daemon
成功后生成:
example/ohosApp/entry/libs/arm64-v8a/libokio.so
example/ohosApp/entry/src/main/cpp/include/libokio_api.h
6.4 构建、签名和安装
在 DevEco Studio 打开 example/ohosApp,完成本机签名配置后构建 HAP:
/Applications/DevEco-Studio.app/Contents/tools/node/bin/node \\
/Applications/DevEco-Studio.app/Contents/tools/hvigor/bin/hvigorw.js \\
--product default assembleHap --parallel --incremental --no-daemon
产物位于:
example/ohosApp/entry/build/default/outputs/default/entry-default-signed.hap
安装并启动:
hdc list targets
hdc -t <设备序列号> install -r \\
example/ohosApp/entry/build/default/outputs/default/entry-default-signed.hap
hdc -t <设备序列号> shell aa start \\
-a EntryAbility -b com.squareup.okio.sample
签名证书、profile、p12 和密码只保存在本机,不要提交到 AtomGit。
四、完整代码对照
4.1 整体架构
ArkUI Index.ets
│ okioText()
▼
libentry.so / C++ N-API
│ OkioText / OkioFree
▼
libokio.so
│ Kotlin/Native C ABI
▼
OkioExamples
│ Buffer.writeUtf8 + Buffer.readUtf8
▼
JSON result
4.2 文件清单
| 文件 | 职责 |
|---|---|
okio/build.gradle.kts | 注册 ohosArm64 和 OpenHarmony 源集。 |
okio/src/ohosMain/kotlin/okio/OhosPosixVariant.kt | OpenHarmony POSIX 兼容实现。 |
example/shared/.../OkioExamples.kt | 共享 Okio Buffer 示例和自检。 |
example/nativeApp/.../OkioNative.kt | C ABI、JSON 返回和内存释放。 |
example/ohosApp/entry/src/main/cpp/napi_init.cpp | C++ N-API 导出和 Native 字符串转换。 |
example/ohosApp/entry/src/main/cpp/CMakeLists.txt | 导入 libokio.so 并链接 libentry.so。 |
example/ohosApp/entry/src/main/ets/pages/Index.ets | ArkUI 真机验收页面。 |
scripts/build-openharmony.sh | OpenHarmony 核心库构建辅助脚本。 |
docs/openharmony/VALIDATION.md | 自动检查和真机验收记录。 |
docs/openharmony/images/okio-openharmony-result.png | 本次适配效果图。 |
4.3 关键 API 对照
| 层次 | API | 作用 |
|---|---|---|
| Okio | Buffer.writeUtf8 | 写入 UTF-8 文本。 |
| Okio | Buffer.readUtf8 | 读取 UTF-8 文本。 |
| Kotlin | OkioExamples.roundTrip | 执行共享 Buffer 往返。 |
| Kotlin | OkioExamples.checks | 返回共享验收结果。 |
| Native | OkioText | 返回 JSON C 字符串。 |
| Native | OkioFree | 释放 Native 返回缓冲区。 |
| N-API | okioText | 向 ArkTS 暴露 Native 方法。 |
4.4 ArkTS 与 Kotlin 的边界
ArkTS 只负责页面和调用生命周期:
this.value = okio.okioText()
Kotlin 负责 Buffer 操作和验收:
public fun roundTrip(): String {
val buffer = Buffer()
buffer.writeUtf8("OpenHarmony / Okio")
return buffer.readUtf8()
}
两者之间只传输 UTF-8 JSON 字符串,不传输 Kotlin 对象、ArkTS class 实例或未校验的动态结构。
五、关键决策说明
决策 1:把 ohosArm64 加入公共构建约定
只有真正链接 ohosArm64 动态库,才能证明共享 Okio 代码可以进入 OpenHarmony 运行时。只在 JVM 上通过测试不足以证明 OpenHarmony Native 兼容。
决策 2:独立消费者必须通过构建产物消费
example 单独解析共享代码,ohosApp 单独接收 .so 和头文件,避免根工程编译通过却无法在 DevEco 中打包。
决策 3:在 ohosMain 处理 POSIX 差异
平台头文件差异属于 OpenHarmony 适配边界,不应把条件分支散落在公共代码中。集中放入 ohosMain,可以保留 Okio 其他平台的实现和 API。
决策 4:JSON 作为跨语言数据契约
JSON 让 ArkTS、C++ 和 Kotlin/Native 的边界清楚可调试,并避免暴露 Buffer 和 Kotlin 运行时对象布局。
决策 5:桥接层只开放必要的 C ABI 入口
示例只需要结果和释放能力,少量 ABI 符号可以降低 Native 生命周期和版本兼容风险。
决策 6:把库验证和设备验证分开
JVM 测试验证 Buffer 规则,Native 链接验证 ABI,Hvigor 验证 HAP,真机验证 ArkUI 到 Kotlin/Native 的完整调用链。每一层都有明确的失败边界。
六、测试与验证
6.1 测试环境
本次验证使用:
- macOS;
- 项目 Gradle Wrapper 和 Kotlin Multiplatform 工具链;
- DevEco Studio 及 OpenHarmony ARM64 Native SDK;
- 已配置本机调试签名的 Stage 工程;
- ARM64 OpenHarmony 真机;
- HDC 设备连接。
6.2 静态检查与单元测试
./gradlew :okio:compileKotlinOhosArm64
./gradlew :okio:publishToMavenLocal -PsigningEnabled=false
(cd example && ./gradlew :shared:jvmTest)
测试覆盖共享示例的 UTF-8 往返和 passed 验收逻辑。compileKotlinOhosArm64 验证 Okio 核心模块可生成 OpenHarmony KLIB。
6.3 原生桥接和 HAP 验证
(cd example && ./gradlew :nativeApp:prepareOhos --no-daemon)
/Applications/DevEco-Studio.app/Contents/tools/node/bin/node \\
/Applications/DevEco-Studio.app/Contents/tools/hvigor/bin/hvigorw.js \\
--product default assembleHap --parallel --incremental --no-daemon
验证结果:
libokio.so generated
libokio_api.h generated
BuildNativeWithNinja successful
Hvigor BUILD SUCCESSFUL
entry-default-signed.hap generated
6.4 功能验证用例
用例 1:默认页面
启动 HAP 后页面显示 KMP Okio OpenHarmony 标题、结果区域和“运行 Okio 检查”按钮。
用例 2:Native 调用
点击“运行 Okio 检查”,ArkTS 调用 libentry.so,C++ 调用 OkioText,Kotlin/Native 创建 Okio Buffer 并返回 JSON。
用例 3:UTF-8 往返
确认页面结果中的 value 为 OpenHarmony / Okio,说明 writeUtf8 和 readUtf8 在 OpenHarmony ARM64 动态库中执行成功。
用例 4:共享自检
确认结果中的 checks 为 passed,说明自检逻辑来自共享 Kotlin 代码,而不是页面固定文本。
用例 5:重复点击和释放
连续点击按钮,确认页面仍能返回同样结果且应用不崩溃。每次返回的 Native 缓冲区都由 OkioFree 释放。
用例 6:Native 错误边界
如果 Native 调用发生异常,页面显示 调用失败 文本,不应出现无响应或崩溃。
6.5 验证结论
自动测试、Kotlin/Native ARM64 编译、N-API/CMake 链接、Hvigor HAP 构建、签名安装和真机页面自检均已完成。真机页面显示 OpenHarmony / Okio 与 checks: passed,说明从 ArkUI 到 Okio Buffer 的链路可用。
七、运行效果
7.1 真机截图

截图中可以看到:
- 页面标题为“KMP Okio OpenHarmony”;
- 结果区域返回
OpenHarmony / Okio; checks状态为passed;- “运行 Okio 检查”按钮位于结果区域下方;
- 页面运行在 OpenHarmony Stage 宿主中,结果来自 Native Okio 调用。
7.2 命令速查
# 编译 Okio OpenHarmony 目标
./gradlew :okio:compileKotlinOhosArm64
# 发布本地依赖并准备 Native 产物
./gradlew :okio:publishToMavenLocal -PsigningEnabled=false
(cd example && ./gradlew :shared:jvmTest :nativeApp:prepareOhos --no-daemon)
# 在已配置签名的工程中构建 HAP
cd example/ohosApp
/Applications/DevEco-Studio.app/Contents/tools/node/bin/node \\
/Applications/DevEco-Studio.app/Contents/tools/hvigor/bin/hvigorw.js \\
--product default assembleHap --parallel --incremental --no-daemon
# 安装和启动
hdc list targets
hdc -t <设备序列号> install -r \\
entry/build/default/outputs/default/entry-default-signed.hap
hdc -t <设备序列号> shell aa start \\
-a EntryAbility -b com.squareup.okio.sample
八、遗留问题与改进方向
8.1 踩坑复盘
- 只改 ArkTS 页面不算 KMP 适配:必须把 Okio 共享代码真正编译成
ohosArm64动态库。 - 只生成
.so也不够:CMake、导出的头文件、N-API 和 HAP 需要使用同一次产物。 - 平台头文件差异要集中处理:
DEFFILEMODE和statx的兼容代码放在ohosMain,避免污染其他平台。 - N-API 必须释放返回字符串:C++ 创建 ArkTS 字符串后应立即调用
OkioFree。 - 签名和构建是两个步骤:Hvigor 构建成功不代表 HAP 已签名,真机安装必须使用签名产物。
8.2 已知问题
- 当前示例只覆盖 ARM64,未提供
x86_64或armeabi-v7aHAP 原生库; - 示例页面验证 Buffer 基础能力,未覆盖全部 Okio 文件系统和网络 API;
- OpenHarmony Native 工具链需要与 DevEco Studio SDK 版本匹配;
- 签名配置属于本机环境,其他开发者需要重新配置证书和 profile;
libokio.so和生成头文件属于构建产物,重新拉取仓库后需要再次执行prepareOhos。
8.3 未来优化方向
- 增加 OpenHarmony 文件系统、压缩和异步 I/O 的专项示例;
- 为 CMP 应用提供可复用的
commonMainOkio 数据层示例; - 增加 Native 依赖检查和 HAP 构建的 CI 任务;
- 为更多 OpenHarmony ABI 提供构建矩阵;
- 为 N-API 增加更细粒度的错误码和异步 API。
九、总结
9.1 核心难点回顾
本次适配真正需要处理的不是一个按钮,而是一条完整跨端链路:
KMP/CMP commonMain
→ Okio ohosArm64
→ Kotlin/Native C ABI
→ C++ N-API
→ ArkUI Stage 页面
→ OpenHarmony ARM64 真机
9.2 封装层次
- Okio KMP 层:提供稳定的 Buffer API 和平台公共实现;
ohosMain层:处理 OpenHarmony POSIX 声明差异;- Native 层:生成 ARM64 动态库,并通过 C ABI 输出有限入口;
- N-API 层:完成字符串转换和 Native 内存释放;
- ArkTS 层:管理页面调用、错误显示和用户操作;
- DevEco 层:完成 CMake、HAP、签名、安装和运行。
9.3 三条经验
- 先让共享 Okio 代码在 JVM 和 OpenHarmony Native 分别通过,再接入 ArkUI;
- 用 JSON 和少量 C ABI 代替跨语言对象传递,降低边界复杂度;
- 把自动测试、HAP 构建和真机调用分别记录,避免把“编译成功”误认为“三方库功能可用”。
9.4 适配成果
当前 Okio OpenHarmony 适配已完成:
ohosArm64KMP 目标;- OpenHarmony
ohosMainPOSIX 兼容实现; - JVM 和 OpenHarmony ARM64 共用的 Buffer 示例;
- Kotlin/Native + C ABI + C++ N-API 桥接;
- CMake 导入
libokio.so; - Stage ArkUI 验收页面;
- 签名 HAP 构建、设备安装和效果图;
- 与参考 KMP/CMP 工程一致的模块和脚本组织;
- AtomGit 项目文档和 OpenHarmony 验收记录。
参考文档
更多推荐


所有评论(0)