本文记录 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 字符串并提供显式释放函数。

示例的结果约定如下:

字段示例值含义
valueOpenHarmony / OkioBuffer UTF-8 写入和读取后的文本。
checkspassed共享 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 目标ohosArm64ARM64 真机动态库
DevEco product6.0.0(20)工程兼容和目标版本
ABIarm64-v8aHAP 原生库架构
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.ktOpenHarmony POSIX 兼容实现。
example/shared/.../OkioExamples.kt共享 Okio Buffer 示例和自检。
example/nativeApp/.../OkioNative.ktC ABI、JSON 返回和内存释放。
example/ohosApp/entry/src/main/cpp/napi_init.cppC++ N-API 导出和 Native 字符串转换。
example/ohosApp/entry/src/main/cpp/CMakeLists.txt导入 libokio.so 并链接 libentry.so。
example/ohosApp/entry/src/main/ets/pages/Index.etsArkUI 真机验收页面。
scripts/build-openharmony.shOpenHarmony 核心库构建辅助脚本。
docs/openharmony/VALIDATION.md自动检查和真机验收记录。
docs/openharmony/images/okio-openharmony-result.png本次适配效果图。

4.3 关键 API 对照

层次API作用
OkioBuffer.writeUtf8写入 UTF-8 文本。
OkioBuffer.readUtf8读取 UTF-8 文本。
KotlinOkioExamples.roundTrip执行共享 Buffer 往返。
KotlinOkioExamples.checks返回共享验收结果。
NativeOkioText返回 JSON C 字符串。
NativeOkioFree释放 Native 返回缓冲区。
N-APIokioText向 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 踩坑复盘

  1. 只改 ArkTS 页面不算 KMP 适配:必须把 Okio 共享代码真正编译成 ohosArm64 动态库。
  2. 只生成 .so 也不够:CMake、导出的头文件、N-API 和 HAP 需要使用同一次产物。
  3. 平台头文件差异要集中处理:DEFFILEMODE 和 statx 的兼容代码放在 ohosMain,避免污染其他平台。
  4. N-API 必须释放返回字符串:C++ 创建 ArkTS 字符串后应立即调用 OkioFree。
  5. 签名和构建是两个步骤:Hvigor 构建成功不代表 HAP 已签名,真机安装必须使用签名产物。

8.2 已知问题

  • 当前示例只覆盖 ARM64,未提供 x86_64 或 armeabi-v7a HAP 原生库;
  • 示例页面验证 Buffer 基础能力,未覆盖全部 Okio 文件系统和网络 API;
  • OpenHarmony Native 工具链需要与 DevEco Studio SDK 版本匹配;
  • 签名配置属于本机环境,其他开发者需要重新配置证书和 profile;
  • libokio.so 和生成头文件属于构建产物,重新拉取仓库后需要再次执行 prepareOhos。

8.3 未来优化方向

  • 增加 OpenHarmony 文件系统、压缩和异步 I/O 的专项示例;
  • 为 CMP 应用提供可复用的 commonMain Okio 数据层示例;
  • 增加 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 三条经验

  1. 先让共享 Okio 代码在 JVM 和 OpenHarmony Native 分别通过,再接入 ArkUI;
  2. 用 JSON 和少量 C ABI 代替跨语言对象传递,降低边界复杂度;
  3. 把自动测试、HAP 构建和真机调用分别记录,避免把“编译成功”误认为“三方库功能可用”。

9.4 适配成果

当前 Okio OpenHarmony 适配已完成:

  • ohosArm64 KMP 目标;
  • OpenHarmony ohosMain POSIX 兼容实现;
  • JVM 和 OpenHarmony ARM64 共用的 Buffer 示例;
  • Kotlin/Native + C ABI + C++ N-API 桥接;
  • CMake 导入 libokio.so;
  • Stage ArkUI 验收页面;
  • 签名 HAP 构建、设备安装和效果图;
  • 与参考 KMP/CMP 工程一致的模块和脚本组织;
  • AtomGit 项目文档和 OpenHarmony 验收记录。

参考文档

Logo

一站式 AI 云服务平台

更多推荐