开源鸿蒙平台 KMP_CMP 三方库「kotlinx.immutable」适配全流程

本文记录 kotlinx.collections.immutable 适配 OpenHarmony 的完整过程,覆盖 KMP/CMP 工程盘点、ohosArm64 目标、持久化集合能力、Kotlin/Native 动态库、C ABI/N-API 桥接、ArkUI 功能总览页面、未签名 HAP、调试签名和真机运行。

本次适配复用库原有的 commonMain 集合接口和实现,只新增 OpenHarmony ARM64 编译目标及验证宿主。ArkTS 页面通过 N-API 调用 Kotlin/Native 生成的真实集合结果,不在页面中重新实现一套集合逻辑。

项目地址: AtomGit/oh-tpc/kotlinx.collections.immutable

上游仓库: Kotlin/kotlinx.collections.immutable

开发工具: 华为云码道

环境安装: 环境安装指南

一、背景

1.1 为什么做 OpenHarmony 平台 KMP/CMP 适配

kotlinx.collections.immutable 是 Kotlin 官方生态中的多平台集合库,提供不可变集合接口和持久化集合实现。标准库的 List、Set、Map 主要表达"调用方不能通过当前引用修改",但不保证底层对象没有其他可变引用;本库通过持久化数据结构让集合快照可以安全共享。

原工程已经覆盖 JVM、JS、Wasm 和 Kotlin/Native 多个平台,但没有 OpenHarmony ohosArm64 目标。只修改 ArkTS 页面或只把标准集合转换成 JSON,都不能证明这份 KMP 集合实现真正运行在鸿蒙设备上。因此适配需要解决以下问题:

障碍 具体问题
目标缺失 核心 KMP 模块没有 ohosArm64(),无法生成 OpenHarmony KLIB。
工具链不一致 Kotlin/Native、Gradle、OpenHarmony LLVM、JDK 和 Native SDK 必须匹配。
跨语言边界 ArkTS 不能直接持有 Kotlin PersistentList、PersistentSet 或 PersistentMap,必须通过 C ABI、C++ N-API 和 JSON。
数据语义容易被误解 示例需要同时展示 immutable 接口、persistent 实现、hash 工厂、转换函数和批量修改。
生命周期管理 Native 返回的 C 字符串必须由明确的 Free 函数释放,避免页面重复读取造成内存泄漏。
交付链路复杂 动态库、CMake、HAP、签名、设备安装和 ARM64 依赖需要分别验收。

因此,本项目把适配边界放在三个地方:KMP 目标配置、Kotlin/Native C ABI/N-API 桥接层和 ArkUI 展示层。集合的创建、更新、结构共享和检查仍由 Kotlin 代码完成,ArkTS 只负责调用和展示。

1.2 库提供的能力

本库不是一个只提供 listOf 的工具类,而是一套完整的不可变和持久化集合 API:

能力 主要 API 说明
不可变接口 ImmutableCollection、ImmutableList、ImmutableSet、ImmutableMap 提供稳定、只读的集合契约。
持久化接口 PersistentCollection、PersistentList、PersistentSet、PersistentMap 修改操作返回新集合,旧集合保持不变。
有序工厂 persistentListOf、persistentSetOf、persistentMapOf 默认保留集合元素的插入迭代顺序。
哈希工厂 persistentHashSetOf、persistentHashMapOf 更注重时间和空间效率,不保证迭代顺序。
immutable 工厂 immutableListOf、immutableSetOf、immutableMapOf 提供语义明确的不可变集合入口。
转换函数 toImmutable*、toPersistent*、toPersistentHash* 把标准集合、数组、序列转换成库类型。
快照更新 adding、removing、putting、replacingAt 返回新快照,尽量复用旧集合结构。
批量修改 builder、build、mutate 在临时可变构建器中集中修改,再生成一个持久化结果。
适配器 ImmutableListAdapter、ImmutableSetAdapter、ImmutableMapAdapter 把已有只读集合包装成 immutable 接口。

1.3 实现适配

维度 要求
代码复用 集合接口、Trie/Vector/Map 实现、转换函数和自检逻辑全部来自 commonMain。
平台目标 为核心模块增加 ohosArm64,生成 OpenHarmony KLIB 并链接 libimmutablecollections.so。
桥接稳定 只导出目录、索引读取、随机读取、自检和释放四类 C ABI 函数。
UI 完整 页面一次性列出 12 类主要功能,并显示 Kotlin 真实计算得到的结果。
可测试 JVM 测试、Native 编译、动态库链接、HAP 构建和设备启动分别验收。
签名安全 源码只保留签名入口,证书、profile 和密钥材料由开发者本机配置。
仓库规范 项目文档、文章、效果图和源码链接统一使用 AtomGit。

示例页面是独立的真机验收宿主。业务项目仍然可以只依赖 kotlinx.collections.immutable,按照自身 Compose/CMP 或 ArkUI 状态管理方式使用集合。

二、实现路线图

text 复制代码
第 1 阶段:项目初始化     ── 盘点集合 API、KMP 模块和 OpenHarmony 示例边界
第 2 阶段:目标与依赖打通 ── 加入 ohosArm64、独立 example 工程和 Native 构建任务
第 3 阶段:功能总览模型   ── 用 Kotlin 构造 12 类真实集合能力和统一 JSON 结构
第 4 阶段:原生桥接       ── Kotlin/Native C ABI、C++ N-API、参数检查和内存释放
第 5 阶段:ArkUI 页面     ── 展示 immutable/persistent API、转换、Builder 和 mutate
第 6 阶段:构建与验证     ── JVM 测试、Native 链接、未签名 HAP、调试签名和设备运行

每个阶段都使用真实产物作为下一阶段输入:先验证共享 Kotlin 逻辑,再把同一份代码链接为 ARM64 动态库,随后由 CMake 和 N-API 加载到 Stage 工程,最后在鸿蒙设备上启动页面。

三、逐步实现过程

第 1 阶段:项目初始化

1.1 盘点公共 API 和工程边界

项目采用独立核心库、Native 宿主和 DevEco 工程三层结构:

text 复制代码
kotlinx.collections.immutable/
├── core/                         KMP 核心库
│   ├── commonMain/src/            immutable/persistent API 和实现
│   ├── commonTest/src/            跨平台契约测试
│   └── build.gradle.kts           KMP 目标和发布配置
├── example/
│   ├── shared/                    JVM/OHOS 示例和功能检查
│   ├── nativeApp/                 ohosArm64 动态库和 C ABI
│   └── ohosApp/                   DevEco Stage、N-API 和 ArkUI 页面
├── scripts/                      构建、签名工程和 HAP 脚本
└── docs/openharmony/              构建与设备验收记录

example 是独立 Gradle 工程,DevEco 工程不是根项目的 Kotlin 子模块。这样 Gradle 可以单独发布和链接 Native,Hvigor 可以单独执行资源、CMake、ArkTS 和 HAP 任务。

1.2 固定工具链和版本矩阵
项目 配置 用途
Kotlin Multiplatform 2.2.21-1.0.0 支持参考工程使用的 OpenHarmony Native 目标。
JDK 21 Gradle、Kotlin 编译和 Native 任务。
OpenHarmony 目标 ohosArm64 ARM64 真机 KLIB 和动态库。
DevEco product default Stage HAP 构建。
DevEco Studio 5.1.0 Release IDE 版本,负责工程同步、HAP 构建与签名
ABI arm64-v8a HAP 原生库目录。
C++ 桥接 N-API 将 C 字符串转换为 ArkTS 字符串。

执行脚本前可以显式选择 JDK 21:

bash 复制代码
export JAVA_HOME="/path/to/jdk-21"
java -version

在 macOS 上,仓库脚本在 JAVA_HOME 未设置时会自动尝试使用 Homebrew 的 /opt/homebrew/opt/openjdk@21。

1.3 创建功能总览页面

功能总览不是随意填充几条字符串,而是由 Kotlin 侧建立真实目录:

text 复制代码
PersistentList          persistentListOf + adding
PersistentSet           persistentSetOf + 去重
PersistentMap           persistentMapOf + putting
PersistentHashSet       persistentHashSetOf
PersistentHashMap       persistentHashMapOf
Immutable factories     immutableListOf / immutableSetOf / immutableMapOf
Conversions             toImmutable* / toPersistent*
+ and - operators       持久化加减运算
Builder                 builder() / build()
Batch mutate            mutate { }
Immutable adapters      三类 Adapter
Type interfaces         immutable/persistent 接口体系

ArkUI 页面直接渲染这个目录,因此用户打开 HAP 就能看到库的主要功能边界,而不是只看到一个随机列表。

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

2.1 加入 ohosArm64 目标

核心模块在保留现有目标的同时加入:

kotlin 复制代码
kotlin {
    applyDefaultHierarchyTemplate()
    explicitApi()

    linuxX64()
    linuxArm64()
    ohosArm64()
    macosX64()
    macosArm64()
    iosArm64()
    // 其他原有目标保持不变
}

ohosArm64 与 native 层级共享 commonMain 的集合实现,不增加一套鸿蒙专属 List、Set 或 Map。

2.2 Native focused build 的作用

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

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

导出的符号为:

text 复制代码
ImmutableCollectionsCatalog
ImmutableCollectionsGet
ImmutableCollectionsRandom
ImmutableCollectionsRunChecks
ImmutableCollectionsFree

链接器只开放这些明确入口,Kotlin/Native 内部实现不会变成不受控的 ABI。

2.3 配置独立消费工程

example/shared 通过本地 Maven 消费适配后的库:

kotlin 复制代码
kotlin {
    jvm()
    ohosArm64()
    sourceSets {
        commonMain.dependencies {
            api("org.jetbrains.kotlinx:kotlinx-collections-immutable:${property("immutableCollectionsVersion")}")
        }
    }
}

根工程先执行 publishToMavenLocal,示例工程再解析相同版本,确保示例不是偷偷依赖根项目源码。

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

prepareOhos 在 Native 链接成功后复制:

kotlin 复制代码
from(layout.buildDirectory.dir("bin/ohosArm64/debugShared")) {
    include("libimmutablecollections.so")
    into("libs/arm64-v8a")
}
from(layout.buildDirectory.dir("bin/ohosArm64/debugShared")) {
    include("libimmutablecollections_api.h")
    into("src/main/cpp/include")
}

动态库和头文件属于构建产物,源码仓库不提交 DevEco/CMake 中间目录;开发者执行脚本即可重新生成。

第 3 阶段:功能总览模型

3.1 为什么需要统一 JSON 契约

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

text 复制代码
PersistentList/Set/Map 实例
        ↓ Kotlin catalog()
ImmutableCollectionsExample
        ↓ Kotlin/Native toJson()
C ABI char*
        ↓ C++ napi_create_string_utf8()
ArkTS JSON.parse()
        ↓
功能总览卡片

页面只消费 JSON,不复制集合创建和更新逻辑。JVM 测试与设备页面使用同一份 catalog 和 runChecks。

3.2 示例数据结构
kotlin 复制代码
public data class ImmutableCollectionsExample(
    val id: String,
    val kind: String,
    val value: String,
    val size: Int,
)

kind 表示功能类别,value 保存可读的 API 示例和运行结果,size 用于页面右侧的数量提示。

3.3 真实功能目录
kotlin 复制代码
public fun catalog(): List<ImmutableCollectionsExample> = listOf(
    example("list", "PersistentList", "persistentListOf(1, 2, 3).adding(4)", 4),
    example("set", "PersistentSet", "persistentSetOf(\"a\", \"a\")", 1),
    example("map", "PersistentMap", "persistentMapOf(...).putting(...)", 1),
    example("hash-set", "PersistentHashSet", "persistentHashSetOf(...) ", 2),
    example("hash-map", "PersistentHashMap", "persistentHashMapOf(...) ", 1),
    // 转换、运算符、Builder、mutate、Adapter 和接口继续列出
)

功能目录的展示文字和自检结果由共享 Kotlin 生成,ArkTS 不对这些字符串做业务判断。

3.4 自检和错误边界

示例自检覆盖:

  1. persistent list 的原值保持不变;
  2. persistent set 会去重;
  3. persistent map 能按 key 返回新值;
  4. hash set/map 工厂可用;
  5. 标准集合可以转换为 persistent 集合;
  6. + 和 - 返回持久化结果;
  7. Builder 可以生成快照;
  8. mutate 可以批量修改;
  9. 目录覆盖全部主要功能;
  10. N-API 可以返回 JSON。

Native 异常统一转换为 {"error":"..."},C++ 在创建 ArkTS 字符串后立即调用 ImmutableCollectionsFree。

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

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

PersistentList、PersistentSet 和 PersistentMap 属于 Kotlin 运行时对象,不能把对象地址直接当成 JavaScript 对象使用。最终采用三层桥接:

text 复制代码
ArkTS
  │ JSON string
  ▼
C++ N-API entry
  │ const char*
  ▼
Kotlin/Native C ABI
  │ persistent collection implementation
  ▼
UTF-8 JSON + explicit free
4.2 方案对比
方案 优点 缺点 选用
直接导出 Kotlin 集合对象 代码少 ABI、泛型和生命周期不可控 ❌
只导出集合数量 实现简单 看不到真实 API 结果 ❌
C ABI + JSON 边界清晰、易调试、易扩展 有一次序列化开销 ✅
在 ArkTS 重写集合逻辑 页面简单 KMP 与 ArkTS 容易产生两套实现 ❌
4.3 Kotlin/Native 导出函数
kotlin 复制代码
@CName("ImmutableCollectionsCatalog")
public fun catalogNative(): CPointer<ByteVar> = response {
    ImmutableCollectionsExamples.catalog()
        .joinToString(prefix = "[", postfix = "]") { it.toJson() }
}

@CName("ImmutableCollectionsGet")
public fun getNative(index: Int): CPointer<ByteVar> = response {
    ImmutableCollectionsExamples.get(index).toJson()
}

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

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

返回值使用 Native heap 分配的、以 0 结尾的 C 字符串。每一块缓冲区都由 Free 函数释放。

4.4 C++ N-API 方法分发

C++ 注册目录、读取、随机读取和自检方法:

cpp 复制代码
napi_property_descriptor methods[] = {
    {"getCatalog", nullptr, Catalog, nullptr, nullptr, nullptr,
        napi_default, nullptr},
    {"getImmutableCollections", nullptr, GetImmutableCollections,
        nullptr, nullptr, nullptr, napi_default, nullptr},
    {"randomImmutableCollections", nullptr, RandomImmutableCollections,
        nullptr, nullptr, nullptr, napi_default, nullptr},
    {"runChecks", nullptr, RunChecks, nullptr, nullptr, nullptr,
        napi_default, nullptr},
};

getImmutableCollections 会检查参数数量、整数类型和非负范围,然后调用 ImmutableCollectionsGet。C++ 完成字符串转换后立即释放 Native 缓冲区。

4.5 CMake 加载动态库
cmake 复制代码
add_library(immutablecollections SHARED IMPORTED)
set_target_properties(immutablecollections PROPERTIES
  IMPORTED_LOCATION
  "${CMAKE_CURRENT_SOURCE_DIR}/../../../libs/arm64-v8a/libimmutablecollections.so")

add_library(entry SHARED napi_init.cpp)
 target_include_directories(entry PRIVATE "${CMAKE_CURRENT_SOURCE_DIR}/include")
 target_link_libraries(entry PRIVATE immutablecollections libace_napi.z.so)

第 5 阶段:ArkUI 页面

5.1 ArkTS 客户端
typescript 复制代码
export interface ImmutableCollectionsExample {
  id: string;
  kind: string;
  value: string;
  size: number;
}

export function catalog(): ImmutableCollectionsExample[] {
  return JSON.parse(immutablecollectionsNative.getCatalog()) as ImmutableCollectionsExample[];
}

ArkTS 页面只把 JSON 映射为界面模型,不创建 Kotlin 集合,也不重新实现 adding、putting 或 mutate。

5.2 功能总览布局
text 复制代码
标题区          immutablecollections / NATIVE READY
说明区          完整功能总览 · KMP / OpenHarmony
卡片区          12 类公开 API 和真实示例结果
状态区          10/10 公共检查通过
底部说明        Kotlin/Native → N-API → ArkTS

每张卡片显示功能名称、调用方式、结果和数量;用户打开应用即可直观看到这个三方库的功能边界。

5.3 页面与库的职责分离

页面负责:

  • 调用 getCatalog;
  • 解析 JSON;
  • 显示功能卡片;
  • 显示 Native 自检状态;
  • 处理错误文本和重新读取。

Kotlin 负责:

  • 创建真实的 persistent/immutable/hash 集合;
  • 执行转换、运算符、Builder 和 mutate;
  • 生成功能描述和 JSON;
  • 执行公共检查。

第 6 阶段:构建与验证

6.1 OpenHarmony Native 准备
bash 复制代码
./scripts/build-openharmony.sh

脚本会完成本地发布、示例测试、ohosArm64 编译、Native 动态库链接和头文件复制。

6.2 构建未签名 HAP
bash 复制代码
/Applications/DevEco-Studio.app/Contents/tools/hvigor/bin/hvigorw \
  --mode module -p module=entry@default -p product=default \
  -p buildMode=debug clean assembleHap --no-daemon

未签名产物:

text 复制代码
example/ohosApp/entry/build/default/outputs/default/entry-default-unsigned.hap
6.3 签名工程和调试包

源码不保存证书和私钥。复制独立签名工程:

bash 复制代码
./scripts/prepare-signing-project.py /absolute/path/to/signing-project

配置 DevEco 签名后构建:

bash 复制代码
./scripts/build-hap.sh /absolute/path/to/signing-project

没有签名文件的 HAP 不能安装到真实鸿蒙设备;开发调试可以使用 DevEco 默认调试签名,正式发布必须使用开发者自己的证书和 profile。

四、完整代码对照

4.1 整体架构

text 复制代码
ArkUI 页面
    │ getCatalog / runChecks
    ▼
C++ N-API(libentry.so)
    │ const char* JSON
    ▼
Kotlin/Native C ABI
    │
libimmutablecollections.so
    │
commonMain persistent collections

4.2 文件清单

文件 职责
core/commonMain/src/ImmutableList.kt immutable/persistent list 接口
core/commonMain/src/ImmutableSet.kt immutable/persistent set 接口
core/commonMain/src/ImmutableMap.kt immutable/persistent map 接口
core/commonMain/src/extensions.kt 工厂、转换、运算符和 mutate
example/shared/.../ImmutableCollectionsExamples.kt 功能目录、真实示例和自检
example/nativeApp/.../NativeBridge.kt C ABI、JSON 返回和内存释放
example/ohosApp/.../ImmutableCollectionsClient.ets N-API JSON 解析
example/ohosApp/.../Index.ets 功能总览页面
example/ohosApp/entry/src/main/cpp/napi_init.cpp N-API 导出和参数检查
scripts/build-openharmony.sh 测试、Native 链接和产物复制
scripts/build-hap.sh 签名工程 HAP 构建
docs/openharmony/VALIDATION.md 构建和设备验收记录

4.3 关键 API 对照

层次 API 作用
Kotlin persistentListOf 创建持久化列表
Kotlin persistentSetOf 创建有序持久化集合
Kotlin persistentMapOf 创建有序持久化映射
Kotlin persistentHashSetOf 创建哈希持久化集合
Kotlin persistentHashMapOf 创建哈希持久化映射
Kotlin adding / putting 返回修改后的新集合
Kotlin toPersistent* 转换标准集合
Kotlin builder / mutate 批量修改并生成快照
Native ImmutableCollectionsCatalog 返回功能目录 JSON
Native ImmutableCollectionsGet 返回单项 JSON
Native ImmutableCollectionsFree 释放 Native 字符串
N-API getCatalog ArkTS 获取目录
ArkTS ForEach 展示 12 类功能卡片

五、关键决策说明

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

只有真正链接 ohosArm64 动态库,才能证明共享集合实现进入 OpenHarmony 运行时,而不是停留在页面字符串或 JVM 测试。

决策 2:示例通过 Maven 产物消费库

example/shared 解析本地发布产物,ohosApp 单独接收 .so 和头文件,避免根工程通过但 DevEco 无法打包。

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

JSON 让 ArkTS、C++ 和 Kotlin/Native 的边界清晰可调试,同时避免暴露 Kotlin 集合内部对象布局。

决策 4:页面展示真实 API 结果

功能总览中的每一项都对应 Kotlin 真实工厂函数或操作,页面只展示结果,避免把"演示 UI"误认为"库的实现"。

决策 5:构建产物和源码分离

.so、HAP、CMake 中间目录和 DevEco 缓存不提交 AtomGit;任何开发者都可以使用自己的 SDK 重新生成。

六、测试与验证

6.1 测试命令

bash 复制代码
# OpenHarmony 目标、Native 动态库和示例测试
./scripts/build-openharmony.sh

# 单独运行共享示例测试
(cd example && ./gradlew :shared:jvmTest -Pkotlin_Werror_override=disable)

# DevEco 未签名 HAP
cd example/ohosApp
/Applications/DevEco-Studio.app/Contents/tools/hvigor/bin/hvigorw \
  --mode module -p module=entry@default -p product=default \
  -p buildMode=debug clean assembleHap --no-daemon

6.2 验证结果

本次适配已经验证:

text 复制代码
shared:jvmTest                         PASSED
compileKotlinOhosArm64                PASSED
linkDebugSharedOhosArm64              PASSED
nativeApp:prepareOhos                  PASSED
DevEco CompileArkTS                    PASSED
DevEco PackageHap                     PASSED
entry-default-unsigned.hap             GENERATED

6.3 真机启动

bash 复制代码
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 kotlinx.collections.immutable.sample

未签名包用于查看产物和 DevEco 构建结果;真机安装需要调试签名或正式签名包。

七、运行效果

7.1 功能总览真机效果图

截图中可以看到:

  • NATIVE READY 表示页面已经加载 Native 动态库;
  • 页面标题明确显示 Kotlin Multiplatform/OpenHarmony;
  • PersistentList、PersistentSet、PersistentMap 直接展示真实调用;
  • PersistentHashSet 和 PersistentHashMap 展示哈希实现;
  • Immutable factories 和 Conversions 展示工厂与转换能力;
  • 下方继续展示 +/- operators、Builder、mutate、Adapter 和接口体系;
  • 页面底部显示 Kotlin/Native、N-API 和 ArkTS 的完整调用链。

7.2 效果说明

页面不是一个普通的静态说明页。每个卡片由 Kotlin catalog() 生成,再由 Native C ABI 返回 JSON,最后由 ArkTS ForEach 渲染。重新读取会重新执行 Native 目录调用和公共自检。

7.3 命令速查

bash 复制代码
# 构建 Native 和生成头文件
./scripts/build-openharmony.sh

# 构建未签名 HAP
cd example/ohosApp
/Applications/DevEco-Studio.app/Contents/tools/hvigor/bin/hvigorw \
  --mode module -p module=entry@default -p product=default \
  -p buildMode=debug clean assembleHap --no-daemon

# 构建签名 HAP
./scripts/build-hap.sh /absolute/path/to/signing-project

八、遗留问题与改进方向

8.1 踩坑复盘

  1. 只改 ArkTS 页面不算 KMP 适配 :必须把 commonMain 集合实现真正编译成 ohosArm64 动态库。
  2. 不可变接口和持久化实现要分开说明 :Immutable* 表达稳定契约,Persistent* 提供返回新集合的修改操作。
  3. N-API 不负责集合业务:C++ 只做参数检查、字符串转换和释放,集合语义放在 Kotlin。
  4. 示例数据不能替代功能目录:页面应该展示工厂、转换、运算符、Builder 和 mutate 等完整能力。
  5. JDK 版本必须匹配:OpenHarmony Kotlin/Native 构建使用 JDK 21。
  6. 未签名 HAP 不能直接安装:设备安装需要 DevEco 调试签名或开发者自己的正式签名。

8.2 已知问题

  • OpenHarmony ohosArm64 主要面向 ARM64 设备;
  • 老版本 Kotlin/Native 编译器可能提示上游返回值检查警告;
  • Adapter 不会冻结底层标准集合,调用方仍需保证底层集合没有被其他引用修改;
  • 功能总览用于验收主要 API,不替代完整 API 文档和契约测试;
  • HAP 签名证书和 profile 依赖开发者本机环境。

8.3 未来优化方向

  • 增加 Compose Multiplatform 业务页面,展示集合在状态管理中的实际用法;
  • 增加更多泛型、嵌套集合和大数据量性能样例;
  • 在持续集成中加入 ohosArm64 链接和未签名 HAP 构建;
  • 增加 OpenHarmony 设备上的自动化启动和截图验收;
  • 为 AtomGit 发布流程增加版本化 Native 产物和 API 兼容检查。

九、总结

9.1 核心难点回顾

本次适配真正需要处理的不是"在页面里显示一个列表",而是一条完整跨端链路:

text 复制代码
KMP commonMain 集合实现
    → ohosArm64 Kotlin/Native
    → libimmutablecollections.so
    → C ABI
    → C++ N-API
    → ArkTS JSON
    → ArkUI 功能总览

9.2 封装层次

  • KMP 层:定义 immutable/persistent 接口、工厂和结构共享实现;
  • Native 层:生成 ARM64 动态库,并通过 C ABI 输出有限入口;
  • N-API 层:完成参数检查、字符串转换和 Native 内存释放;
  • ArkTS 层:管理页面状态和功能卡片;
  • DevEco 层:完成 CMake、ArkTS、HAP、签名和设备启动。

9.3 三条经验

  1. 先让真实集合 API 在 JVM 和 Native 通过,再接入 ArkUI;
  2. 用 JSON 和少量 C ABI 代替跨语言对象传递;
  3. 把自动测试、HAP 构建和真机安装分别记录,避免把"页面显示成功"误认为"库适配完成"。

9.4 适配成果

当前 kotlinx.collections.immutable 已完成:

  • ohosArm64 Kotlin/Native 目标;
  • persistent/immutable/hash 集合在 OpenHarmony ARM64 上的编译;
  • Kotlin/Native + C ABI + N-API 桥接;
  • 12 类主要公开功能的 ArkUI 总览页面;
  • JVM 测试、Native 链接、未签名 HAP 构建;
  • DevEco 调试签名包的设备安装与启动;
  • AtomGit 项目文档、效果图和 OpenHarmony 验收记录。

适配过程 FAQ

以下问题来自 ohosArm64 目标接入、NAPI 桥接、HAP 构建与真机验收的实际流程,按出现频率排序。

Q1:DevEco Studio Sync 失败或 ohpm 依赖下载超时,怎么处理?

  • 确认 DevEco Studio 版本与工程 build-profile.json5 的 compatibleSdkVersion 一致;
  • 在 File > Settings > Build, Execution, Deployment > Ohpm 配置可用的 ohpm 仓库镜像或代理;
  • 删除 oh_modules、oh-package-lock.json5 与 .hvigor 缓存后重新 Sync;
  • 公司网络需要代理时,同时给 ohpm 与 hvigor 配置 http.proxy。

Q2:CMake 报 Missing .../libxxx.so 或找不到 Kotlin/Native 动态库?

  • 先在 example 目录执行 ./gradlew :nativeApp:linkDebugSharedOhosArm64(模拟器用 linkDebugSharedOhosX64);
  • 确认 libxxx.so 与 libc++_shared.so 已复制到 ohos/entry/libs/<abi>/;
  • 检查 entry/build-profile.json5 的 abiFilters 与设备 ABI 是否一致(真机 arm64-v8a,模拟器 x86_64);
  • 清理 entry/.cxx 与 entry/build 后重试。

Q3:ArkTS 报 Cannot find module 'libxxx_napi.so' 或调用返回 undefined?

  • 确认 CMake 的 add_library(<target> SHARED ...) 名称与 import ... from 'libxxx_napi.so' 完全一致;
  • 解包 HAP,确认 libs/<abi>/libxxx_napi.so 已打包;
  • 删除 entry/build、entry/.cxx 与 oh_modules 后重新构建;
  • 检查 module.json5 的 srcEntry 与页面路径是否正确。

Q4:真机安装报 signature verification failed 或 install failed?

  • 在 DevEco Studio 的 Signing Configs 配置证书、Profile 与密码;
  • bundleName 必须与签名 profile 中的 bundle name 一致;
  • 设备需加入 profile 的 deviceIds,调试证书有效期需覆盖当前时间;
  • 用 hdc list targets 确认设备在线,再执行 hdc install -r xxx-signed.hap。

Q5:应用启动白屏、闪退或 NAPI 调用崩溃?

  • 用 hdc hilog | grep -i "napi\|kotlin" 查看原生日志;
  • 确认 libc++_shared.so 与 Kotlin/Native 工具链版本匹配,且 ABI 与设备一致;
  • 检查 C ABI 返回的字符串是否由 Native 侧分配、ArkTS 侧复制后是否调用 XxxFree 释放;
  • 在 DevEco 的 Native 调试配置中附加 lldb,定位空指针与越界访问。

Q6:Gradle 报 ohosArm64 目标不存在或 Kotlin/Native 编译失败?

  • 使用鸿蒙发行版 Kotlin(如 2.2.21-1.0.0),并在 settings.gradle.kts 配置 eazytec 仓库;
  • Gradle 建议 8.x、JDK 21,版本不匹配时先统一工具链;
  • 确认 kotlin { ohosArm64() } 已应用,且 local.properties 的 SDK 路径有效;
  • 清理 ~/.gradle/caches 中对应版本的 Kotlin/Native 缓存后重试。

Q7:HAP 构建报 SDK 版本不匹配或 compatibleSdkVersion 错误?

  • 对齐 DevEco Studio、HarmonyOS SDK 与 build-profile.json5 中的 compatibleSdkVersion;
  • 升级 DevEco Studio 后同步升级 hvigor 与 SDK,避免 modelVersion 与 IDE 不匹配;
  • 命令行构建时设置 DEVECO_SDK_HOME 指向 DevEco 的 sdk 目录。

Q8:命令行 hvigor 构建失败,但 DevEco 里能构建?

  • 使用 DevEco 自带的 node 与 hvigor:
    "$DEVECO/Contents/tools/node/bin/node" "$DEVECO/Contents/tools/hvigor/bin/hvigorw.js" ...;
  • 设置 JAVA_HOME、NODE_HOME、DEVECO_SDK_HOME;
  • 加上 --no-daemon 排除守护进程缓存影响,并清理 entry/build 与 .hvigor。

参考文档

相关推荐
apple_692658853 小时前
MOSS Image 生图全攻略:工作流、特调模型与参数详解
开源·生图
分布式存储与RustFS3 小时前
把 LSM 引擎直接架在对象存储上:slatedb 能不能跑在 RustFS 上?
运维·云原生·开源·对象存储·分布式存储·s3·性能基准
wflynn3 小时前
GitHub 日榜趋势速报 | 2026-10-25
开源·github
文慧的科技江湖3 小时前
2026年10月25日微电网EMS行业早报:标准从舱管到模块,存量电站要自己补课
开源·储能·微电网·能源管理系统·源网荷储·电力安全·新型储能
2501_944525765 小时前
开源鸿蒙平台 KMP_CMP 三方库「Kaml」适配全流程
开源·kotlin·harmonyos
miofly5 小时前
Apollo 经济学家:AI 在科技行业外的财务回报为零
开源·github
1024奇点5 小时前
【仓颉语言入门 · 第二篇 第 15 课】
开发语言·数据库·ide·开源
2501_944525545 小时前
开源鸿蒙平台 KMP_CMP 三方库「Clikt」适配全流程
华为·开源·harmonyos
小熊吃保安5 小时前
Windows也能用 Apple Watch 解锁
windows·rust·开源