开源鸿蒙平台 CMP 三方库「Paletteon」适配全流程
本文记录
paletteon接入 OpenHarmony 的完整过程,覆盖动态取色库盘点、鸿蒙 Compose 制品线选型、colormath 无 ohos 制品的处置、真实引擎整库编译、ohosArm64/ohosX64目标、Kotlin/Native 动态库、C ABI/N-API 桥接、ArkUI 真机页面、HAP 构建和真机验收。
paletteon是基于 Compose Multiplatform 的动态取色主题库:给定一颗种子色,通过内置的 Google material-color-utilities Kotlin 移植(HCT 色彩科学、动态色、色调面板、量化器、评分器)生成完整的 Material 3ColorScheme;PaletteonDynamicTheme一个组合函数即可挂接主题,支持 9 种配色风格、4 档对比度、AMOLED 纯黑、扩展保真色集与颜色过渡动画。本次适配与社区早期路线(qrose / haze / PreCompose / voyager 的"零依赖内核提取")有本质区别:社区 Maven 上不仅有完整的 HarmonyOS kotlinx 制品线,社区 Compose 线(1.12.0-0.1.0-07)连 animation 构件都发布了ohosArm64/ohosX64klib,因此上游三个库模块的全部 commonMain 源文件——material-color-utilities(39 文件)、paletteon-core(27 文件)、paletteon-icons(25 文件)——零修改编译成 OpenHarmony KLIB;唯一的缺口 colormath(上游 implementation 依赖)没有任何仓库发布 ohos 变体,本 fork 把官方 3.6.1-sources.jar的 45 个纯 commonMain 文件逐字节内置为:colormathGradle 模块,上游源码依然一行不改。一个"种子色进、35 角色主题出"的完整取色引擎运行在真机上,ArkUI 四页签页面作为验收宿主。项目地址: AtomGit/oh-tpc/paletteon.git
上游仓库: teogor/paletteon
开发工具: 华为云码道
环境安装: 环境安装指南
一、背景
1.1 为什么做 OpenHarmony 平台 KMP/CMP 适配
paletteon 为 Compose Multiplatform 应用提供"种子色 ↔ 完整主题"这一件事的完整答案:paletteonColorScheme(seedColor, isDark, isAmoled, style, contrast, isExtendedFidelity) 一行生成 35 角色的 Material 3 ColorScheme,PaletteonDynamicTheme 组合函数直接挂接,configurePaletteonTheme DSL 深度可配置,ktx 扩展补齐 lighten / darken / isDisliked / fixIfDisliked / Blend.harmonize 等色彩科学工具,paletteon-icons 附带 21 个矢量图标。KMP 项目把这些能力放在 commonMain,由 Android、iOS、桌面、Web 共享。OpenHarmony 应用同样需要动态取色能力,但上游构建(Kotlin 2.2.20 / Gradle 8.14.3 / AGP 8.11.2 / 新版 KMP android 插件)不会生成 OpenHarmony 变体。
如果只把示例页面改成 ArkTS,页面可以画出色块,却无法证明取色引擎(HCT 求解器、九种 DynamicScheme、MaterialDynamicColors 角色推导、色彩调和、反感色修复、colormath 饱和度匹配)在 OpenHarmony ARM64 设备上完成编译、链接和执行。适配需要同时验证取色引擎、Compose 编译产物、Native 动态库、C ABI、N-API、ArkUI 页面和 HAP 交付链路。
| 障碍 | 具体问题 |
|---|---|
| 目标缺失 | 必须增加 ohosArm64() / ohosX64() 才能生成 OpenHarmony KLIB。 |
| 工具链不一致 | 上游 Kotlin 2.2.20 / Gradle 8.14.3 / AGP 8.11.2(com.android.kotlin.multiplatform.library)与鸿蒙工具链无法共存于一个 Gradle 构建。 |
| 依赖矩阵 | 引擎依赖 Compose runtime / ui / foundation / animation / material3 五件套,每一个都要有 ohos 变体才能解析;com.github.ajalt.colormath:colormath:3.6.1 全网无 ohos 制品。 |
| material3 版本陷阱 | JB material3 与 Compose 主线分别版本化:compose 1.9.1 存在、material3 1.9.1 不存在(实际是 1.9.0)。 |
| 语言边界不同 | ArkTS 不能直接持有 Kotlin 对象,需要 C ABI、C++ N-API 和 JSON。 |
| 组合能力跨不了桥 | PaletteonDynamicTheme / animateColorAsState 是 Compose 运行时能力,社区 Compose 线尚无鸿蒙组合宿主。 |
| 交付链路复杂 | .so、CMake、N-API、HAP、签名和 hdc 都要单独验收。 |
因此,本项目把边界放在:上游三模块与内置 colormath 的 ohos 编译、根构建替换(cmp_qrose 模式)和 C ABI/N-API 桥接层。取色语义完全由上游原版实现负责,ArkTS 只负责调用 Native、解析 JSON 和展示结果。
1.2 库提供的能力
Demo 提供一个真实的动态取色会话,对应桥接的全部入口:
- 配色方案(scheme):选一颗种子色 + 风格 + 对比度 + 深色/AMOLED/扩展保真开关,经真实的
paletteonColorScheme()(PaletteonDynamicTheme内部调用的同一个引擎函数)生成 35 个 ColorScheme 角色并整表展示,附一张由这些角色驱动的 ArkUI 主题预览卡; - 色调面板(palettes):读取真实
DynamicScheme的六条 TonalPalette(primary / secondary / tertiary / neutral / neutralVariant / error),13 档色调(0/10/…/95/99/100)由 HCT 色调求解器实时计算; - 颜色实验(hct + color):任意 hex 输入的 HCT 三分量分解、
withTone色阶、亮度、反感色判定,以及lighten/darken/fixIfDisliked/Blend.harmonize(含matchSaturation分支——该分支正是内置 colormath 的 HSL 转换路径); - 版本握手(version):
paletteonVersion()返回 fork 版本号,来自真实 Native 调用而非静态文字。
响应包含:
| 字段 | 作用 |
|---|---|
ok | 操作是否成功,失败时 error 带原文。 |
op | 回显本次操作。 |
roles / palettes / tones | scheme 返回 35 个角色;palettes 返回六条色板各 13 档色调。 |
variant / sourceColorHex | 真实 DynamicScheme.variant 与源色,证明风格参数映射到了正确的 Scheme 实现。 |
引擎侧完整能力(九种 Scheme、MaterialDynamicColors 角色推导、扩展保真色集、AMOLED 纯黑、DislikeAnalyzer、TemperatureCache、QuantizerCeleci、Score)全部在上游 commonMain 里原样随编译进入 .so。
1.3 实现适配
| 维度 | 要求 |
|---|---|
| 代码复用 | 上游三个模块(91 个源文件)全部 commonMain、零平台源码集,零修改编译;全仓唯一"新增源码"是 45 个内置 colormath 文件(官方 3.6.1 -sources.jar 逐字节取出,MIT)与桥接层。 |
| 平台目标 | 根构建直接声明 ohosArm64 + ohosX64,保留 jvm 作交叉验证,库模块出 KLIB,example/nativeApp 出双 ABI 动态库。 |
| 构建隔离 | 根构建替换为极简 ohos 构建(cmp_qrose 模式);paletteon-kobweb(js/wasmJs 专用)与上游 demo 留盘不进构建。 |
| 桥接稳定 | 使用 2 个 C ABI 和 UTF-8 JSON,不传递 Kotlin 对象地址;异常永远不跨 FFI。 |
| 依赖纪律 | Compose 五件套走"双线":ohos 线 1.12.0-0.1.0-07、jvm 线上游同版 1.9.1 + material3 1.9.0,换线不换源;colormath 换源不换版(内置 3.6.1 源码)。 |
| UI 完整 | 四页签覆盖全部桥接入口:种子色选择、风格/对比度切换、三开关、35 角色表、主题预览卡、六条色调面板、HCT 实验室、调和实验、版本握手。 |
| 仓库规范 | 项目说明、文章、效果图和源码链接统一使用 AtomGit。 |
本项目的 ArkUI 页面不是 Compose Multiplatform UI 实现。社区 Compose 线暂无鸿蒙组合宿主,取色引擎的非 Composable 公共面经 C ABI 桥出,ArkTS Demo 把主题画出来。
二、实现路线图
第 0 阶段:查重与选线 ── oh-tpc 内无 paletteon 适配;确定与 pigment 同路(整库编译)不同库(生成器 vs 选择器)
第 1 阶段:钉工具链 ── Kotlin 2.4.20-0.1.0-07 + 社区 Compose 1.12.0-0.1.0-07 + Gradle 9.6.1 + JDK 21
第 2 阶段:模块停靠 ── 根构建替换(cmp_qrose 模式),上游构建文件 *.upstream 保全
第 3 阶段:依赖补位 ── colormath 无 ohos 制品 → 官方 3.6.1 源码整源内置为 :colormath
第 4 阶段:整库编译 ── 上游三模块零修改,jvm + ohosArm64 + ohosX64 三目标
第 5 阶段:平面 JSON 桥 ── @CName PaletteonDispatch × 8 个 op,覆盖引擎非 Composable 公共面
第 6 阶段:CMake NAPI ── libpaletteon_napi.so + $ORIGIN rpath + DisposeString 纪律
第 7 阶段:ArkUI 宿主 ── 四页签 Demo:配色方案 / 色调面板 / 颜色实验 / 关于
第 8 阶段:HAP 与真机 ── 签名(DevEco 自动生成)、安装、验收
三、逐步实现过程
第 1 阶段:项目初始化
1.1 工程边界
material-color-utilities/ 上游 Google MCU Kotlin 移植(39 文件,纯算法零依赖,进 ohos 构建)
paletteon-core/ 上游核心库(27 文件,仅 commonMain,进 ohos 构建)
paletteon-icons/ 上游图标库(25 文件,纯 ImageVector 构建器,进 ohos 构建)
colormath/ 内置 colormath 3.6.1 核心(45 文件,MIT,LICENSE.colormath)
example/nativeApp/ Kotlin/Native 动态库(Bridge.kt:@CName × 2 + MiniJson)
example/harmonyApp/ DevEco Stage、CMake N-API 和 ArkTS 四页签页面
paletteon-kobweb/ js/wasmJs 专用(Kobweb/Silk),留盘不进 settings
demo/ 上游 demo(composeApp / kobwebApp / iosApp),留盘不进构建
README.md / README.upstream.md fork 首页 / 上游 README 原文
README.OpenHarmony.md / _CN.md 鸿蒙适配说明(英 / 中,中文版 20 节)
*.upstream 被替换的上游构建文件,逐字保全
构建拓扑走 cmp_qrose 模式(根构建即 ohos 构建):settings.gradle.kts 只 include 三个上游库模块 + :colormath 与 :example:nativeApp,rootProject.name = "ohos_paletteon"。上游的 teogor-winds 发布插件、vanniktech 发布、dokka、spotless、binary-compatibility-validator、AGP classpath、GitHub Packages 发布段全部随根构建替换剥离——本 fork 不发 Maven Central。
1.2 工具链矩阵
| 项目 | 配置 |
|---|---|
| Kotlin Multiplatform | HarmonyOS Kotlin 2.4.20-0.1.0-07(eazytec Maven) |
| JDK | 21(org.gradle.java.home 指向本机 JDK 21) |
| OpenHarmony 目标 | ohosArm64 + ohosX64(jvm 保留作交叉验证) |
| Gradle | 9.6.1(上游 8.14.3;2.4.20 线配 Gradle 9.x,pigment 同款配对) |
| Compose(ohos 线) | 1.12.0-0.1.0-07:runtime / ui / foundation / animation / material3 |
| Compose(jvm 线) | 上游同线 1.9.1 + material3 1.9.0(Maven Central) |
| colormath | 3.6.1 内置为 :colormath Gradle 模块(源码级,零坐标声明) |
| DevEco product | 5.1.0(18) 兼容 |
| DevEco Studio | 5.1.0 Release |
| ABI | arm64-v8a + x86_64 |
| Bundle | org.terminator.ohos.paletteon |
export JAVA_HOME="/path/to/jdk-21"
java -version
1.3 源码依赖度盘点
| 模块 | commonMain 文件 | 依赖形态 | 处置 |
|---|---|---|---|
material-color-utilities | 39 | 纯 Kotlin,零依赖、零平台源码集 | 整体编译(jvm + 双 ohos) |
paletteon-core | 27 | compose material3/runtime/ui/foundation/animation + api(MCU) + implementation(colormath) | 整体编译(commonMain 唯一,无平台源码集) |
paletteon-icons | 25 | compose.ui + core(纯 ImageVector 构建器,零 @Composable) | 整体编译(不挂 compose 编译器插件) |
paletteon-kobweb | 13(jsMain) | Kobweb/Silk 专属 | 留盘不进构建 |
expect/actual 声明全库为零(上游唯一的 expect 组在 demo 里,demo 不进构建)。上游 paletteon-core 的 androidMain 只声明了一个源码零使用的 androidx.window 依赖,随模块构建替换剥离。
1.4 页面边界
页签 配色方案 / 色调面板 / 颜色实验 / 关于
标题区 PALETTEON · OPENHARMONY / 版本握手行(动态取色主题引擎 · 1.0.0-alpha02-ohos.1)
种子色区 20 颗种子色圆点(真 StandardColors 数据,横向滚动,选中描边)
风格区 9 个 PaletteStyle 芯片(TonalSpot/Neutral/Vibrant/Expressive/Rainbow/FruitSalad/Monochrome/Fidelity/Content)
对比度区 4 档 Contrast 芯片(Default 0.0/Medium 0.5/High 1.0/Reduced -1.0)
开关区 深色模式 / AMOLED 纯黑 / 扩展保真色集 三个 Switch
预览卡 surface 背景 + onSurface 文字 + primary/secondaryContainer/error 胶囊按钮,随主题实时换肤
角色表 35 个 ColorScheme 角色(色块 + 角色名 + hex)
色调面板页签 六条色板 × 13 档色调 + 色相/色度 + variant 元数据
颜色实验页签 hex 输入 + HCT 分解 + withTone 色阶 + ktx 结果 + 调和实验(含匹配饱和度分支)
关于页签 版本握手、调用链、已知限制、仓库与社区链接
页面启动时先调 paletteonVersion() 做握手,再拉取 styles / contrasts / seeds 三组真枚举与真数据渲染选择器。
第 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: ohosArm64ApiElements-published / ohosX64ApiElements-published / jvmApiElements-published ✓
org.jetbrains.compose.ui:ui / foundation:foundation / animation:animation / animation-core:animation-core 同上 ✓
org.jetbrains.compose.material3:material3:1.12.0-0.1.0-07 同上 ✓
com.github.ajalt.colormath:colormath:3.6.1
variants: jvm / ios / js / wasm / macos / linux / mingw …无任何 ohos 变体 ✗
三个关键结论:其一,paletteon 用到的全部 Compose 构件——包括上游 internal/ColorScheme.kt 里 animateColorAsState 所在的 animation 线——社区 Compose 线都有真实 ohos klib,"整库编译"没有死角;其二,ohos 线不发布 *-desktop 子构件,jvm 交叉验证必须换到 Maven Central 的上游同线(compose 1.9.1);其三,colormath 是全网唯一的缺口——eazytec 双仓 + Maven Central 的 REST 检索都只有无 ohos 变体的原版制品。
2.2 colormath 的处置:整源内置(换源不换版)
上游 paletteon-core 以 implementation 消费 com.github.ajalt.colormath:colormath:3.6.1,使用点集中在两个文件:internal/ColorMath.kt(compose Color ↔ colormath 各色彩空间互转)与 ktx/Color.kt 的 matchSaturation(HSL 饱和度匹配,Blend.harmonize(matchSaturation = true) 的分支)。改源码绕开 colormath 违反"上游零修改"底线;手写 colormath 兼容 shim 则要复刻 ACES/ROMM/LAB/XYZ 一整套传递函数,漂移风险极高。
验证发现 colormath 3.6.1 的核心模块是45 个纯 commonMain 文件、零平台源码集、零第三方依赖——官方 -sources.jar 解开即用。处置:把 45 个文件逐字节放进 colormath/src/commonMain/,建一个无任何依赖的 KMP 模块(jvm + ohosArm64 + ohosX64),paletteon-core 的依赖坐标从 Maven 坐标换成 implementation(project(":colormath"))——源内容与版本完全一致,换源不换版。上游 MIT 许可随仓分发(colormath/LICENSE.colormath)。
2.3 工具链钉版(为什么是 2.4.20-0.1.0-07 + Gradle 9.6.1)
社区此前批准的钉法有两条线:零依赖移植用 Kotlin 2.2.21-1.0.0 + Gradle 8.14.1(qrose / haze);依赖社区制品线的用 2.4.20-0.1.0-07 + Gradle 9.x(pigment / KStore)。paletteon 必须走第二条:社区 Compose 线 1.12.0-0.1.0-07 的 stdlib 元数据要求 2.4 线。上游源码是 Kotlin 2.2.20 时代写的,2.4.20 编译器直接可编(同代向后兼容),源码级成本为零。Gradle 从上游 8.14.3 换到 9.6.1(pigment 已验证的配对),wrapper jar + properties 一并替换。
settings.gradle.kts 把 eazytec 仓库写在最前:
pluginManagement {
repositories {
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_paletteon"
include(":material-color-utilities")
include(":paletteon-core")
include(":paletteon-icons")
include(":colormath")
include(":example:nativeApp")
2.4 编译真实引擎
paletteon-core/build.gradle.kts 重写为双线依赖的纯 KMP 模块(上游原件保全为 build.gradle.kts.upstream):
plugins {
alias(libs.plugins.kotlin.multiplatform)
alias(libs.plugins.kotlin.compose.compiler) // 上游 composables 原样编译进 ohos klib
}
kotlin {
explicitApi()
applyDefaultHierarchyTemplate()
jvm() // 交叉验证用
ohosArm64()
ohosX64()
sourceSets {
commonMain.dependencies {
api(project(":material-color-utilities"))
implementation(project(":colormath")) // 内置 3.6.1,换源不换版
}
ohosMain.dependencies { // 社区 Compose 线
api(libs.compose.runtime)
api(libs.compose.ui)
api(libs.compose.foundation)
api(libs.compose.animation)
api(libs.compose.material3)
}
jvmMain.dependencies { // 上游制品线(Maven Central)
api(libs.compose.runtime.jvm)
api(libs.compose.ui.jvm)
api(libs.compose.foundation.jvm)
api(libs.compose.animation.jvm)
api(libs.compose.material3.jvm)
}
}
}
material-color-utilities 与 colormath 无 Compose 依赖,构建里只有 jvm() + 双 ohos 目标;paletteon-icons 零 @Composable,不挂 compose 编译器插件。实测:
.\gradlew.bat :material-color-utilities:compileKotlinJvm :colormath:compileKotlinJvm `
:paletteon-core:compileKotlinJvm :paletteon-icons:compileKotlinJvm # BUILD SUCCESSFUL
.\gradlew.bat :colormath:compileKotlinOhosArm64 :material-color-utilities:compileKotlinOhosArm64 `
:paletteon-core:compileKotlinOhosArm64 :paletteon-icons:compileKotlinOhosArm64 # 全部通过
上游 91 个源文件一个没动,paletteon-core 的 27 个组合函数、DSL、ktx、设备模拟组件全部带着 @Composable 编进了 ohos klib——只差一个组合宿主。
第 3 阶段:桥协议建模
3.1 JSON 契约
dev.teogor.paletteon/**(上游引擎 + MCU + 内置 colormath,原样编译)
↓ paletteonColorScheme / toDynamicScheme / ktx / StandardColors
Bridge.kt(@CName × 2,扁平 JSON 进出)
↓ UTF-8 JSON
N-API paletteonDispatch() / paletteonVersion()
↓
ArkUI Index.ets(四页签滚动)
请求与响应(scheme 为例):
请求 : {"op":"scheme","hex":"#6750A4","dark":"true","amoled":"false",
"extended":"false","style":"TonalSpot","contrast":"Default"}
响应 : {"ok":true,"op":"scheme","seedHex":"#6750A4","dark":true,"amoled":false,
"style":"TonalSpot","contrastLevel":0.0,"variant":"TONAL_SPOT",
"sourceColorHex":"#6750A4",
"roles":[{"role":"primary","hex":"#D0BCFF"},... 共 35 项]}
桥共 8 个 op:version / styles / contrasts / seeds(真枚举与真数据)/ scheme(真 paletteonColorScheme())/ palettes(真 DynamicScheme 六色板 × 13 档)/ hct(真 Hct.fromInt() 色彩科学)/ color(真 ktx 扩展,含 colormath 分支)。
3.2 8 个 op 的真库执行点
| op | 真库执行点 |
|---|---|
version | paletteonVersion() 原生常量 |
styles / contrasts | 真 PaletteStyle.entries / Contrast.entries 枚举遍历 |
seeds | 真 StandardColors 列表(上游 demo 同款 20 颗种子色) |
scheme | 真 paletteonColorScheme()——PaletteonDynamicTheme 组合时调用的同一个函数 |
palettes | 真 Color.toDynamicScheme() → DynamicScheme.primaryPalette…errorPalette + TonalPalette.tone() |
hct | 真 Hct.fromInt() / withTone() / ColorUtils.calculateLuminance() / DislikeAnalyzer |
color | 真 ktx:isLight / lighten / darken / isDisliked / fixIfDisliked / Blend.harmonize(±matchSaturation) |
variant 字段来自真实 DynamicScheme.variant(MCU Variant 枚举),证明 9 种风格参数真实映射到 SchemeTonalSpot / SchemeContent 等对应实现。
3.3 错误通道
每个导出整体 try/catch,Kotlin 异常永远不跨 FFI:
@CName("PaletteonDispatch")
fun paletteonDispatch(request: String): String = try {
/* op 分发 */
} catch (t: Throwable) {
"""{"ok":false,"op":"dispatch","error":${jsonString(t.message ?: t.toString())}}"""
}
非法 hex、未知风格、未知对比度、不支持的 op,全部走同一个 ok:false 通道,ArkTS 侧状态区统一渲染。
第 4 阶段:原生桥接(技术难点)
4.1 三层桥接
ArkTS
│ JSON string
▼
C++ N-API entry(libpaletteon_napi.so)
│ const char*
▼
Kotlin/Native C ABI(libohospaletteon.so)
│ PaletteonDispatch / PaletteonVersion
▼
UTF-8 JSON + DisposeString
直接导出 Kotlin 对象会暴露 ABI 和生命周期问题;只导出整数又会让页面复制取色语义,所以选择 C ABI + JSON。
4.2 Kotlin/Native 导出函数
@CName("PaletteonVersion")
fun paletteonVersion(): String = "1.0.0-alpha02-ohos.1" // 版本握手
@CName("PaletteonDispatch")
fun paletteonDispatch(request: String): String // 8 个 op 一口进
| C ABI 符号 | ArkTS 方法 | 覆盖内核 |
|---|---|---|
PaletteonDispatch | paletteonDispatch(req) | scheme / palettes / hct / color / styles / contrasts / seeds / version |
PaletteonVersion | paletteonVersion() | fork 版本握手(真机版本行的数据来源) |
4.3 C++ N-API 方法分发
napi_property_descriptor desc[] = {
{"paletteonDispatch", nullptr, NapiPaletteonDispatch, nullptr, nullptr, nullptr,
static_cast<napi_property_attributes>(napi_default | napi_enumerable), nullptr},
{"paletteonVersion", nullptr, NapiPaletteonVersion, nullptr, nullptr, nullptr,
static_cast<napi_property_attributes>(napi_default | napi_enumerable), nullptr},
};
每个回调统一三步:napi_typeof 校验必须是 string、OH_LOG_INFO 打字节数、调 KN 函数并包装返回。KN 返回的字符串必须经 libohospaletteon_symbols()->DisposeString 归还 Kotlin 运行时,绝不 free()。libohospaletteon_api.h 按社区已审仓库同款手写(只声明 ExportedSymbols 结构与两个业务符号),不拖整份生成头。
4.4 CMake 和类型声明
add_library(ohospaletteon SHARED IMPORTED)
set_target_properties(ohospaletteon PROPERTIES
IMPORTED_LOCATION ${KN_SO}
IMPORTED_NO_SONAME TRUE)
add_library(paletteon_napi SHARED napi_init.cpp)
target_link_libraries(paletteon_napi PUBLIC
libace_napi.z.so
libhilog_ndk.z.so
ohospaletteon)
OHOS_ARCH 为空或 libohospaletteon.so 缺失直接 FATAL_ERROR(提示先跑 link 任务);$ORIGIN rpath 加 POST_BUILD 拷贝保证 NAPI so、KN so、libc++ 三件套同目录,dlopen 时互相可见。
export const paletteonDispatch: (request: string) => string;
export const paletteonVersion: () => string;
第 5 阶段:ArkUI 宿主
5.1 注入数据和触发操作
aboutToAppear(): void {
this.version = paletteonVersion(); // 版本握手:纯字符串 C 导出
const stylesRes = JSON.parse(paletteonDispatch(JSON.stringify(stylesReq))) as StylesResponse;
this.styles = stylesRes.styles; // 真枚举渲染 9 个风格芯片
/* contrasts / seeds 同理,然后 refreshScheme() / refreshPalettes() / refreshLab() */
}
private refreshScheme(): void {
const req: SchemeRequest = {
op: 'scheme', hex: this.seedHex(),
dark: this.dark ? 'true' : 'false',
amoled: this.amoled ? 'true' : 'false',
extended: this.extended ? 'true' : 'false',
style: this.styles[this.selectedStyle].name,
contrast: this.contrasts[this.selectedContrast].name
};
const res = JSON.parse(paletteonDispatch(JSON.stringify(req))) as SchemeData;
if (res.ok) { this.scheme = res; } else { this.schemeError = res.error ?? 'unknown'; }
}
注意 ArkTS 严格模式的两条铁律:对象字面量必须对应显式 interface(SchemeRequest 等 8 个请求/响应接口先行声明),Record<string, string> 的标识符键字面量会被本 SDK 拒绝。
5.2 页面交互
配色方案页签自上而下:20 颗种子色圆点行(真 StandardColors,选中加粗描边)、9 个风格芯片、4 档对比度芯片、深色/AMOLED/扩展保真三个开关、由真实角色色驱动的主题预览卡(surface 背景 + primary/secondaryContainer/error 胶囊),随后是 35 个角色整表(色块 + 角色名 + hex,中英对照)。色调面板页签展示六条色板 × 13 档色调,头部带 variant 与色相/色度元数据。颜色实验页签提供 hex 输入与目标色输入,展示 HCT 分解、withTone 色阶、ktx 结果与调和对比。关于页签展示版本握手行、ArkTS → NAPI → C ABI → Kotlin 引擎 → material-color-utilities 分层说明与仓库链接。
5.3 对比度和错误边界
种子色圆点带选中描边、芯片选中态反色、AMOLED 开启时 surface 系角色肉眼可见地变纯黑;ok:false 时错误原文进状态行;Native 调用本身抛错(理论不该发生)同样被 try/catch 收进状态区。
第 6 阶段:示例与验证
6.1 工程结构
example/
├── nativeApp/
│ ├── build.gradle.kts 双 ABI sharedLib(baseName=ohospaletteon)+ copyKnSo*
│ └── src/ohosMain/kotlin/example/nativeapp/
│ └── Bridge.kt @CName × 2 + MiniJson(扁平 JSON,继承自 cmp_qrose 审核模式)
└── harmonyApp/
├── build-profile.json5 signingConfigs 留空(签名由 DevEco 自动生成回填)
├── AppScope/app.json5 bundleName org.terminator.ohos.paletteon、versionName 1.0.0-alpha02-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/libohospaletteon_api.h
├── src/main/cpp/types/libpaletteon_napi/{index.d.ts, oh-package.json5}
└── src/main/ets/pages/Index.ets ArkUI 四页签验收页面
6.2 构建、签名和安装
# 双 ABI so(链接产物自动拷入 example/harmonyApp/entry/libs/)
.\gradlew.bat :example:nativeApp:linkDebugSharedOhosArm64 # BUILD SUCCESSFUL in 2m 3s
.\gradlew.bat :example:nativeApp:linkDebugSharedOhosX64 # BUILD SUCCESSFUL in 2m 13s
# HAP(DevEco Studio 打开 example/harmonyApp 构建,或命令行 hvigorw)
hvigorw assembleHap -p product=default -p buildMode=debug --no-daemon
签名在 DevEco Studio 的 File → Project Structure → Signing Configs 中勾选自动生成,证书写入本机,不进入仓库(仓库内 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.paletteon -m entry
四、完整代码对照
4.1 整体架构
dev.teogor.paletteon.paletteon-core/**(上游引擎原样编译)
│ paletteonColorScheme / PaletteonDynamicTheme / ktx / PaletteonConfig DSL
dev.teogor.paletteon(material-color-utilities)/**(上游算法原样编译)
│ Hct / DynamicScheme × 9 / MaterialDynamicColors / TonalPalette / DislikeAnalyzer / Score
com.github.ajalt.colormath/**(内置 3.6.1 源码原样编译)
│ HSL / LAB / XYZ 色彩空间(matchSaturation 路径)
Bridge.kt + MiniJson
│ PaletteonDispatch / PaletteonVersion
libpaletteon_napi.so
│ paletteonDispatch / paletteonVersion
Index.ets
│ JSON parse + @State 更新
▼
ArkUI 真机页面(四页签滚动)
4.2 文件清单
| 文件 | 职责 |
|---|---|
settings.gradle.kts | 极简 ohos 构建、eazytec Maven 置前、ohos_paletteon、kobweb/demo 移出 |
gradle/libs.versions.toml | Kotlin 2.4.20-0.1.0-07 + Compose 双线钉版(ohos 1.12.0-0.1.0-07 / jvm 1.9.1 + material3 1.9.0) |
build.gradle.kts(根) | 版本 1.0.0-alpha02-ohos.1、全局禁用 Ohos*Test* 任务 |
paletteon-core/build.gradle.kts 等 ×3 | 双线纯 KMP:jvm + ohosArm64 + ohosX64(原件 *.upstream 保全) |
colormath/(45 文件 + 构建 + LICENSE) | 官方 3.6.1 源码逐字节内置,MIT |
example/nativeApp/build.gradle.kts | 双 ABI sharedLib、copyKnSo* 回填三件套 |
nativeApp/.../Bridge.kt | 2 个 C ABI、8 个 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 四页签验收页面 |
README.OpenHarmony_CN.md | 鸿蒙适配主文档(20 节) |
4.3 关键 API 对照
| 层次 | API | 作用 |
|---|---|---|
| 引擎 | paletteonColorScheme(seedColor, isDark, isAmoled, style, contrast, isExtendedFidelity) | 种子色 → 35 角色 ColorScheme(PaletteonDynamicTheme 的引擎函数) |
| 引擎 | Color.toDynamicScheme(isDark, style, contrast) | 9 种 PaletteStyle → 对应 MCU Scheme 实现 |
| 算法 | TonalPalette.tone(tone) | HCT 色调求解器取任意档色调 |
| 算法 | Hct.fromInt / withTone | HCT 分解与改调重建 |
| 算法 | DislikeAnalyzer.isDisliked / fixIfDisliked | 反感色判定与修复 |
| ktx | Color.isLight / lighten / darken / harmonize(matchSaturation) | 公共色彩扩展(后者的饱和度匹配走内置 colormath) |
| Native | PaletteonDispatch / PaletteonVersion | C ABI JSON 导出 |
| N-API | paletteonDispatch(req) / paletteonVersion() | 向 ArkTS 暴露能力 |
| ArkTS | refreshScheme() / refreshPalettes() / refreshLab() | 触发操作并刷新 UI |
4.4 ArkTS 与 Kotlin 的边界
const res = JSON.parse(paletteonDispatch(JSON.stringify({
op: 'scheme', hex: '#FBC02D', dark: 'true', amoled: 'false',
extended: 'false', style: 'Expressive', contrast: 'High'
}))) as SchemeData;
// res.variant === 'EXPRESSIVE';res.roles 35 项,primary/onPrimary…直接喂给 ArkUI 组件
val scheme = paletteonColorScheme(
seedColor = Color(argb), isDark = dark, isAmoled = amoled,
style = style, contrast = contrast, isExtendedFidelity = extended,
)
两者之间只传输扁平 JSON 字符串,不传输 Kotlin 对象或未校验的动态结构。
五、关键决策说明
决策 1:真编译整库而不是提取零依赖内核
早期仓库抽内核,是因为 ohos 上没有它们依赖的制品。本次适配先验证制品线(Compose 五件套含 animation 全部有 ohos 变体),障碍只剩 colormath——再用下一条决策补位。再抽内核等于把取色引擎手抄一遍,反而引入漂移风险。真编译让九种 Scheme、35 角色推导、HCT 求解器、反感色修复全部是上游原件。
决策 2:colormath 整源内置(换源不换版)
全网无 colormath ohos 制品。它的核心恰好是 45 个纯 commonMain 文件,官方 -sources.jar 逐字节取出内置为 :colormath 模块:源内容与 3.6.1 发布件完全一致、MIT 许可随仓分发、上游 paletteon-core 源码零修改(只把依赖坐标换成 project 依赖)。手写兼容 shim 需要复刻 ACES/ROMM/LAB/XYZ 传递函数,漂移风险不可接受。
决策 3:工具链钉 2.4.20-0.1.0-07 + Gradle 9.6.1,双线 Compose 依赖
社区 Compose 线的 stdlib 元数据要求 Kotlin 2.4 线(2.2.21 旧线读不了);上游 Kotlin 2.2.20 源码对 2.4.20 编译器零成本。ohos 线不发布 *-desktop 子构件,jvm 交叉验证换 Maven Central 上游同线——换线不换源(pigment / pipette / SpriteView 先例)。material3 与主线分别版本化(1.9.1 的 material3 不存在,实际 1.9.0)。
决策 4:桥只覆盖非 Composable 面
paletteonColorScheme 本就是公共纯函数(PaletteonDynamicTheme 内部调用的就是它),HCT / ktx / StandardColors 同理。组合函数与 animateColorAsState 动画是 Compose 运行时能力,不跨 C ABI——组合函数已编进 klib,等鸿蒙组合宿主出现后 UI 面自然回归,ArkUI 复刻层退役。
决策 5:根构建替换(cmp_qrose 模式),上游构建 *.upstream 保全
上游根构建(teogor-winds / vanniktech / dokka / spotless / binary-compatibility-validator / AGP 9 / 新版 KMP android 插件)对鸿蒙没有保留价值,整个替换为极简 ohos 构建;paletteon-kobweb(浏览器 Silk 专用)与上游 demo 留盘不进 settings——它们的存在方式只有这一种。
决策 6:保守并发与编译器 JVM 内存上限
本机 16 GB 同时运行 DevEco Studio 与模拟器时,默认并行扇出 + Gradle/Kotlin/KN 三个 JVM 叠加会耗尽原生内存(malloc 失败、守护进程崩溃)。gradle.properties 固化 org.gradle.parallel=false、workers.max=2、kotlin.daemon.jvmargs=-Xmx1g、kotlin.native.daemon.jvmargs=-Xmx2g,必要时先 .\gradlew --stop 再链接。
六、测试与验证
6.1 测试环境
本次验证使用 Windows(x86_64)、JDK 21、DevEco Studio 5.1.0 Release、OpenHarmony 命令行工具(hvigor / hdc)与鸿蒙真机(arm64)。
6.2 JVM 交叉验证
.\gradlew.bat :material-color-utilities:compileKotlinJvm :colormath:compileKotlinJvm `
:paletteon-core:compileKotlinJvm :paletteon-icons:compileKotlinJvm
四模块对上游同线制品(compose 1.9.1 + material3 1.9.0)全部 BUILD SUCCESSFUL——上游 91 个源文件零修改的编译级证明(上游库模块本无 commonMain 测试,showcase 在 demo app,验证等级为编译级,与 pigment 先例一致)。
6.3 HAP 验证
:colormath/:material-color-utilities/:paletteon-core/:paletteon-icons:compileKotlinOhosArm64: 通过
linkDebugSharedOhosArm64: BUILD SUCCESSFUL(2m 3s)→ libohospaletteon.so 约 28.3 MB + libc++ 约 1.8 MB
linkDebugSharedOhosX64: BUILD SUCCESSFUL(2m 13s)→ 约 27.1 MB + 约 1.7 MB
llvm-nm -D 双 ABI: PaletteonDispatch / PaletteonVersion / libohospaletteon_symbols 均为全局 T 符号
CompileArkTS: finished(3.8 s)
BuildNativeWithNinja: finished(libpaletteon_napi.so 双 ABI)
PackageHap: finished
BUILD SUCCESSFUL
install bundle successfully / start ability successfully
entry-default-unsigned.hap 约 42.9 MB,内含双 ABI 各三件套(libpaletteon_napi.so / libohospaletteon.so / libc++_shared.so,KN so 打包 strip 后约 20 MB/ABI)。
6.4 功能用例
- 启动后标题行出现
paletteonVersion() → 1.0.0-alpha02-ohos.1(真实 Native 握手,非静态文字); - 种子色行显示 20 颗真
StandardColors圆点;选 Golden Amber,角色表 35 项整体随种子色变化; - 风格切
Expressive,variant变为EXPRESSIVE,三级色系明显增强; - 对比度切
High(1.0),主色与其上文字对比度肉眼增强; - 开 AMOLED 后 surface/onSurface/surfaceContainer 系变纯黑或近黑,
background角色读数#000000; - 色调面板页签六条色板各 13 档,Tone 0/100 接近纯黑/白;风格切
Monochrome后中间调度彩色消失(色度趋 0),variant变MONOCHROME; - 颜色实验输入
#5D4037:HCT 三分量与亮度刷新,withTone 30/50/80 色阶连续;调和目标填#388E3C,harmonize与"匹配饱和度"两个结果可不同(后者经内置 colormath 的 HSL 路径计算); - 输入非法 hex,状态区显示桥返回的 error 原文,不崩溃。
6.5 验证结论
取色引擎、HCT 色彩科学、Compose 编译产物、Native 动态库、C ABI、N-API、ArkUI 页面和 HAP 已完成链路验证。应用在鸿蒙真机上成功安装并进入前台,四页签展示了真实的动态取色会话:版本握手、35 角色主题生成、风格/对比度/AMOLED 实时换肤、色调面板与色彩实验(见下节效果图)。
七、运行效果
7.1 真机截图

7.2 命令速查
git clone https://atomgit.com/oh-tpc/paletteon.git
cd paletteon
export JAVA_HOME="/path/to/jdk-21"
./gradlew :paletteon-core:compileKotlinJvm :paletteon-icons:compileKotlinJvm # jvm 交叉验证
./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.paletteon -m entry
hdc shell hilog | grep -E "PaletteonNapi|PaletteonDemo"
八、遗留问题与改进方向
8.1 踩坑复盘
- 只改 ArkTS 页面不算 KMP 适配,必须让三个上游模块真正编译成
ohosArm64/ohosX64动态库; settings.gradle.kts里 eazytec 双仓必须排在pluginManagement与dependencyResolutionManagement最前,否则配置期直接Unresolved reference: ohosArm64;- ohos 线不发布
*-desktop子构件:jvm 交叉验证必须换 Maven Central 上游同线;material3 与 Compose 主线分别版本化,1.9.1的 material3 不存在,实际是1.9.0; - colormath 全网无 ohos 制品(REST 检索 eazytec 双仓 + Maven Central 确认),唯一安全解是官方源码整源内置——手写 shim 要复刻一整套传递函数,漂移风险不可接受;
- compose 的 ARGB 生态是 Int:桥内颜色统一 Int(hex 解析
toULong(16).toInt()),出参无符号展示才.toUInt().toString(),否则Color(...)/Hct.fromInt(...)/hex6(...)全线类型不匹配; - 本机 16 GB 同时跑 DevEco + 模拟器时,Gradle 默认并行扇出 + 三个 JVM 叠加会耗尽原生内存(
malloc failed、守护进程崩溃、hs_err_pid*.log满天飞):固化保守并发与两个编译器 JVM 堆上限,链接前先.\gradlew --stop释放内存; - 上游
PaletteonColorScheme.kt用的 35 角色构造器在新版 material3 标记弃用(建议 fixed 容器角色),坚持"上游零修改"则保留警告(与上游自家构建一致),不擅自升级调用; - ArkTS 严格模式禁用内联对象类型(
as { styles: X[] }直接报arkts-no-obj-literals-as-types),且本 SDK 拒绝Record<string, string>的标识符键字面量:请求/响应一律先声明 interface 再构造字面量; finalizedBy的拷贝任务在 link 失败后仍会执行(空集无害),重跑 link 后自然补齐;- 命令行 hvigor 打包报
PackageHap ... spawn java ENOENT:打包进程从 PATH 找 java,跑 hvigorw 前把 DevEco 自带 JBR 加入 PATH,或在 DevEco Studio 里直接构建; build-profile.json5的signingConfigs条目缺material字段会被 hvigor schema 拒绝——仓库内留空数组且 product 不引用签名,勾选自动签名后由 IDE 回填;- HAP 缺当前 ABI 的 so 会报安装失败
9568347,abiFilters必须双 ABI,每个 ABI 三件套缺一不可; - KN 返回字符串必须
DisposeString归还运行时,N-API 层不能free()。
8.2 已知问题
paletteon-kobweb(浏览器 Silk 专用)不适用鸿蒙,留盘不进构建;PaletteonDynamicTheme等组合函数已编进 ohos klib,但社区 Compose 线暂无鸿蒙组合宿主,Demo 视觉层为 ArkUI 复刻——宿主就绪后自然回归;ColorScheme过渡动画(animateColorAsState)是 Compose 运行时能力,不跨 C ABI;ExtendedColorScheme的 success/warning 扩展角色经rememberColorForRole组合读取,不单独桥接;Score.scoreFromImage(从位图提取种子色)需要像素缓冲,当前桥不传图,后续可加argb:count列表载荷;- android / js / wasmJs / ios / macos 目标剥离(源文件留在树里),发布、dokka、spotless、二进制兼容校验一并剥离,本 fork 不发 Maven Central;
- HAP 安装必须使用与 bundle name
org.terminator.ohos.paletteon匹配的签名。
8.3 未来优化方向
- 鸿蒙组合宿主就绪后,直接把
PaletteonDynamicTheme挂上(klib 已含全部 Composable),ArkUI 复刻层退役; - 桥补
scoreop 支持壁纸取色演示(QuantizerCeleci+Score已随编译进入.so); - 在 CI 中加入 ohos 编译、ELF 符号检查和 HAP 构建;
- 评估
.har形态发布,让 ArkTS 工程直接依赖而不是走 so 三件套; - 补充模拟器(x86_64)与其他设备 ABI 的验收矩阵和自动化截图脚本。
九、总结
9.1 核心难点回顾
上游三模块(91 个 commonMain 文件,零平台源码集)
→ 验证社区 Compose 制品线(五件套含 animation 全有 ohos klib)
→ 工具链对齐 Kotlin 2.4.20-0.1.0-07(Gradle 9.6.1 配对)
→ 根构建替换(cmp_qrose 模式,上游构建 *.upstream 保全)
→ colormath 缺口:官方 3.6.1 源码整源内置(换源不换版)
→ ohosArm64 / ohosX64 KLIB,上游源码零修改
→ C ABI(@CName × 2,8 op)+ 扁平 JSON
→ C++ N-API(DisposeString 纪律)
→ ArkTS JSON 解析
→ ArkUI 真机四页签验收(35 角色主题 / 六色板 / HCT 实验 / 版本握手)
9.2 封装层次
- 引擎层:上游原版
paletteonColorScheme、Color.toDynamicScheme、PaletteonConfig DSL 与 ktx 扩展; - 算法层:上游原版 material-color-utilities(Hct、九种 Scheme、MaterialDynamicColors、TonalPalette、DislikeAnalyzer、Score);
- 色彩空间层:内置 colormath 3.6.1 源码(HSL/LAB/XYZ,matchSaturation 路径);
- Native 层:生成双 ABI 动态库并通过 2 个 C ABI 输出 JSON;
- N-API 层:完成参数检查、字符串转换和 Native 内存释放;
- ArkTS 层:管理页面状态、JSON 校验、操作分发、错误文本;
- DevEco 层:完成 CMake、HAP、签名、安装和真机运行。
9.3 三条经验
- 先验证生态制品线再定适配路线:Compose 五件套(含 animation)的 ohos klib 齐全后,UI 库的正确答案就是"整库编译"——91 个上游文件零改动;唯一的 colormath 缺口用"官方源码整源内置"补位而不是手写 shim,"换源不换版"让漂移风险为零;
- 版本矩阵要整条线看:Kotlin 钉版由 Compose 线的 stdlib 元数据反推(2.4.20-0.1.0-07),Gradle 按线配对(9.6.1),material3 与主线分别版本化——三个版本号互相牵制,漏看任何一个都会在解析期或编译期炸出不同错误;
- 资源受限机器上的构建纪律是工程问题:DevEco + 模拟器常驻的 16 GB 机器上,Gradle 并行扇出与三 JVM 叠加会触发原生内存耗尽;保守并发 + 编译器堆上限 + 链接前释放守护进程,是让"整库链接 Compose 运行时"这种重活在真实开发机上可复现的关键。
9.4 适配成果
当前适配已完成:
- paletteon
1.0.0-alpha02真实取色引擎的双 ABI Kotlin/Native 编译(上游 91 个 commonMain 文件零重写,ohos klib 含全部 Composable); - 社区 Compose 线 1.12.0-0.1.0-07 在动态取色库上的完整落地(runtime / ui / foundation / animation / material3 五件套 ohos klib);
- colormath 3.6.1 官方源码整源内置(45 文件,MIT 随仓分发,上游源码零修改);
- 2 个 C ABI 符号(8 个 op 覆盖引擎非 Composable 公共面)和 C++ N-API 包装层(
llvm-nm双 ABI 验证全局 T 符号); - DevEco Stage ArkUI 四页签验收页面(配色方案 / 色调面板 / 颜色实验 / 关于);
- JVM 交叉验证编译全绿 + HAP 构建(42.9 MB,双 ABI 六件套 so)、签名、设备安装和真机验证;
- AtomGit 项目文档、OpenHarmony README(中英双份,中文版 20 节)和真机效果图。
适配过程 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)