本文记录 Kermit 接入 OpenHarmony 的完整过程,覆盖 KMP/CMP 工程盘点、ohosArm64 目标、Kotlin/Native 动态库、C ABI/N-API 桥接、ArkUI 真机页面、签名 HAP 和真机验收。

本次适配复用 Kermit Kotlin 侧的 Logger、Severity、LoggerConfig、LogWriter 和公共自检逻辑,再由 ArkTS 调用 N-API 进入 Kotlin/Native。这样验证的是同一份 KMP 日志代码在 OpenHarmony ARM64 设备上的运行结果,而不是重新在 ArkTS 页面里实现一套日志逻辑。

项目地址: AtomGit/oh-tpc/Kermit

开发工具: 华为云码道

一、背景

1.1 为什么做开源鸿蒙平台 KMP/CMP 适配

Kermit 是 Kotlin Multiplatform 日志库。它把日志级别、Tag、格式化、异常和
LogWriter 抽象放在共享 Kotlin 代码中,再由各个平台提供默认输出实现。要让
Kermit 在 OpenHarmony 上真正可用,不能只把示例页面改成 ArkTS;必须让同一份
共享日志代码经过 Kotlin/Native 编译,在 ARM64 鸿蒙设备上运行。

如果只在页面里调用 console.info,页面可以显示“日志已写入”,却无法证明
Kermit 的 Logger、LoggerConfig、Severity 和 LogWriter 已经穿过 Native
边界。适配需要解决下面几个问题:

障碍具体问题
目标缺失KMP 模块默认没有 OpenHarmony 目标,必须增加 ohosArm64() 才能生成 KLIB。
工具链不一致Kotlin/Native、OpenHarmony LLVM、Native SDK、Gradle 和 JDK 必须使用匹配版本。
平台默认实现platformLogWriter 是 expect API,OpenHarmony 必须提供对应的 actual。
语言边界不同ArkTS 不能直接持有 Kotlin Logger 或 LogWriter 对象,必须经过 C ABI、C++ N-API 和 JSON。
内存生命周期Kotlin/Native 返回的字符串必须由 C++ 复制后显式释放,不能把 Native 指针交给 ArkTS 保存。
交付链路复杂Native 动态库、CMake、HAP、签名、设备安装和 ARM64 依赖需要分别验证。

因此,本项目把适配边界放在三个地方:Kotlin/Native 目标配置、Kermit 默认日志
实现和 C ABI/N-API 桥接层。Logger 的公共 API 仍由 KMP 维护,ArkTS 只负责页面
输入、按钮交互和错误展示。

1.2 库提供的能力

Kermit 公共模块提供以下能力:

  • Logger:提供 v、d、i、w、e、a 多级日志 API;
  • Severity:定义 Verbose、Debug、Info、Warn、Error 和 Assert 六级日志级别;
  • LoggerConfig:控制最小日志级别和多个 LogWriter;
  • CommonWriter:使用跨平台 stdout 输出格式化日志;
  • LogWriter:允许应用接入自定义日志后端;
  • MessageStringFormatter:统一处理级别、Tag 和消息文本;
  • Logger.withTag:在共享代码中创建带固定 Tag 的 Logger;
  • MutableLoggerConfig:在需要时动态修改日志级别和 Writer 列表。

OpenHarmony 默认实现位于:

kermit-core/src/ohosArm64Main/kotlin/co/touchlab/kermit/PlatformLogWriter.kt

它返回 CommonWriter,因此最小使用方式与其他 KMP 平台一致:

Logger.i(tag = "KermitExample") {
    "Kermit OpenHarmony"
}

示例中由 Native 桥接返回的 JSON 记录如下:

{
  "logged": true,
  "message": "Kermit OpenHarmony sample"
}

1.3 实现适配

维度要求
代码复用Logger、Severity、配置、格式化和默认 Writer 继续使用 KMP 共享实现。
平台目标为 Kermit 核心模块、日志模块和扩展模块加入 ohosArm64。
平台实现在 ohosArm64Main 提供 platformLogWriter 的 actual。
桥接稳定使用少量 C ABI 函数和 JSON,避免把 Kotlin 对象地址交给 ArkTS。
UI 完整页面支持输入日志、写入日志和运行公共检查。
可测试JVM 测试、Native 链接、HAP 构建、设备安装分别验收。
签名安全源码只保留签名入口,证书和密钥材料由开发者本机配置。
仓库规范项目说明、文章、效果图和源码链接统一使用 AtomGit。

本项目的 ArkUI 页面是独立的真机验收宿主,Kermit 公共 API 仍然保持平台无关。其他 KMP/CMP 应用可以复用 Kermit Logger 和 LogWriter,再自行决定是否接入 Hilog、文件或远端日志后端。

二、实现路线图

第 1 阶段:项目初始化     ── 盘点 KMP 模块、Logger API 和 OpenHarmony 示例边界
第 2 阶段:目标与依赖打通 ── 加入 ohosArm64、独立 example 工程和 Native 构建任务
第 3 阶段:日志与序列化   ── 建立 KermitExamples、JSON 契约和错误边界
第 4 阶段:原生桥接       ── Kotlin/Native C ABI、C++ N-API、内存释放和类型检查
第 5 阶段:页面能力封装   ── ArkUI 输入框、日志按钮、自检按钮和页面状态
第 6 阶段:示例与验证     ── HAP 构建、签名、设备安装、日志输出和效果图

每个阶段都使用真实产物作为下一阶段输入:先用 JVM 验证 Kermit 门面,再把同一
份源码链接为 ARM64 动态库,随后由 CMake 和 N-API 加载到 Stage 工程,最后安装
签名 HAP 观察设备页面和 stdout 日志。

三、逐步实现过程

第 1 阶段:项目初始化

1.1 盘点公共 API 和工程边界

项目采用与参考 KMP/CMP 工程一致的分层:

Kermit/                         KMP 日志库和既有平台实现
kermit-core/                    LoggerConfig、LogWriter、Severity、CommonWriter
kermit/                         Logger 公共 API 和默认配置
kermit-io/                      文件日志 Writer
extensions/                     Ktor、Koin 等扩展
example/shared/                 示例门面和 JVM 验收测试
example/nativeApp/              ohosArm64 Kotlin/Native 动态库
example/ohosApp/                DevEco Stage 工程和 ArkUI 页面
scripts/                        Native 构建和依赖检查脚本
docs/openharmony/               验收记录和真机效果图

example 是独立 Gradle 工程,不把 DevEco 工程当作 Kotlin 子模块。这样可以分别
执行 Gradle 和 Hvigor,也可以把 ohosApp 复制到另一个目录后在 DevEco Studio
中配置签名。

1.2 固定工具链和版本矩阵

本项目使用以下版本约定:

项目配置用途
Kotlin Multiplatform2.2.21-1.0.0JVM、Kotlin/Native 和 KLIB
JDK21Gradle、Kotlin 编译和 Native 任务
OpenHarmony 目标ohosArm64ARM64 真机动态库
DevEco product6.0.0(20)工程兼容和目标版本
ABIarm64-v8aHAP 原生库架构
默认 WriterCommonWriterOpenHarmony stdout 输出

执行 Gradle 脚本前先选择 JDK 21:

export JAVA_HOME="/path/to/jdk-21"
java -version

JDK 25 可能导致 Kotlin 编译器解析 Java 版本元数据失败。设备验证使用 ARM64
鸿蒙手机或 ARM64 模拟器。

1.3 创建 OpenHarmony 示例目录

示例页面围绕“输入消息、写入日志、运行检查”组织:

标题区          Kermit 工作台 / Kotlin Multiplatform / OpenHarmony
输入区          Kermit OpenHarmony
操作区          写入一条日志 / 运行公共检查
状态区          日志已写入 OpenHarmony stdout
链路区          ArkTS → N-API → Kotlin/Native → Kermit

页面不会使用 console.info 假装调用 Kermit。点击“写入一条日志”时,ArkTS 调用
N-API,C++ 再调用 Kotlin/Native 导出的 KermitLog,最终由 Kermit Logger 使用
OpenHarmony 默认 Writer 输出。

第 2 阶段:目标与依赖打通

2.1 加入 ohosArm64 目标

核心模块和扩展模块加入 OpenHarmony Native 目标:

kotlin {
    androidTarget()
    jvm()
    js { nodejs() }

    linuxX64()
    linuxArm64()
    ohosArm64()

    sourceSets {
        commonMain.dependencies {
            api(project(":kermit-core"))
        }
    }
}

ohosArm64 与 JVM、Linux、Apple 等目标并列,公共日志实现保持在 commonMain。
只有平台默认 Writer 的 actual 放在 ohosArm64Main,这样不会影响其他平台。

2.2 Native focused build 的作用

example/nativeApp 构建一个受 linker map 约束的 shared library:

kotlin {
    ohosArm64 {
        binaries.sharedLib {
            baseName = "kermit"
            linkerOpts(
                "--entry=0",
                "--version-script=${project.file("src/ohosArm64Main/linker/shared-library.map")}",
            )
            linkerOpts("-lace_napi.z", "-luv", "-lhilog_ndk.z")
        }
    }
}

链接器只导出三个 C ABI 符号:

KermitLog
KermitRunChecks
KermitFree

这样 ArkTS 只能通过明确的边界获取日志结果和检查结果,Kotlin/Native 内部实现
不会变成不受控的 ABI。

2.3 配置仓库和独立消费工程

example/settings.gradle.kts 使用 OpenHarmony 社区 Maven、Maven Central 和
Gradle Plugin Portal:

pluginManagement {
    repositories {
        maven("https://maven.eazytec-cloud.com/nexus/repository/maven-public/")
        google()
        gradlePluginPortal()
        mavenCentral()
    }
}

dependencyResolutionManagement {
    repositories {
        maven("https://maven.eazytec-cloud.com/nexus/repository/maven-public/")
        mavenLocal()
        google()
        mavenCentral()
    }
}

示例的 shared 模块直接编译当前仓库的 Kermit 源码,确保生成的动态库和当前
工作区源码一致:

sourceSets {
    commonMain {
        kotlin.srcDirs(
            "../../kermit-core/src/commonMain/kotlin",
            "../../kermit/src/commonMain/kotlin",
            "src/commonMain/kotlin",
        )
    }
}

这种独立消费者结构可以隔离根工程发布配置,先验证库源码和 Native 桥接,再把
生成物交给 DevEco。

2.4 通过构建产物消费共享库

prepareOhos 在 Native 链接成功后复制动态库和 C 头文件:

val prepareOhos by tasks.registering(Copy::class) {
    dependsOn("linkDebugSharedOhosArm64")
    from(layout.buildDirectory.dir("bin/ohosArm64/debugShared")) {
        include("libkermit.so")
        into("libs/arm64-v8a")
    }
    from(layout.buildDirectory.dir("bin/ohosArm64/debugShared")) {
        include("libkermit_api.h")
        into("src/main/cpp/include")
    }
    into(rootProject.layout.projectDirectory.dir("../example/ohosApp/entry"))
}

动态库、生成头文件和 HAP 属于构建产物,仓库通过 .gitignore 排除它们;每台
开发机都可以从源码重新生成与自身 SDK 匹配的文件。

第 3 阶段:日志与序列化

3.1 为什么需要统一 JSON 契约

ArkTS、C++ 和 Kotlin/Native 不能直接共享 Kotlin Logger 对象,因此边界使用
UTF-8 JSON:

ArkTS log(message)
        ↓ message string
N-API KermitLog()
        ↓ void* UTF-8 input
Kotlin/Native KermitLog()
        ↓ Kermit Logger.i()
JSON result
        ↓
ArkUI status

页面只消费 JSON,不复制 Kermit 的日志输出规则。这样 JVM 测试、Native 调用和
设备页面使用同一份 KermitExamples 门面。

3.2 KermitExamples 和日志结果

公共示例门面位于 example/shared/src/commonMain:

public object KermitExamples {
    public fun log(message: String = "Kermit OpenHarmony sample") {
        Logger.i(tag = "KermitExample") { message }
    }

    public fun checks(): List<String> = listOf(
        "Kermit logger API is callable",
        "OpenHarmony uses a native target",
        "default writer accepts UTF-8 messages",
    )
}

Kermit 本身仍然提供完整的 Logger API,KermitExamples 只是为了给跨语言样例
提供稳定、可验证的调用入口。

3.3 门面和 JSON

JSON 边界保持字段稳定:

{
  "logged": true,
  "message": "Kermit OpenHarmony sample"
}

公共检查返回:

{
  "passed": true,
  "checks": [
    "Kermit logger API is callable",
    "OpenHarmony uses a native target",
    "default writer accepts UTF-8 messages"
  ]
}

所有字符串经过 JSON 转义,避免用户输入的引号、反斜杠或换行破坏 JSON。日志
正文不会直接拼接到 ArkTS 对象中,而是先由 Native 层生成完整字符串。

3.4 自检和错误边界

KermitExamples.checks() 覆盖三项检查:

  1. Kermit Logger API 可以从共享代码调用;
  2. OpenHarmony 目标已经进入 Native 编译链;
  3. 默认 Writer 可以接受 UTF-8 消息。

Native 异常会转换为 {"error":"..."},C++ 在创建 ArkTS 字符串后立即调用
KermitFree。这样页面能显示可读错误,同时不会泄漏 Kotlin/Native 堆内存。

第 4 阶段:原生桥接(技术难点)

4.1 Kotlin/Native 对象不能直接交给 ArkTS

Kotlin/Native 的 Logger、LoggerConfig 和 LogWriter 属于 Kotlin 运行时对象。
ArkTS 只能接收 JavaScript 值,C++ 不能把 Kotlin 对象地址直接当成 JavaScript
对象。

最终采用三层桥接:

ArkTS
  │ JSON string
  ▼
C++ N-API entry
  │ void* / UTF-8
  ▼
Kotlin/Native C ABI
  │ Kermit Logger
  ▼
UTF-8 JSON + explicit free
4.2 方案对比
方案优点缺点选用
直接导出 Kotlin Logger代码少ABI、生命周期和类型不可控❌
只导出 stdout实现简单页面无法确认 Kermit API 被调用❌
C ABI + JSON边界清晰、易扩展、易调试有一次序列化开销✅
在 ArkTS 重写 Logger页面调用简单KMP 和 ArkTS 逻辑容易分叉❌
4.3 Kotlin/Native 导出函数
@CName("KermitLog")
public fun logNative(message: CPointer<ByteVar>?): CPointer<ByteVar> = response {
    val text = message?.toKString() ?: "Kermit OpenHarmony sample"
    Logger.i(tag = "KermitExample") { text }
    "{\"logged\":true,\"message\":\"${quote(text)}\"}"
}

@CName("KermitRunChecks")
public fun checksNative(): CPointer<ByteVar> = response {
    KermitExamples.checksJson()
}

@CName("KermitFree")
public fun freeNative(pointer: CPointer<ByteVar>?) {
    if (pointer != null) nativeHeap.free(pointer.rawValue)
}

返回值使用 Native heap 分配的、以 0 结尾的 C 字符串。每一块返回缓冲区都由
KermitFree 释放。用户输入通过 UTF-8 转换进入 Kermit,不跨越 Kotlin 对象引用。

4.4 C++ N-API 方法分发

C++ 注册两个 ArkTS 方法:

napi_property_descriptor methods[] = {
    {"log", nullptr, Log, nullptr, nullptr, nullptr, napi_default, nullptr},
    {"runChecks", nullptr, Checks, nullptr, nullptr, nullptr, napi_default, nullptr},
};

log 会读取 ArkTS 字符串,创建可写缓冲区,再调用 KermitLog。返回值遵循
“调用 Native、创建 ArkTS 字符串、释放 Native 缓冲区”的顺序。

CMake 将 Kotlin/Native 动态库作为 imported library:

add_library(kermit SHARED IMPORTED)
set_target_properties(kermit PROPERTIES
  IMPORTED_LOCATION
  "${CMAKE_CURRENT_SOURCE_DIR}/../../../libs/arm64-v8a/libkermit.so")

add_library(entry SHARED napi_init.cpp)
target_include_directories(entry PRIVATE "${CMAKE_CURRENT_SOURCE_DIR}/include")
target_link_libraries(entry PRIVATE kermit libace_napi.z.so)
4.5 N-API 生命周期
ArkTS log(message)
        │
        ▼
read UTF-8 string + argument check
        │
        ▼
KermitLog(nativeBuffer)
        │
        ▼
napi_create_string_utf8(...)
        │
        ▼
KermitFree(nativeResult)
        │
        ▼
return JS result

C++ 负责完成跨语言字符串转换和 Native 释放,ArkTS 页面不需要知道
Kotlin/Native 的堆实现。

第 5 阶段:页面能力封装

5.1 ArkTS 调用 N-API

日志调用封装在 KermitClient.ets:

import kermitNative from 'libentry.so';

export interface KermitLogResult {
  logged: boolean;
  message: string;
}

export function log(message: string): KermitLogResult {
  return JSON.parse(kermitNative.log(message)) as KermitLogResult;
}

页面只调用 log 和 runChecks,不会接触 C ABI 符号、Native 指针或 C++ 对象。

5.2 权限声明

Kermit 默认 Writer 使用 stdout,不需要传感器、相机或运动检测权限。宿主只需
声明普通 Stage 页面能力即可。若业务在 Kermit 之上增加运动、位置或其他系统
能力,应按实际 API 单独声明对应权限,不能因为 Kermit 日志功能而扩大权限范围。

5.3 ArkUI 页面状态

Index.ets 保存输入消息、检查结果和错误文本:

@State private message: string = 'Kermit OpenHarmony';
@State private status: string = '正在加载';
@State private errorText: string = '';

点击“写入一条日志”时调用 N-API:

private writeLog(): void {
  try {
    const result = log(this.message);
    this.status = result.logged
      ? '日志已写入 OpenHarmony stdout'
      : '写入失败';
    this.errorText = '';
  } catch (error) {
    this.errorText = String(error);
  }
}

页面状态只负责展示结果,日志级别、Tag 和 Writer 仍由 Kotlin/Kermit 决定。

5.4 页面交互预设

页面提供两个核心操作:

  • 写入一条日志:把输入框内容送入 Kermit Logger;
  • 运行公共检查:调用 Native 检查并显示 3/3 公共检查通过。

这样既能验证真实日志调用,又能在没有打开终端的情况下确认跨语言桥接已经返回。

第 6 阶段:示例与验证

6.1 example 工程结构
example/
├── shared/
│   ├── src/commonMain/.../KermitExamples.kt
│   └── src/commonTest/.../KermitExamplesTest.kt
├── nativeApp/
│   ├── src/ohosArm64Main/.../NativeBridge.kt
│   └── src/ohosArm64Main/linker/shared-library.map
└── ohosApp/
    ├── AppScope/
    ├── entry/src/main/cpp/
    │   ├── CMakeLists.txt
    │   └── napi_init.cpp
    └── entry/src/main/ets/
        ├── pages/Index.ets
        └── kermit/KermitClient.ets

shared 验证公共门面,nativeApp 产生 Native 动态库,ohosApp 负责 ArkUI 页面。
三者的边界清晰,任何一层失败都能单独定位。

6.2 原生模块注册

napi_init.cpp 通过 napi_module_register 注册 entry 模块,类型声明位于:

example/ohosApp/entry/src/main/cpp/types/libentry/index.d.ts

ArkTS 使用:

import kermitNative from 'libentry.so';
6.3 Native 动态库准备

执行:

export JAVA_HOME="/path/to/jdk-21"
./scripts/build-openharmony.sh

脚本执行示例测试、linkDebugSharedOhosArm64 和 prepareOhos。成功后生成:

example/ohosApp/entry/libs/arm64-v8a/libkermit.so
example/ohosApp/entry/src/main/cpp/include/libkermit_api.h

还可以检查 ELF 的动态依赖:

python3 scripts/check-native-deps.py \
  example/ohosApp/entry/libs/arm64-v8a/libkermit.so
6.4 构建、签名和安装

未配置签名时,可以在 DevEco Studio 构建未签名 HAP;真机安装需要签名 HAP。配置
签名后执行 DevEco 的 assembleHap 任务:

cd example/ohosApp
/Applications/DevEco-Studio.app/Contents/tools/node/bin/node \
  /Applications/DevEco-Studio.app/Contents/tools/hvigor/bin/hvigorw.js \
  --mode module -p module=entry@default -p product=default \
  -p requiredDeviceType=phone assembleHap

产物位于:

entry/build/default/outputs/default/entry-default-signed.hap

签名证书、profile、p12 和密码只保存在本机,不要提交到 AtomGit。

四、完整代码对照

4.1 整体架构

ArkUI input/button
    │
    ▼
KermitClient.ets
    │ JSON
    ▼
libentry.so / N-API
    │ C ABI
    ▼
libkermit.so / KermitLog
    │ Logger.i + CommonWriter
    ▼
OpenHarmony stdout + JSON result

4.2 文件清单

文件职责
kermit-core/src/ohosArm64Main/.../PlatformLogWriter.ktOpenHarmony 默认 Writer actual
example/shared/.../KermitExamples.kt公共门面和自检
example/nativeApp/.../NativeBridge.ktC ABI、JSON 返回和内存释放
example/ohosApp/entry/src/main/cpp/napi_init.cppN-API 导出、参数读取和释放
example/ohosApp/entry/src/main/ets/kermit/KermitClient.etsN-API JSON 解析
example/ohosApp/entry/src/main/ets/pages/Index.etsArkUI 输入和效果页面
scripts/build-openharmony.sh测试、Native 链接和产物复制
docs/openharmony/images/kermit-workbench.png真机效果图

4.3 关键 API 对照

层次API作用
KotlinLogger.i写入 Info 级别日志
KotlinLogger.withTag创建带固定 Tag 的 Logger
KotlinCommonWriterOpenHarmony 默认输出 Writer
NativeKermitLog接收 UTF-8 消息并返回 JSON
NativeKermitRunChecks返回公共自检结果
N-APIlog向 ArkTS 暴露日志方法
ArkTSwriteLog更新页面状态和错误信息

4.4 ArkTS 与 Kotlin 的边界

ArkTS 只负责读取输入和页面生命周期:

const result: KermitLogResult = log(this.message);
this.status = result.logged ? '日志已写入 OpenHarmony stdout' : '写入失败';

Kotlin 负责实际日志调用:

Logger.i(tag = "KermitExample") { text }

两者之间只传输 UTF-8 文本和 JSON,不传输 Kotlin 对象、ArkTS class 实例或未校验
的动态结构。

五、关键决策说明

决策 1:把 ohosArm64 加入公共构建约定

Kermit 的价值在共享 Logger API 和 Writer 抽象。只有真正链接 ohosArm64 动态库,
才能证明共享 Kotlin 代码可以进入 OpenHarmony 运行时。

决策 2:独立消费者必须通过构建产物消费

example 单独编译源码,ohosApp 单独接收 .so 和头文件,避免 Gradle 编译通过
却无法在 DevEco 中打包。

决策 3:JSON 作为跨语言数据契约

JSON 让 ArkTS、C++ 和 Kotlin/Native 的边界清楚可调试,并允许后续增加日志级别、
Tag 或错误字段而不暴露内部对象布局。

决策 4:桥接层只开放三个 C ABI 入口

日志、检查和释放已经覆盖示例所需能力;减少 ABI 符号可以降低 Native 生命周期和
兼容风险。

决策 5:输入日志和系统 stdout 分开验证

输入框验证 UTF-8 传递,设备 stdout 验证 Kermit Writer。两条结果同时成立时,才说明
消息真正穿过 KMP、Native 和 N-API 链路。

决策 6:把库验证和设备验证分开

JVM 测试验证门面规则,Native 链接验证 ABI,Hvigor 验证 HAP,真机验证页面和日志。
每一层都有明确的失败边界。

六、测试与验证

6.1 测试环境

本次设备验证使用:

  • macOS;
  • JDK 21;
  • Kotlin Multiplatform 2.2.21-1.0.0;
  • DevEco Studio 及 OpenHarmony ARM64 Native SDK;
  • 已签名 entry-default-signed.hap;
  • ARM64 OpenHarmony 设备;
  • hdc 设备安装和启动工具。

6.2 静态检查与单元测试

export JAVA_HOME="/path/to/jdk-21"
(cd example && ./gradlew :shared:jvmTest)
git diff --check

测试覆盖 Logger API 调用、OpenHarmony 目标门面和 UTF-8 消息处理。

6.3 原生桥接和 HAP 验证

./scripts/build-openharmony.sh
python3 scripts/check-native-deps.py \
  example/ohosApp/entry/libs/arm64-v8a/libkermit.so

验证结果:

Kotlin/Native ohosArm64 link: SUCCESS
libkermit.so copied: SUCCESS
CMake/Ninja: SUCCESS
ArkTS compile: SUCCESS
HAP package and signing: SUCCESS

6.4 功能验证用例

用例 1:默认页面和自检

启动应用后页面显示“Kermit 工作台”和 3/3 公共检查通过。初始状态由共享门面
和 Native KermitRunChecks 返回。

用例 2:输入 UTF-8 消息

在输入框中输入中文或英文消息,确认 ArkTS 可以正常保存文本,并且没有出现乱码。

用例 3:写入一条日志

点击“写入一条日志”,页面显示“日志已写入 OpenHarmony stdout”。Kotlin/Native
会调用 Logger.i,默认 CommonWriter 将格式化日志写入 stdout。

用例 4:运行公共检查

点击“运行公共检查”,确认页面显示 3/3 公共检查通过,并且 checks 数组包含
三条共享检查名称。

用例 5:重复写入和异常输入

连续点击日志按钮,确认每次都返回新的 JSON。输入包含引号、反斜杠和换行时,确认
JSON 仍然可以被 ArkTS 正确解析。

用例 6:设备安装和启动

使用签名 HAP 安装到 ARM64 设备,启动 EntryAbility,确认页面加载、Native 模块
注册成功,且不会出现 libkermit.so 缺失或 ABI 不匹配错误。

6.5 验证结论

共享 JVM 测试、Kotlin/Native ARM64 链接、CMake/Ninja、ArkTS 编译、HAP 签名安装和
设备页面验证均已完成。真机效果图中的“日志已写入 OpenHarmony stdout”说明从
ArkTS 到 Kermit Writer 的调用链可用。

七、运行效果

7.1 真机截图

在这里插入图片描述

截图中可以看到:

  • 页面标题为“Kermit 工作台”;
  • 副标题说明 Kotlin Multiplatform / OpenHarmony;
  • 输入框显示 Kermit OpenHarmony;
  • “写入一条日志”按钮可触发 Kermit Logger;
  • “运行公共检查”按钮可执行共享检查;
  • 页面显示“日志已写入 OpenHarmony stdout”;
  • 页面底部展示 ArkTS → N-API → Kotlin/Native → Kermit 调用链。

7.2 命令速查

# 选择 JDK 21
export JAVA_HOME="/path/to/jdk-21"

# 编译共享测试、Native 动态库并复制到 DevEco 工程
./scripts/build-openharmony.sh

# 单独运行共享测试
(cd example && ./gradlew :shared:jvmTest)

# 检查 Native 动态库依赖
python3 scripts/check-native-deps.py \
  example/ohosApp/entry/libs/arm64-v8a/libkermit.so

# 在 DevEco 工程中构建 HAP
cd example/ohosApp
/Applications/DevEco-Studio.app/Contents/tools/node/bin/node \
  /Applications/DevEco-Studio.app/Contents/tools/hvigor/bin/hvigorw.js \
  --mode module -p module=entry@default -p product=default \
  -p requiredDeviceType=phone assembleHap

# 安装和启动
hdc -t <设备序列号> install -r \
  entry/build/default/outputs/default/entry-default-signed.hap
hdc -t <设备序列号> shell aa start \
  -a EntryAbility -b co.touchlab.kermit.sample

八、遗留问题与改进方向

8.1 踩坑复盘

  1. 只改 ArkTS 页面不算 KMP 适配:必须把共享 Kermit 源码真正编译成 ohosArm64 动态库。
  2. N-API 不负责业务日志格式:C++ 只做类型检查、字符串转换和释放,日志级别和 Tag 放在 Kotlin。
  3. Native 指针不能交给 ArkTS 保存:每个返回字符串都必须在 C++ 复制后立即调用 KermitFree。
  4. JDK 版本影响 Native 构建:JDK 25 可能造成 Kotlin 工具链解析失败,构建环境固定使用 JDK 21。
  5. 签名配置需要绑定产品:products[].signingConfig 必须指向 signingConfigs,否则 Hvigor 只生成未签名 HAP。

8.2 已知问题

  • Kermit 默认 OpenHarmony Writer 当前使用 stdout,不包含 Hilog 专用域和自定义日志持久化;
  • DevEco 工程必须先获得与自身 SDK 匹配的 libkermit.so 和头文件;
  • 签名证书、profile、p12 和密码只能由本机配置,不能直接复制给其他开发者;
  • 当前示例是单页面日志宿主,多页面应用需要在业务层集中管理 Logger 和 Writer;
  • 根工程的完整 Maven 发布仍需结合项目现有发布配置执行,OpenHarmony 示例优先走独立 example 构建。

8.3 未来优化方向

  • 增加 OpenHarmony Hilog LogWriter,让业务可以选择 stdout 或 Hilog;
  • 将日志桥接封装为可复用的 KMP expect/actual 适配层;
  • 为 Compose Multiplatform 页面提供统一的日志控制台组件;
  • 增加日志级别、Tag 和异常信息在 ArkTS 页面上的可视化展示;
  • 在持续集成中加入 ohosArm64 链接、ELF 依赖检查和 HAP 未签名构建任务。

九、总结

9.1 核心难点回顾

本次适配真正需要处理的不是一个日志按钮,而是一条完整跨端链路:

ArkUI 输入
    → C++ N-API
    → Kotlin/Native C ABI
    → KMP Logger
    → OpenHarmony CommonWriter
    → JSON 结果
    → ArkUI 状态

9.2 封装层次

  • KMP 层:定义稳定的 Logger、Severity、Config 和 Writer 抽象;
  • OpenHarmony Native 层:生成 ARM64 动态库,并通过 C ABI 输出有限入口;
  • N-API 层:完成参数检查、字符串转换和 Native 内存释放;
  • ArkTS 层:管理输入、按钮、页面状态和错误展示;
  • DevEco 层:完成 CMake、HAP、签名、安装和运行。

9.3 三条经验

  1. 先让 Kermit 公共 API 在 JVM 和 Native 通过,再接入 ArkUI;
  2. 用 JSON 和少量 C ABI 代替跨语言对象传递;
  3. 把自动测试、HAP 构建和真机页面分别记录,避免把“编译成功”误认为“设备可用”。

9.4 适配成果

当前 Kermit 已完成:

  • ohosArm64 Kotlin/Native 目标;
  • OpenHarmony platformLogWriter actual;
  • Kermit Logger、Severity 和 CommonWriter 的共享运行;
  • Kotlin/Native + C ABI + N-API 桥接;
  • UTF-8 日志输入和 JSON 返回;
  • CMake imported library 和 ARM64 HAP;
  • 签名 HAP 构建、设备安装和 Kermit 工作台效果图;
  • 与参考 CMP 工程一致的模块和脚本组织;
  • AtomGit 项目文档和 OpenHarmony 验收记录。

参考文档

Logo

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

更多推荐