本文记录 Kore 接入 OpenHarmony 的完整过程,覆盖设计系统库盘点、社区 Compose 制品线选型、整库真实编译、ohosArm64 / ohosX64 目标、Kotlin/Native 动态库、C ABI/N-API 桥接、ArkUI 真机页面、HAP 构建和真机验收。

Kore 是基于 Compose Multiplatform 的设计系统底座(io.github.dev778g-me:kore 1.0.0-alpha03,MIT):25+ 预样式组件(buttons / card / dialog / dropdown / textFields / navigationBar / tabRow / accordion / stepper……)站在一套完整令牌系统上——KoreColorScheme 23 个语义色槽(明 / 暗双主题)、RadixColors 33 个命名色板 × 12 步、TailwindColors 22 个色板 × 11 步、KoreTypography 15 级字阶、KoreShapes / KoreSizes 档位、AbsoluteSmoothCornerShape 平滑圆角(连续曲率"方圆")与 koreRipple 涟漪。本次适配与社区两条既有路线都不同:qrose / haze 走"零依赖内核提取"(因为它们有纯 Kotlin 编码器可拆),KStore 走"kotlinx 制品线真编译"(因为存储引擎只依赖 kotlinx);而 Kore 是纯 Compose UI 库——每一行都站在 androidx.compose 类型上,没有可拆的纯 Kotlin 子集。社区 Maven 上已经出现带 ohosArm64 / ohosX64 klib 的 Compose 制品线(1.12.0-0.1.0-07),因此采用整库原样编译 + 无头令牌门面路线(compose-shimmer / bubbles-compose 已审同款):上游 commonMain 的 25 个组件与全部主题代码零修改编译进 .so,KoreHeadless 把真实令牌系统提升为 UI-free 表面,ArkUI 页面用 Kotlin 算出的真实数值重绘——令牌即内核,像素归 ArkTS。

项目地址: AtomGit/oh-tpc/Kore.git

上游仓库: dev778g-me/Kore

开发工具: 华为云码道

环境安装: 环境安装指南

一、背景

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

Kore 为 Compose Multiplatform 应用提供"预样式组件 + 主题令牌"两层资产:组件层把按钮 / 卡片 / 对话框 / 下拉 / 文本域等 25 类交互件的样式、状态色、圆角、涟漪全部预置好;令牌层用 KoreTheme / DefaultKoreTheme 一次注入语义色(background / surface / primary / complementary / success / error × on 色 × 容器色共 23 槽)、15 级字阶与尺寸档位,RadixColors 与 TailwindColors 两套业界色板原样内置。KMP 项目把这些能力放在 commonMain,由 Android、iOS、桌面、Web 共享。OpenHarmony 应用同样需要这套设计系统,但上游构建(Kotlin 2.3.21 / Compose 1.10.0 / AGP 9.0.0)不会生成 OpenHarmony 变体。

如果只把示例页面改成 ArkTS 手抄一遍颜色值,页面可以长得像,却无法证明上游设计系统在 OpenHarmony ARM64 设备上完成编译、链接和执行——手抄的令牌第二天就会和上游漂移。适配需要同时验证令牌引擎、Compose 编译链、Native 动态库、C ABI、N-API、ArkUI 页面和 HAP 交付链路。

障碍具体问题
目标缺失必须增加 ohosArm64() / ohosX64() 才能生成 OpenHarmony KLIB。
没有 composition host鸿蒙上没有把 Composable 挂上屏幕的宿主(社区线只有 klib,没有 AndroidWindow / UIKit 对应物),Composable UI 不能直接上屏。
版本线不能混用上游基于 Compose 1.10.0 / Kotlin 2.3.21;社区 Compose 鸿蒙线是 1.12.0-0.1.0-07(Kotlin 2.4.20 线),两条线不能在一个 target 里混搭。
compose-resources 不可用上游字阶绑定 9 个 Google Sans Flex ttf(compose-resources 生成 Res 类),鸿蒙构建没有这套资源机器。
语言边界不同ArkTS 不能直接持有 Kotlin 对象,需要 C ABI、C++ N-API 和 JSON。
交付链路复杂.so、CMake、N-API、HAP、签名和 hdc 都要单独验收。

因此,本项目把边界放在:上游 commonMain 整库的 ohos 编译、根构建替换(cmp_qrose 模式)、无头令牌门面与 C ABI/N-API 桥接层。令牌数值完全由上游原版实现负责,ArkTS 只负责调用 Native、解析 JSON 和把数值画出来。

1.2 库提供的能力

Demo 提供一个真实的设计令牌会话,对应桥接的全部 11 个入口:

  • 版本握手:koreVersion() 返回 fork 版本号 / 上游版本 / Kotlin 线 / Compose 线,来自真实 Native 调用而非静态文字;
  • 语义主题(scheme):KoreDefaults.defaultLightColorScheme / defaultDarkColorScheme 的 23 个语义槽(background…transparent),值形如 #FF1C2024;
  • Radix 色板(radixNames / radix):33 个命名色板(gray / blue / orange / tomato / crimson / iris / jade / mint / sky…,white / black 单阶),每阶明 / 暗 × 12 步;
  • Tailwind 色板(tailwindNames / tailwind):22 个色板 × 11 步(slate / zinc / emerald / fuchsia / rose…);
  • 字阶(typography):KoreTypography() 默认值——display1…label3 共 15 级的 weight / size / lineHeight / letterSpacing;
  • 尺寸与圆角档(tokens):defaultSizes xxs…xl 与 defaultShapes xs…xl 的 dp 值;
  • 平滑圆角几何(shape):传 width / height / radius / smoothing,返回 M/L/C/Z 路径命令串——由库内 SmoothCorner 连续曲率数学实时算出,ArkUI Canvas 重放;
  • 字族清单(fonts):9 个 Google Sans Flex ttf 文件名与字重映射,配合 demo 的 rawfile 字体注册;
  • 涟漪默认值(ripple):koreRipple 公共工厂参数(color / bounded / radius)。

响应字段:

字段作用
ok操作是否成功,失败时 error 带原文。
op回显本次操作类型。
数据键step1…step12(色阶)、<样式>.size(字阶)、cmd0…cmdN(路径命令)、count(条目数)等,全部为字符串。

令牌侧完整能力(23 槽语义色、两套业界色板、15 级字阶、平滑圆角、涟漪节点、25 个 Composable 组件本体)全部在上游 commonMain 里原样随编译进入 .so;JVM 侧保留整库 compileKotlinJvm 编译级交叉验证(上游未携带测试套件),证明 ohos .so 里编译的令牌逻辑没有漂移。

1.3 实现适配

维度要求
代码复用kore 模块全部 commonMain 文件(44 个:25 组件 + 主题系统)原样编译,不提取、不重写;唯一改动是全库唯一 expect 接缝 googleSansFlex()(上游资源体逐字节归档进 src/composeMain,同 qrose 归档 Painter 的处置)。
平台目标根构建直接声明 ohosArm64 + ohosX64,保留 jvm() 作交叉验证,库模块出 KLIB,example/nativeApp 出双 ABI 动态库。
构建隔离根构建替换为极简 ohos 构建(cmp_qrose 模式);composeApp / androidApp / themebuilder / iosApp / docs(依赖 AGP / material3 / hot-reload 等鸿蒙线不发布的制品)留盘不进构建,上游构建备份为 *.upstream。
桥接稳定使用 2 个 C ABI 和 UTF-8 JSON,不传递 Kotlin 对象地址;异常永远不跨 FFI。
依赖纪律版本主体与上游完全一致(jvm 验证线 Compose 1.10.0),ohos 线换到社区制品 1.12.0-0.1.0-07(换线不换版)。
UI 完整单页四 Tab 覆盖全部桥接入口:主题 / 调色板 / 字体 / 平滑圆角。
仓库规范项目说明、文章、效果图和源码链接统一使用 AtomGit。

本项目的 ArkUI 页面不是 Compose Multiplatform UI 实现。鸿蒙没有 Compose 宿主,ArkTS Demo 用桥返回的真实令牌数值重绘界面——颜色、字号、圆角、几何没有一个手抄常量,这就是"设计系统跨平台"在当前鸿蒙生态下的最诚实形态。

二、实现路线图

第 1 阶段:项目初始化     ── 盘点模块与依赖边界、确认社区 Compose ohos 制品线、确定交付物
第 2 阶段:目标与依赖打通 ── 根构建替换、eazytec Maven 置前、Kotlin 2.4.20-0.1.0-07 钉版、双线依赖隔离
第 3 阶段:桥协议建模     ── 令牌 JSON 契约(11 op)、路径命令编码、错误通道
第 4 阶段:原生桥接       ── @CName C ABI、C++ N-API、参数校验和内存释放
第 5 阶段:ArkUI 宿主     ── 版本握手、rawfile 字体注册、四 Tab 令牌渲染、Canvas 重放
第 6 阶段:示例与验证     ── HAP、签名、设备安装、真机运行和效果图

三、逐步实现过程

第 1 阶段:项目初始化

1.1 工程边界
kore/                                上游设计系统库(进 ohos 构建,jvm + ohosArm64 + ohosX64)
kore/src/commonMain/                 44 个上游文件原样编译(25 组件 + 主题系统,零修改)
kore/src/composeMain/                归档:上游 googleSansFlex() 资源体(不编译,等 CMP 宿主出现再接回)
kore/src/jvmMain/ + src/ohosMain/    新增:googleSansFlex() 两个 actual(全库唯一接缝)
kore/src/ohosMain/.../KoreHeadless.ohos.kt   新增:无头令牌门面
composeApp/ androidApp/ themebuilder/ iosApp/ docs/   上游 demo 与文档站,留盘不进 settings
example/nativeApp/                   Kotlin/Native 动态库(Bridge + MiniJson)
example/harmonyApp/                  DevEco Stage、CMake N-API 和 ArkTS 页面
README.OpenHarmony.md / _CN.md       鸿蒙适配说明(中英双份,中文主文档 21 节)
README.upstream.md                   上游原文保存

构建拓扑走 cmp_qrose 模式(根构建即 ohos 构建):settings.gradle.kts 只 include :kore 与 :example:nativeApp,rootProject.name = "ohos_kore"。上游的 AGP 9 / compose hot-reload / lint 插件管理、vanniktech Maven Central 发布、compose.resources 字体生成全部随根构建替换剥离——本 fork 不发 Maven Central。

1.2 工具链矩阵
项目配置
Kotlin MultiplatformHarmonyOS Kotlin 2.4.20-0.1.0-07(eazytec Maven)
JDK21(org.gradle.java.home 指向本机 Temurin 21)
OpenHarmony 目标ohosArm64 + ohosX64(jvm() 保留作交叉验证)
Gradle9.6.1(上游 wrapper 9.3.1 钉到 9.6.1;2.4.20 线支持 Gradle 9.x)
Compose(ohos 线)org.jetbrains.compose.*:1.12.0-0.1.0-07(社区线,发布双 ohos klib,不发布 *-desktop 子制品)
Compose(jvm 验证线)1.10.0(Maven Central,上游 1.0.0-alpha03 所用线)
DevEco product5.1.0(18) 兼容
DevEco Studio5.1.0 Release
ABIarm64-v8a + x86_64
Bundleorg.terminator.ohos.kore
export JAVA_HOME="/path/to/jdk-21"     # Windows 下写入 gradle.properties 的 org.gradle.java.home
java -version
1.3 源码依赖度盘点
源码集文件依赖形态处置
commonMain/components/**(25 目录)38compose foundation / ui / animation / runtime,零平台 API整体编译(jvm + 双 ohos)
commonMain/themes/**(色板 / 字阶 / 形状 / 涟漪)8compose ui.graphics / text / unit + 纯 Kotlin 数学整体编译
commonMain/.../typography/Typography.kt1compose-resources 生成的 Res.font.*唯一接缝:expect/actual 化,上游体归档 composeMain
androidMain / iosMain(模板残留)2平台 hello留盘不进编译(无对应 target)

两个盘点结论决定了整个适配形态:

其一,androidx.graphics:graphics-shapes 在上游版本目录里声明、但被零个源文件 import——死依赖直接从 ohos 构建移除,少一个解析变数。

其二,expect 声明全库只有 1 处:@Composable fun googleSansFlex(): FontFamily(KoreDefaults.defaultTypography 唯一调用方)。上游体把 9 个 Google Sans Flex ttf 接成 FontFamily,依赖 compose-resources 的 Res 生成类——鸿蒙构建没有这套机器。处置是三件套:

// kore/src/commonMain/.../typography/Typography.kt   (改写为 expect)
@Composable
public expect fun googleSansFlex(): FontFamily

// kore/src/ohosMain/.../typography/Typography.ohos.kt (ohos actual;jvmMain 同款)
@Composable
public actual fun googleSansFlex(): FontFamily = FontFamily.Default

// kore/src/composeMain/.../typography/Typography.kt   (上游原文逐字节归档,不编译)

字体文件本身没有丢:9 个 ttf 随 demo 的 rawfile 分发,ArkTS 侧用系统 font.registerFont 注册后按字重取用(见第 5 阶段)——同一份字体资产,换了注册通道。

1.4 页面边界
标题区          Kore 设计令牌 / 版本徽章(1.0.0-alpha03-ohos.1,真实 Native 握手)
技术栈行        Kotlin 2.4.20-0.1.0-07 · Compose 1.12.0-0.1.0-07 · cmp_qrose 模式适配 · 全部数值来自 libohoskore.so
Tab 1 主题      明/暗切换 → 23 槽语义色卡(键名 + hex)+ 令牌驱动组件演示(主按钮/卡片/状态圆点)+ 涟漪默认值表
Tab 2 调色板    Radix 33 阶名 chips + 明/暗切换 → 12 步色带(步语义注释);Tailwind 22 阶 chips → 11 步色带
Tab 3 字体      15 级字阶逐级真字体渲染(Google Sans Flex 9 字重)+ 尺寸/圆角档位条
Tab 4 平滑圆角  radius / smoothing 双滑条 → shape op → Canvas 路径重放(Kotlin 几何实时生成)+ 默认轮廓对照

页面启动时先调 koreVersion() 做握手,再依次拉取 scheme / fonts / 字阶令牌,未就绪时显示加载页。

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

2.1 制品线验证(本适配的成败手)

先查社区 Maven(https://maven.eazytec-cloud.com)上有没有 Compose 的 ohos 制品。逐个验证 .module 元数据:

org.jetbrains.compose.runtime:runtime:1.12.0-0.1.0-07
  variants: ohosArm64Api-published / ohosX64Api-published / …(jvm 亦在)
org.jetbrains.compose.foundation:foundation:1.12.0-0.1.0-07
  variants: 同上(foundation → ui / animation / runtime 传递齐全)
org.jetbrains.compose.ui / animation / material3…
  variants: 同上(本库只用 foundation 传递闭包)

三个关键结论:其一,社区 Compose 线基于 Kotlin 2.4.20 构建,工具链必须同线;其二,该线不发布 *-desktop 子制品,jvm() 交叉验证目标不能从这条线解析——这就是"双线依赖"的由来(compose-shimmer / eungabi / AdaptiveStack 同款拆法):ohos 目标走社区线、jvm 目标走 Maven Central 上游线,换线不换版(1.12.0-0.1.0-07 对 1.10.0,版本主体只升不乱,jvm 验证线与上游 1.0.0-alpha03 完全同版);其三,compose-resources 的 Gradle 生成机器不在这条线上,印证了 1.3 节的字体接缝决策。

2.2 工具链钉版(为什么是 2.4.20-0.1.0-07)

社区此前批准的钉法是 HarmonyOS Kotlin 2.2.21-1.0.0(qrose 用的)。但社区 Compose 线 1.12.0-0.1.0-07 的 klib 元数据由 Kotlin 2.4.20 线产出,2.2.21 旧线读不了;工具链版本跟着依赖走:Kotlin 钉 2.4.20-0.1.0-07。上游源码本来就是 Kotlin 2.3.21 写的,2.4.20 与之同代,源码级兼容成本为零;Gradle 从上游 wrapper 9.3.1 钉到 9.6.1(KStore / compose-shimmer 同款,2.4.20 线支持 Gradle 9.x),不像 2.2.21 旧线必须降 8.14.1。

settings.gradle.kts 把 eazytec 仓库写在最前:

pluginManagement {
    repositories {
        // HarmonyOS Kotlin toolchain must resolve first, otherwise ohosArm64()
        // fails with "Unresolved reference" at configuration time.
        maven("https://maven.eazytec-cloud.com/nexus/repository/maven-public/")
        maven("https://maven.eazytec-cloud.com/nexus/content/groups/harmonyos")
        gradlePluginPortal()
        google()
        mavenCentral()
    }
}

rootProject.name = "ohos_kore"

include(":kore")
include(":example:nativeApp")
2.3 编译真实库

kore/build.gradle.kts 收敛为双线 KMP 模块:

plugins {
    alias(libs.plugins.kotlin.multiplatform)
    alias(libs.plugins.kotlin.compose.compiler)   // 社区线的 Compose 编译器插件,jvm/ohos 共用
}

kotlin {
    applyDefaultHierarchyTemplate()

    jvm { compilerOptions { jvmTarget.set(JvmTarget.JVM_21) } }   // 交叉验证目标

    ohosArm64()    // the adaptation
    ohosX64()

    sourceSets {
        ohosMain.dependencies {
            api(libs.compose.runtime)          // 1.12.0-0.1.0-07(社区线)
            api(libs.compose.foundation)
        }
        jvmMain.dependencies {
            api(libs.compose.runtime.jvm)      // 1.10.0(Maven Central,上游同版)
            api(libs.compose.foundation.jvm)
        }
    }
}

实测(Windows / JDK 21 / Gradle 9.6.1):

./gradlew :kore:compileKotlinJvm                 # BUILD SUCCESSFUL(整库交叉验证)
./gradlew :kore:compileKotlinOhosArm64 :kore:compileKotlinOhosX64   # BUILD SUCCESSFUL

25 个组件、两套色板、字阶、平滑圆角、涟漪的全部 commonMain 文件一个没动,只有字体接缝三件套(expect + 两个 actual)和归档体是新的;唯一告警是上游 Dialog.kt 预存在的 “Expression is unused”,与适配无关。

2.4 测试与告警的处置

上游仓库未携带 commonTest 测试套件(模板残留的 androidHostTest / androidDeviceTest 不在编译链上),因此交叉验证是编译级的::kore:compileKotlinJvm 让整库逻辑对着上游制品线(1.10.0)重新过了一遍类型与语义检查,JVM 侧编译通过等价于"令牌内核没有漂移"的静态证明。Kotlin/Native 的 ohos 测试 runner 不可用,根构建沿用社区纪律全局禁用:

subprojects {
    tasks.matching { it.name.contains("Ohos", ignoreCase = true) && it.name.contains("Test") }
        .configureEach { enabled = false }
}

第 3 阶段:桥协议建模

3.1 JSON 契约
com.dev.kore/**(上游令牌系统 + 25 组件,原样编译)
        ↓ KoreDefaults / RadixColors / TailwindColors / KoreTypography / SmoothCorner
KoreHeadless.ohos.kt(无头门面,读活对象出纯 Kotlin 数据)
        ↓
Bridge.kt(@CName × 2,扁平 JSON 进出)
        ↓ UTF-8 JSON
N-API koreVersion() / koreDispatch()
        ↓
ArkUI Index.ets(四 Tab)

请求与响应(值全部为字符串;颜色 #AARRGGBB 大写):

请求 : {"op":"scheme","mode":"light"}
       {"op":"radix","scale":"blue","mode":"dark"}
       {"op":"tailwind","scale":"slate"}
       {"op":"typography"}
       {"op":"tokens"}
       {"op":"shape","width":"300","height":"120","radius":"24","smoothing":"100"}
       {"op":"fonts"} / {"op":"radixNames"} / {"op":"tailwindNames"} / {"op":"ripple"} / {"op":"version"}
响应 : {"ok":"true","op":"scheme","background":"#FF1C2024",…23 槽…}
       {"ok":"true","op":"radix","count":"12","step1":"#FFFBFCFD",…}
       {"ok":"true","op":"shape","count":"13","cmd0":"M 0.00 48.00","cmd1":"C 0.00 25.37 …",…}
       {"ok":"false","error":"…"}

一个不对称设计:除 shape 的路径命令内含空格外,全部键值是扁平字符串映射——MiniJson(自 cmp_qrose 审核桥移植的引号感知解析器)不需要理解嵌套,ArkTS 侧 JSON.parse 一次拿到 Record<string, string>,两端都没有双重转义。

3.2 令牌载荷为什么读活对象

门面不做数值搬运工,每个值运行时从库对象里取:

// kore/src/ohosMain/kotlin/com/dev/kore/KoreHeadless.ohos.kt
fun colorScheme(dark: Boolean): Map<String, Long> {
    val scheme = if (dark) KoreDefaults.defaultDarkColorScheme
                 else KoreDefaults.defaultLightColorScheme
    return linkedMapOf(
        "background" to scheme.background.toArgbLong(),
        "onBackGround" to scheme.onBackGround.toArgbLong(),
        /* …23 槽… */
    )
}

字阶读 KoreTypography().display1.fontSize.value 等构造默认值,尺寸读 KoreDefaults.defaultSizes,色板枚举 RadixColors.Gray.light.step1…——上游改一个色值,demo 立刻跟着变,令牌与 Demo 之间不存在第二份拷贝。平滑圆角走得更远:smoothCornerOutline(w, h, r, smoothing) 逐句转录 AbsoluteSmoothCornerShape.createOutline 的三分支路径构建(radius=0 矩形 / smoothing=0 圆角 / 平滑角),复用模块内 internal SmoothCorner 的连续曲率数学,输出 M/L/C/Z 命令串:

width=300 height=120 radius=24 smoothing=100 →
M 0.00 48.00
C 0.00 25.37 0.00 14.06 7.03 7.03
C 14.06 0.00 25.37 0.00 48.00 0.00
L 252.00 0.00 …(topStart→topEnd→bottomEnd→bottomStart 对称四角)… Z

ArkUI Canvas 拿到命令串做 moveTo / lineTo / bezierCurveTo / closePath 重放——屏幕上那个"方圆",就是 Kotlin 侧 SmoothCorner 数学算出来的同一个轮廓。

3.3 错误通道

每个导出整体 try/catch,Kotlin 异常永远不跨 FFI:

@CName("KoreDispatch")
fun koreDispatch(request: String): String {
    return try {
        val json = MiniJson.parse(request)
        when (json.str("op")) {
            "version" -> versionJson()
            "fonts" -> fontsResponse()
            /* …11 op… */
            else -> err(json.str("op").ifBlank { "(missing)" }, "unsupported op")
        }
    } catch (t: Throwable) {
        err("dispatch", t.message ?: t.toString())
    }
}

非法 JSON、缺 op / scale / mode、不支持的 op、越界参数,全部走同一个 ok:false 通道,ArkTS 侧统一渲染。

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

4.1 三层桥接
ArkTS
  │ JSON string
  ▼
C++ N-API entry(libkore_napi.so)
  │ const char*
  ▼
Kotlin/Native C ABI(libohoskore.so)
  │ KoreDispatch / KoreVersion
  ▼
UTF-8 JSON + DisposeString

直接导出 Kotlin 对象会暴露 ABI 和生命周期问题;只导出整数又会让页面手抄令牌,所以选择 C ABI + JSON。

4.2 Kotlin/Native 导出函数
@CName("KoreDispatch")
fun koreDispatch(request: String): String   // 11 op 一口进

@CName("KoreVersion")
fun koreVersion(): String                   // 版本握手
C ABI 符号ArkTS 方法覆盖内核
KoreDispatchkoreDispatch(req)23 槽主题 / 33+22 色板 / 15 级字阶 / 尺寸档 / 平滑圆角几何 / 字族表 / 涟漪默认
KoreVersionkoreVersion()fork 版本握手(真机版本徽章的数据来源)
4.3 C++ N-API 方法分发
napi_property_descriptor desc[] = {
    {"koreDispatch", nullptr, NapiKoreDispatch, nullptr, nullptr, nullptr,
     static_cast<napi_property_attributes>(napi_default | napi_enumerable), nullptr},
    {"koreVersion", nullptr, NapiKoreVersion, nullptr, nullptr, nullptr,
     static_cast<napi_property_attributes>(napi_default | napi_enumerable), nullptr},
};

每个回调统一三步:napi_typeof 校验必须是 string、OH_LOG_INFO 打字节数、调 KN 函数并包装返回。KN 返回的字符串必须经 libohoskore_symbols()->DisposeString 归还 Kotlin 运行时,绝不 free()。libohoskore_api.h 按社区已审仓库同款手写(只声明 ExportedSymbols 结构与两个业务符号),不拖整份生成头。

4.4 CMake 和类型声明
add_library(ohoskore SHARED IMPORTED)
set_target_properties(ohoskore PROPERTIES
  IMPORTED_LOCATION ${KN_SO}
  IMPORTED_NO_SONAME TRUE)

add_library(kore_napi SHARED napi_init.cpp)
target_link_libraries(kore_napi PUBLIC
  libace_napi.z.so
  libhilog_ndk.z.so
  ohoskore)

OHOS_ARCH 为空或 libohoskore.so 缺失直接 FATAL_ERROR(提示先跑 link 任务);$ORIGIN rpath 加 POST_BUILD 拷贝保证 NAPI so、KN so、libc++ 三件套同目录,dlopen 时互相可见。

export const koreVersion: () => string;
export const koreDispatch: (request: string) => string;

第 5 阶段:ArkUI 宿主

5.1 版本握手和字体注册
aboutToAppear(): void {
  this.versionInfo = JSON.parse(koreVersion()) as Record<string, string>;   // 真实 Native 握手
  this.registerFonts();    // fonts op → font.registerFont × 9 字重
  /* 拉取 scheme / typography / tokens … */
}

private registerFonts(): void {
  const res = this.dispatch({ op: 'fonts' });
  for (let i = 0; i < Number(res.count); i++) {
    const weight = res[`weight${i}`] ?? '';
    const file = res[`file${i}`] ?? '';
    font.registerFont({ familyName: `GoogleSansFlex_${weight}`,
                        familySrc: `/rawfile/font/${file}` });   // try/catch 容错
  }
}

9 个 ttf 从上游 composeResources/font/ 原样拷入 entry/src/main/resources/rawfile/font/——Kotlin 侧 googleSansFlex() 的资源体归档了,字体资产换通道继续服役,Tab 3 的 15 级字阶全部用对应字重的真字体渲染。

5.2 页面交互

四 Tab 自上而下:

  • 主题:明 / 暗 chips 触发 {"op":"scheme","mode":…},23 槽色卡(键名 + hex)三列 Flex 排布;下方用 primary / onPrimary / complementary / disabled / success / error 与 radius.md 组装"主按钮 + 卡片 + 状态圆点"——一个完全由令牌驱动的组件演示;再往下是涟漪默认值表与版本握手表;
  • 调色板:Radix 33 阶名横向 chips(选中态用 primary/surface 令牌着色)+ 明 / 暗切换 → 12 步色带,每步带步号、hex 与语义注释(1–2 背景 / 3–5 组件 / 6–8 边框 / 9–10 实底 / 11–12 文本);Tailwind 22 阶 chips → 11 步色带;
  • 字体:15 级字阶逐级渲染"Kore display1 57sp"式样本,字号 / 字重 / 行高全部来自 typography op 的 <样式>.size 等键;底部 size.xxs…xl 条形图与 radius.xs…xl 圆角预览盒;
  • 平滑圆角:radius(0–60)/ smoothing(0–100)双滑条 onChange 组 shape 请求,Canvas 重放命令串填充 primary 色;并列一个 radius=16 / smoothing=60 的默认轮廓描边对照,旁注"轮廓由 Kotlin SmoothCorner 数学实时生成"。
5.3 加载态和错误边界

令牌未就绪时显示加载页,杜绝闪白;ok:false 时错误原文进状态行;registerFont 逐个 try/catch(个别字重注册失败只降级该字重显示,不阻断页面);数字解析全部带容错兜底。主操作按钮用 primary 实底白字、语义与对比度对齐库自身的无障碍意图。

第 6 阶段:示例与验证

6.1 工程结构
example/
├── nativeApp/
│   ├── build.gradle.kts                      双 ABI sharedLib(baseName=ohoskore)+ cxxShared + copyKnSo*
│   └── src/ohosMain/kotlin/kore/nativeapp/
│       └── Bridge.kt                         @CName × 2 + MiniJson(扁平 JSON,继承自 cmp_qrose 审核模式)
└── harmonyApp/
    ├── build-profile.json5                   signingConfigs 留空(签名由 DevEco 自动生成回填)
    ├── AppScope/app.json5                    bundleName org.terminator.ohos.kore、versionName 1.0.0-alpha03-ohos.1
    └── entry/
        ├── build-profile.json5               externalNativeOptions + abiFilters [arm64-v8a, x86_64]
        ├── libs/{arm64-v8a,x86_64}/          linkDebugShared* 经 copyKnSo* 回填的三件套(gitignore)
        ├── src/main/cpp/CMakeLists.txt       IMPORTED 引入 KN so、$ORIGIN rpath
        ├── src/main/cpp/napi_init.cpp        N-API 导出、参数检查、DisposeString
        ├── src/main/cpp/include/libohoskore_api.h
        ├── src/main/cpp/types/libkore_napi/{index.d.ts, oh-package.json5}
        ├── src/main/resources/rawfile/font/  9 个 Google Sans Flex ttf(4.9 MB)
        └── src/main/ets/pages/Index.ets      ArkUI 四 Tab 验收页面
6.2 构建、签名和安装
./gradlew :kore:compileKotlinJvm                                  # 整库交叉验证
./gradlew :example:nativeApp:linkDebugSharedOhosArm64 :example:nativeApp:linkDebugSharedOhosX64

cd example/harmonyApp
hvigorw assembleHap -p product=default -p buildMode=debug --no-daemon

签名在 DevEco Studio 的 File → Project Structure → Signing Configs 中勾选自动生成,证书写入本机 ~/.ohos/config,不进入仓库(仓库内 build-profile.json5 的 signingConfigs 保持空数组,勾选自动签名后由 IDE 回填带 material 的完整配置)。

hdc list targets -v
hdc -t <设备序列号> install -r \
  entry/build/default/outputs/default/entry-default-signed.hap
hdc -t <设备序列号> shell aa start \
  -a EntryAbility -b org.terminator.ohos.kore -m entry

四、完整代码对照

4.1 整体架构

com.dev.kore.components/**(上游 25 组件,原样编译)
    │ Composable 本体(鸿蒙无宿主,不上屏,随 .so 待命)
com.dev.kore.themes/**(上游令牌系统,原样编译)
    │ KoreColorScheme ×23 / RadixColors ×33 / TailwindColors ×22 / KoreTypography ×15 / SmoothCorner
KoreHeadless.ohos.kt(无头门面)
    │ 读活对象 → 纯 Kotlin 数据(Map / List / 命令串)
Bridge.kt + MiniJson
    │ KoreDispatch / KoreVersion
libkore_napi.so
    │ koreDispatch / koreVersion
Index.ets
    │ JSON parse + @State 更新 + Canvas 重放 + font.registerFont
    ▼
ArkUI 真机页面(四 Tab)

4.2 文件清单

文件职责
settings.gradle.kts极简 ohos 构建、eazytec Maven 置前、ohos_kore、上游 demo 模块移出
gradle/libs.versions.tomlKotlin 2.4.20-0.1.0-07 + Compose 双线钉版(ohos 1.12.0-0.1.0-07 / jvm 1.10.0)
build.gradle.kts(根)版本 1.0.0-alpha03-ohos.1、全局禁用 Ohos*Test* 任务
kore/build.gradle.kts双线 KMP:jvm + ohosArm64 + ohosX64,compose 依赖按 target 线声明
kore/src/composeMain/.../Typography.kt上游字体资源体归档(逐字节,含原始空格笔误)
kore/src/{jvm,ohos}Main/.../Typography.*.kt唯一接缝的两个 actual(FontFamily.Default)
kore/src/ohosMain/.../KoreHeadless.ohos.kt无头令牌门面(23 槽 / 色板 / 字阶 / SmoothCorner 几何)
example/nativeApp/build.gradle.kts双 ABI sharedLib、cxxShared 定位 libc++、copyKnSo* 回填
nativeApp/.../Bridge.kt2 个 C ABI、11 op 分发、MiniJson、错误通道
harmonyApp/.../cpp/napi_init.cppN-API 导出、参数检查、DisposeString
harmonyApp/.../cpp/CMakeLists.txtIMPORTED 引入 KN so、$ORIGIN rpath、POST_BUILD 三件套
harmonyApp/.../ets/pages/Index.etsArkUI 四 Tab 验收页面
README.OpenHarmony_CN.md鸿蒙适配主文档(21 节)

4.3 关键 API 对照

层次API作用
令牌KoreDefaults.defaultLight/DarkColorScheme23 槽语义色(桥 scheme op 数据源)
令牌RadixColors.<Name>.light/dark.step1…1233 阶 × 12 步(桥 radix op)
令牌TailwindColors.<Name>22 阶 × 11 步(桥 tailwind op)
令牌KoreTypography() 默认值15 级字阶 weight/size/lineHeight/letterSpacing(桥 typography op)
令牌KoreDefaults.defaultSizes / defaultShapes尺寸与圆角档位(桥 tokens op)
几何SmoothCorner(internal)+ createOutline 转录连续曲率方圆轮廓 → M/L/C/Z 命令串(桥 shape op)
主题koreRipple(...) 公共参数涟漪默认值(桥 ripple op)
NativeKoreDispatch / KoreVersionC ABI JSON 导出
N-APIkoreDispatch(req) / koreVersion()向 ArkTS 暴露能力
ArkTSdispatch(op) / registerFonts()触发查询并刷新 UI / 注册 9 字重

4.4 ArkTS 与 Kotlin 的边界

const scheme = JSON.parse(koreDispatch(JSON.stringify({ op: 'scheme', mode: 'dark' })));
// scheme.primary === '#FF3E63DD' —— 来自 KoreDefaults.defaultDarkColorScheme.primary
this.primaryBtnColor = hexColor(scheme.primary);   // '#AARRGGBB' → 0xAARRGGBB

const shape = JSON.parse(koreDispatch(JSON.stringify(
  { op: 'shape', width: '300', height: '120', radius: '24', smoothing: '100' })));
// shape.cmd0 === 'M 0.00 48.00' —— Kotlin SmoothCorner 数学算出的轮廓
fun colorScheme(dark: Boolean): Map<String, Long> {
    val scheme = if (dark) KoreDefaults.defaultDarkColorScheme
                 else KoreDefaults.defaultLightColorScheme
    return linkedMapOf("background" to scheme.background.toArgbLong(), /* … */)
}

两者之间只传输扁平 JSON 字符串,不传输 Kotlin 对象或未校验的动态结构。

五、关键决策说明

决策 1:真编译整库而不是提取"纯令牌内核"

qrose 抽编码器内核,是因为它的编码器恰好是纯 Kotlin。Kore 的令牌系统站在 Color / TextStyle / Dp / Shape 这些 Compose 类型上,抽"纯内核"等于把 1049 行 Radix 色板、字阶、圆角数学手抄成第二份数据——漂移风险比编译风险大得多。社区 Compose 线既然发布了 ohos klib,正确答案就是整库原样编译:Color(0xFF1C2024) 还是上游那一个 Color,只是编译器换了线。

决策 2:工具链升到 Kotlin 2.4.20-0.1.0-07,Gradle 钉 9.6.1

社区 Compose 线 1.12.0-0.1.0-07 基于 Kotlin 2.4.20 构建,旧钉法 2.2.21 读不了它的 klib 元数据;上游源码本来就是 Kotlin 2.3.21,2.4.20 同代零降级。Gradle 方面 2.4.20 线支持 9.x(KStore / compose-shimmer 先例),上游 wrapper 9.3.1 钉到 9.6.1 对齐社区验证过的组合——只有 2.2.21 旧线才必须降 8.14.1,两条工具链线的经验不能互相套用。

决策 3:无头令牌门面而不是 ArkTS 手抄令牌

门面 KoreHeadless.ohos.kt 放在库模块的 src/ohosMain(不是桥模块):一来同模块才能引用 internal SmoothCorner 数学(跨模块 internal 不可见);二来它读的是活对象——上游改一个色值、调一档字号,.so 重编后 demo 自动跟上,ArkTS 侧永远拿最新令牌。手抄令牌则会在上游第一次发版时就变成"长得像 Kore 的另一个设计系统"。

决策 4:字体走 expect/actual 接缝 + rawfile 通道

鸿蒙线上没有 compose-resources 的 Res 生成机器,硬扛会把资源插件的不确定性引进整个构建。接缝化之后:上游体逐字节归档 composeMain(社区 Compose 宿主出现时可原样接回),ohos/jvm actual 返回 FontFamily.Default(字阶的 15 组数值与字族无关,门面照常吐),9 个 ttf 原文件随 demo rawfile 分发、font.registerFont 注册——字体资产零丢失,接缝面最小(全库唯一 expect)。

决策 5:jvm 编译级交叉验证 + “换线不换版”

上游没带测试套件,交叉验证落在 :kore:compileKotlinJvm:整库对着上游制品线(Compose 1.10.0,与上游 1.0.0-alpha03 完全同版)重新编译通过,等价于令牌逻辑静态没漂移。ohos 线用 1.12.0-0.1.0-07 只是"同一 API 空间的更新线",版本主体规则与 KStore 的 kotlinx 制品线完全一致。

六、测试与验证

6.1 测试环境

本次验证使用 Windows(x86_64)、JDK 21(Temurin)、Gradle 9.6.1、DevEco Studio 5.1.0 Release、OpenHarmony 命令行工具(hvigor / hdc)与鸿蒙真机(arm64)。

6.2 JVM 交叉验证

./gradlew :kore:compileKotlinJvm

整库(44 文件:25 组件 + 主题系统)对上游制品线编译通过,唯一告警为上游 Dialog.kt:129 预存在的 “Expression is unused”——上游自带的,与适配无关。

6.3 产物验证

:kore:compileKotlinOhosArm64 / OhosX64: BUILD SUCCESSFUL
linkDebugSharedOhosArm64 / linkDebugSharedOhosX64: BUILD SUCCESSFUL
copyKnSoArm64 / copyKnSoX64: 三件套自动落位 example/harmonyApp/entry/libs/<abi>/

实测产物(2026-10-17,debugShared 未 strip,含静态链接的 Compose runtime):

产物arm64-v8ax86_64
libohoskore.so27.8 MB(29,136,104 B)26.6 MB(27,894,072 B)
libc++_shared.so≈1.8 MB≈1.7 MB

.so 体积大是路线的诚实代价:整库 Compose runtime 静态链接进 KN 动态库(compose-shimmer 同款 30 MB 级),换来的是"上游 commonMain 在鸿蒙 KLIB 层完全可用"这条最强声明。

6.4 功能用例

  1. 启动后标题区出现版本徽章 1.0.0-alpha03-ohos.1 与技术栈行(来自 koreVersion() 真实 Native 调用,非静态文字);
  2. 主题 Tab 明 / 暗切换,23 槽语义色卡即时换色,hex 与 Radix 色板步值一致(如 dark.primary = Blue.dark.step9);
  3. 令牌驱动的"主按钮 + 卡片 + 状态圆点"随主题联动换色,圆角取 radius.md;
  4. 调色板 Tab 任选 Radix 色阶 → 12 步色带带步语义注释;Tailwind 色阶 → 11 步色带;
  5. 字体 Tab 15 级字阶以对应字重的 Google Sans Flex 真字体渲染(thin…black 9 档可辨),字号 / 行高来自桥值;
  6. 平滑圆角 Tab 拖动 radius / smoothing 滑条,Canvas 轮廓逐帧重绘(shape op 实时重算),smoothing=0 退化为圆角、radius=0 退化为矩形;
  7. 杀掉应用重启,版本握手与各 Tab 令牌渲染如常(令牌常驻 .so,无状态依赖);
  8. 全程 hilog | grep KoreNapi 可见调用日志。

6.5 验证结论

令牌系统、Compose 编译链、Native 动态库、C ABI、N-API、ArkUI 页面和 HAP 已完成链路验证。应用在鸿蒙真机上成功安装并进入前台,四 Tab 展示了真实的设计令牌会话:版本握手、明暗主题、两套色板、真字体字阶与实时平滑圆角(见下节效果图)。

七、运行效果

7.1 真机截图

在这里插入图片描述

7.2 命令速查

git clone https://atomgit.com/oh-tpc/Kore.git
cd Kore
export JAVA_HOME="/path/to/jdk-21"
./gradlew :kore:compileKotlinJvm                                      # 整库交叉验证
./gradlew :kore:compileKotlinOhosArm64 :kore:compileKotlinOhosX64    # klib
./gradlew :example:nativeApp:linkDebugSharedOhosArm64 :example:nativeApp:linkDebugSharedOhosX64
cd example/harmonyApp
hvigorw assembleHap -p product=default -p buildMode=debug --no-daemon
hdc install -r entry/build/default/outputs/default/entry-default-signed.hap
hdc shell aa start -a EntryAbility -b org.terminator.ohos.kore -m entry
hdc shell hilog | grep KoreNapi

八、遗留问题与改进方向

8.1 踩坑复盘

  1. 只改 ArkTS 页面不算 KMP 适配,必须让 :kore 真正编译成 ohosArm64 / ohosX64 并链出动态库;
  2. settings.gradle.kts 里 eazytec 双仓必须排在 pluginManagement 与 dependencyResolutionManagement 最前,否则配置期直接 Unresolved reference: ohosArm64;
  3. 社区 Compose 线不发布 *-desktop 子制品:jvm 交叉验证线必须单独声明在 jvmMain(Maven Central 1.10.0),ohos 线声明在 ohosMain(1.12.0-0.1.0-07)——两线共存,commonMain 保持 line-agnostic;
  4. compose-resources 的 Res 生成机器不在鸿蒙线上:上游 Typography.kt 里 Res.font.* 直接编不过,必须接缝化(expect/actual + 归档),别试图把资源插件引进来;
  5. 上游代码连笔误都要原样归档:Res.font. google_sans_flex_extra_light(点后带空格,Kotlin 合法)在 composeMain 归档体里逐字节保留,diff 才干净;
  6. internal 可见性按模块算:桥模块碰不到 :kore 的 internal SmoothCorner,无头门面必须写在库模块 src/ohosMain,桥只调 public API;
  7. androidx.graphics:graphics-shapes 在上游目录里声明但零 import——死依赖移除,少一个 ohos 解析变数;
  8. 新 SDK 的 Scroll 单参构造只收 Scroller:Scroll({ scrollBar: BarState.Off }) 编译报错,改 Scroll() + .scrollBar(BarState.Off) 链式属性;
  9. Circle().fill("#hex 字符串") 命中 SDK 26 才有的字符串重载(兼容 5.1.0(18) 时告警),改走 fill(0xAARRGGBB number) 数字路径;
  10. font.registerFont 有 deprecation 告警但功能正常,@ohos.font 无明确等价替代,保留并容忍告警;
  11. HAP 缺当前 ABI 的 so 会报安装失败 9568347,abiFilters 必须双 ABI,每个 ABI 三件套(NAPI so / KN so / libc++)缺一不可——模拟器是 x86_64;
  12. KN 返回字符串必须 DisposeString 归还运行时,N-API 层不能 free();
  13. build-profile.json5 的 signingConfigs 仓库内留空数组且 product 不引用完整签名,勾选 DevEco 自动签名后由 IDE 回填带 material 的配置,证书绝不进 AtomGit;
  14. 根 .gitignore 的 **/build/ 是根锚定模式,DevEco 生成的 entry/libs、.hvigor、.cxx 要单独追加规则;oh-package-lock.json5 按社区先例提交入库(可复现构建),别顺手 ignore。

8.2 已知问题

  • 鸿蒙没有 Compose composition host:25 个 Composable 组件编译进 .so 但不上屏,ArkTS Demo 是令牌级复刻而非组件级复刻;社区宿主(CPF-KMP-CMP)出现后可整体切换;
  • googleSansFlex() 的 ohos/jvm actual 返回 FontFamily.Default:字阶数值不受影响(门面从 KoreTypography() 默认值读取),但 Compose 侧组合该字族时得到系统默认字体;
  • 桥是同步一问一答:koreRipple 的动画节点、组件的有状态交互(如 dropdown 展开)不跨 C ABI,涟漪只暴露默认参数;
  • tokens 的 dp 值按 density=1 换算成 px,ArkTS 侧需要自行乘屏幕密度;
  • android / js / wasmJs / apple 目标剥离(源文件留在树里),vanniktech 发布剥离,本 fork 不发 Maven Central;
  • HAP 安装必须使用与 bundle name org.terminator.ohos.kore 匹配的签名。

8.3 未来优化方向

  • 社区 Compose 宿主落地后:composeMain 归档的字体资源体原样接回,demo 从"令牌级复刻"升级为"真实 Composable";
  • 补充组件级桥接入口(按钮 / 开关 / 滑条的尺寸与状态色打包 op),让 ArkTS 复刻更省调用;
  • 评估 .har 形态发布,让 ArkTS 工程直接依赖而不是走 so 三件套;
  • 在 CI 中加入 ohos 编译、ELF 符号检查(llvm-nm 验证 KoreDispatch / KoreVersion 为全局 T 符号)和 HAP 构建;
  • 补充模拟器(x86_64)与其他设备 ABI 的验收矩阵和自动化截图脚本。

九、总结

9.1 核心难点回顾

上游 kore(25 组件 + 令牌系统,全部 commonMain)
    → 确认"纯 UI 库"形态:无可拆纯 Kotlin 内核 → 整库编译路线(compose-shimmer 同款第二档)
    → 验证社区 Compose ohos 制品线(1.12.0-0.1.0-07)
    → 工具链对齐 Kotlin 2.4.20-0.1.0-07(Gradle 9.6.1)
    → 根构建替换(cmp_qrose 模式)+ 双线依赖隔离(ohos 社区线 / jvm 上游线)
    → 唯一接缝 googleSansFlex() expect/actual + composeMain 归档
    → 无头令牌门面 KoreHeadless(读活对象 + internal SmoothCorner 几何)
    → C ABI(@CName × 2)+ 11 op 扁平 JSON
    → C++ N-API(DisposeString 纪律)
    → ArkTS JSON 解析 + rawfile 字体注册 + Canvas 重放
    → ArkUI 真机四 Tab 验收(主题 / 色板 / 字阶 / 平滑圆角)

9.2 封装层次

  • 令牌层:上游原版 23 槽语义色、Radix / Tailwind 色板、15 级字阶、尺寸与圆角档、平滑圆角数学;
  • 组件层:上游 25 个 Composable 原样编译进 .so(鸿蒙无宿主,待命);
  • 门面层:KoreHeadless 把活对象提升为 UI-free 纯数据表面;
  • Native 层:生成双 ABI 动态库并通过 2 个 C ABI 输出 JSON;
  • N-API 层:完成参数检查、字符串转换和 Native 内存释放;
  • ArkTS 层:管理页面状态、JSON 解析、字体注册、Canvas 路径重放;
  • DevEco 层:完成 CMake、HAP、签名、安装和真机运行。

9.3 三条经验

  1. 纯 UI 库的内核就是令牌系统:组件树不能上屏不等于库没有可验证的内核——色板、字阶、几何这些"设计系统的数学"全部可编译、可跨 FFI、可断言。先盘点"库的哪部分是数据 / 数学",路线(内核提取 vs 整库编译 + 门面)自然浮现;
  2. 路线选择基于当天的制品可达性:社区 Compose klib 线出现之前,这类库只能归档等待;线出现之后,整库编译 + 无头门面就是最强声明(“换线不换版”,上游代码在鸿蒙 KLIB 层完全可用)。历史结论要重新验证,不能沿用;
  3. 真机截图是最终验收:编译、链接、符号表都是静态证明,只有真机上"每个颜色值都来自 libohoskore.so"的页面才能闭环——版本徽章做握手、四 Tab 逐项对照上游令牌,漂移无处藏身。

9.4 适配成果

当前适配已完成:

  • Kore 1.0.0-alpha03 真实设计系统的双 ABI Kotlin/Native 编译(上游 commonMain 44 文件零修改,唯一接缝为字体 expect/actual 三件套);
  • 社区 Compose 制品线在设计系统库上的完整落地(ohos 线 1.12.0-0.1.0-07 + jvm 验证线 1.10.0,整库静态链接进 KN so:arm64 27.8 MB / x86_64 26.6 MB);
  • 无头令牌门面(23 槽主题 / 33+22 色板 / 15 级字阶 / SmoothCorner 几何)与 2 个 C ABI 符号、C++ N-API 包装层;
  • DevEco Stage ArkUI 四 Tab 验收页面(主题 / 调色板 / 真字体字阶 / 实时平滑圆角、9 字重 rawfile 字体、版本握手);
  • JVM 整库编译交叉验证 + HAP 构建、签名、设备安装和真机验证(见效果图);
  • AtomGit 项目文档、OpenHarmony README(中英双份,中文主文档 21 节)和本地效果图。

适配过程 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。

参考文档

Logo

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

更多推荐