开源鸿蒙平台 CMP 三方库「Kore」适配全流程
本文记录
Kore接入 OpenHarmony 的完整过程,覆盖设计系统库盘点、社区 Compose 制品线选型、整库真实编译、ohosArm64/ohosX64目标、Kotlin/Native 动态库、C ABI/N-API 桥接、ArkUI 真机页面、HAP 构建和真机验收。
Kore是基于 Compose Multiplatform 的设计系统底座(io.github.dev778g-me:kore1.0.0-alpha03,MIT):25+ 预样式组件(buttons / card / dialog / dropdown / textFields / navigationBar / tabRow / accordion / stepper……)站在一套完整令牌系统上——KoreColorScheme23 个语义色槽(明 / 暗双主题)、RadixColors33 个命名色板 × 12 步、TailwindColors22 个色板 × 11 步、KoreTypography15 级字阶、KoreShapes/KoreSizes档位、AbsoluteSmoothCornerShape平滑圆角(连续曲率"方圆")与koreRipple涟漪。本次适配与社区两条既有路线都不同:qrose / haze 走"零依赖内核提取"(因为它们有纯 Kotlin 编码器可拆),KStore 走"kotlinx 制品线真编译"(因为存储引擎只依赖 kotlinx);而Kore是纯 Compose UI 库——每一行都站在androidx.compose类型上,没有可拆的纯 Kotlin 子集。社区 Maven 上已经出现带ohosArm64/ohosX64klib 的 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):
defaultSizesxxs…xl 与defaultShapesxs…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 Multiplatform | HarmonyOS Kotlin 2.4.20-0.1.0-07(eazytec Maven) |
| JDK | 21(org.gradle.java.home 指向本机 Temurin 21) |
| OpenHarmony 目标 | ohosArm64 + ohosX64(jvm() 保留作交叉验证) |
| Gradle | 9.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 product | 5.1.0(18) 兼容 |
| DevEco Studio | 5.1.0 Release |
| ABI | arm64-v8a + x86_64 |
| Bundle | org.terminator.ohos.kore |
export JAVA_HOME="/path/to/jdk-21" # Windows 下写入 gradle.properties 的 org.gradle.java.home
java -version
1.3 源码依赖度盘点
| 源码集 | 文件 | 依赖形态 | 处置 |
|---|---|---|---|
commonMain/components/**(25 目录) | 38 | compose foundation / ui / animation / runtime,零平台 API | 整体编译(jvm + 双 ohos) |
commonMain/themes/**(色板 / 字阶 / 形状 / 涟漪) | 8 | compose ui.graphics / text / unit + 纯 Kotlin 数学 | 整体编译 |
commonMain/.../typography/Typography.kt | 1 | compose-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 方法 | 覆盖内核 |
|---|---|---|
KoreDispatch | koreDispatch(req) | 23 槽主题 / 33+22 色板 / 15 级字阶 / 尺寸档 / 平滑圆角几何 / 字族表 / 涟漪默认 |
KoreVersion | koreVersion() | 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"式样本,字号 / 字重 / 行高全部来自
typographyop 的<样式>.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.toml | Kotlin 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.kt | 2 个 C ABI、11 op 分发、MiniJson、错误通道 |
harmonyApp/.../cpp/napi_init.cpp | N-API 导出、参数检查、DisposeString |
harmonyApp/.../cpp/CMakeLists.txt | IMPORTED 引入 KN so、$ORIGIN rpath、POST_BUILD 三件套 |
harmonyApp/.../ets/pages/Index.ets | ArkUI 四 Tab 验收页面 |
README.OpenHarmony_CN.md | 鸿蒙适配主文档(21 节) |
4.3 关键 API 对照
| 层次 | API | 作用 |
|---|---|---|
| 令牌 | KoreDefaults.defaultLight/DarkColorScheme | 23 槽语义色(桥 scheme op 数据源) |
| 令牌 | RadixColors.<Name>.light/dark.step1…12 | 33 阶 × 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) |
| Native | KoreDispatch / KoreVersion | C ABI JSON 导出 |
| N-API | koreDispatch(req) / koreVersion() | 向 ArkTS 暴露能力 |
| ArkTS | dispatch(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-v8a | x86_64 |
|---|---|---|
libohoskore.so | 27.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.0.0-alpha03-ohos.1与技术栈行(来自koreVersion()真实 Native 调用,非静态文字); - 主题 Tab 明 / 暗切换,23 槽语义色卡即时换色,hex 与 Radix 色板步值一致(如 dark.primary = Blue.dark.step9);
- 令牌驱动的"主按钮 + 卡片 + 状态圆点"随主题联动换色,圆角取
radius.md; - 调色板 Tab 任选 Radix 色阶 → 12 步色带带步语义注释;Tailwind 色阶 → 11 步色带;
- 字体 Tab 15 级字阶以对应字重的 Google Sans Flex 真字体渲染(thin…black 9 档可辨),字号 / 行高来自桥值;
- 平滑圆角 Tab 拖动 radius / smoothing 滑条,Canvas 轮廓逐帧重绘(
shapeop 实时重算),smoothing=0 退化为圆角、radius=0 退化为矩形; - 杀掉应用重启,版本握手与各 Tab 令牌渲染如常(令牌常驻
.so,无状态依赖); - 全程
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 踩坑复盘
- 只改 ArkTS 页面不算 KMP 适配,必须让
:kore真正编译成ohosArm64/ohosX64并链出动态库; settings.gradle.kts里 eazytec 双仓必须排在pluginManagement与dependencyResolutionManagement最前,否则配置期直接Unresolved reference: ohosArm64;- 社区 Compose 线不发布
*-desktop子制品:jvm 交叉验证线必须单独声明在jvmMain(Maven Central 1.10.0),ohos 线声明在ohosMain(1.12.0-0.1.0-07)——两线共存,commonMain保持 line-agnostic; - compose-resources 的
Res生成机器不在鸿蒙线上:上游Typography.kt里Res.font.*直接编不过,必须接缝化(expect/actual + 归档),别试图把资源插件引进来; - 上游代码连笔误都要原样归档:
Res.font. google_sans_flex_extra_light(点后带空格,Kotlin 合法)在composeMain归档体里逐字节保留,diff 才干净; internal可见性按模块算:桥模块碰不到:kore的 internalSmoothCorner,无头门面必须写在库模块src/ohosMain,桥只调 public API;androidx.graphics:graphics-shapes在上游目录里声明但零 import——死依赖移除,少一个 ohos 解析变数;- 新 SDK 的
Scroll单参构造只收Scroller:Scroll({ scrollBar: BarState.Off })编译报错,改Scroll()+.scrollBar(BarState.Off)链式属性; Circle().fill("#hex 字符串")命中 SDK 26 才有的字符串重载(兼容 5.1.0(18) 时告警),改走fill(0xAARRGGBB number)数字路径;font.registerFont有 deprecation 告警但功能正常,@ohos.font无明确等价替代,保留并容忍告警;- HAP 缺当前 ABI 的 so 会报安装失败
9568347,abiFilters必须双 ABI,每个 ABI 三件套(NAPI so / KN so / libc++)缺一不可——模拟器是 x86_64; - KN 返回字符串必须
DisposeString归还运行时,N-API 层不能free(); build-profile.json5的signingConfigs仓库内留空数组且 product 不引用完整签名,勾选 DevEco 自动签名后由 IDE 回填带material的配置,证书绝不进 AtomGit;- 根
.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 三条经验
- 纯 UI 库的内核就是令牌系统:组件树不能上屏不等于库没有可验证的内核——色板、字阶、几何这些"设计系统的数学"全部可编译、可跨 FFI、可断言。先盘点"库的哪部分是数据 / 数学",路线(内核提取 vs 整库编译 + 门面)自然浮现;
- 路线选择基于当天的制品可达性:社区 Compose klib 线出现之前,这类库只能归档等待;线出现之后,整库编译 + 无头门面就是最强声明(“换线不换版”,上游代码在鸿蒙 KLIB 层完全可用)。历史结论要重新验证,不能沿用;
- 真机截图是最终验收:编译、链接、符号表都是静态证明,只有真机上"每个颜色值都来自
libohoskore.so"的页面才能闭环——版本徽章做握手、四 Tab 逐项对照上游令牌,漂移无处藏身。
9.4 适配成果
当前适配已完成:
- Kore
1.0.0-alpha03真实设计系统的双 ABI Kotlin/Native 编译(上游commonMain44 文件零修改,唯一接缝为字体 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。
参考文档
更多推荐

所有评论(0)