本文记录 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 3 ColorScheme;PaletteonDynamicTheme 一个组合函数即可挂接主题,支持 9 种配色风格、4 档对比度、AMOLED 纯黑、扩展保真色集与颜色过渡动画。本次适配与社区早期路线(qrose / haze / PreCompose / voyager 的"零依赖内核提取")有本质区别:社区 Maven 上不仅有完整的 HarmonyOS kotlinx 制品线,社区 Compose 线(1.12.0-0.1.0-07)连 animation 构件都发布了 ohosArm64 / ohosX64 klib,因此上游三个库模块的全部 commonMain 源文件——material-color-utilities(39 文件)、paletteon-core(27 文件)、paletteon-icons(25 文件)——零修改编译成 OpenHarmony KLIB;唯一的缺口 colormath(上游 implementation 依赖)没有任何仓库发布 ohos 变体,本 fork 把官方 3.6.1 -sources.jar 的 45 个纯 commonMain 文件逐字节内置为 :colormath Gradle 模块,上游源码依然一行不改。一个"种子色进、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 / tonesscheme 返回 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 MultiplatformHarmonyOS Kotlin 2.4.20-0.1.0-07(eazytec Maven)
JDK21(org.gradle.java.home 指向本机 JDK 21)
OpenHarmony 目标ohosArm64 + ohosX64(jvm 保留作交叉验证)
Gradle9.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)
colormath3.6.1 内置为 :colormath Gradle 模块(源码级,零坐标声明)
DevEco product5.1.0(18) 兼容
DevEco Studio5.1.0 Release
ABIarm64-v8a + x86_64
Bundleorg.terminator.ohos.paletteon
export JAVA_HOME="/path/to/jdk-21"
java -version
1.3 源码依赖度盘点
模块commonMain 文件依赖形态处置
material-color-utilities39纯 Kotlin,零依赖、零平台源码集整体编译(jvm + 双 ohos)
paletteon-core27compose material3/runtime/ui/foundation/animation + api(MCU) + implementation(colormath)整体编译(commonMain 唯一,无平台源码集)
paletteon-icons25compose.ui + core(纯 ImageVector 构建器,零 @Composable)整体编译(不挂 compose 编译器插件)
paletteon-kobweb13(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真库执行点
versionpaletteonVersion() 原生常量
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 方法覆盖内核
PaletteonDispatchpaletteonDispatch(req)scheme / palettes / hct / color / styles / contrasts / seeds / version
PaletteonVersionpaletteonVersion()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.tomlKotlin 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.kt2 个 C ABI、8 个 op、MiniJson、错误通道
harmonyApp/.../cpp/napi_init.cppN-API 导出、参数检查、DisposeString
harmonyApp/.../cpp/CMakeLists.txtIMPORTED 引入 KN so、$ORIGIN rpath、POST_BUILD 三件套
harmonyApp/.../ets/pages/Index.etsArkUI 四页签验收页面
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 / withToneHCT 分解与改调重建
算法DislikeAnalyzer.isDisliked / fixIfDisliked反感色判定与修复
ktxColor.isLight / lighten / darken / harmonize(matchSaturation)公共色彩扩展(后者的饱和度匹配走内置 colormath)
NativePaletteonDispatch / PaletteonVersionC ABI JSON 导出
N-APIpaletteonDispatch(req) / paletteonVersion()向 ArkTS 暴露能力
ArkTSrefreshScheme() / 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 功能用例

  1. 启动后标题行出现 paletteonVersion() → 1.0.0-alpha02-ohos.1(真实 Native 握手,非静态文字);
  2. 种子色行显示 20 颗真 StandardColors 圆点;选 Golden Amber,角色表 35 项整体随种子色变化;
  3. 风格切 Expressive,variant 变为 EXPRESSIVE,三级色系明显增强;
  4. 对比度切 High(1.0),主色与其上文字对比度肉眼增强;
  5. 开 AMOLED 后 surface/onSurface/surfaceContainer 系变纯黑或近黑,background 角色读数 #000000;
  6. 色调面板页签六条色板各 13 档,Tone 0/100 接近纯黑/白;风格切 Monochrome 后中间调度彩色消失(色度趋 0),variant 变 MONOCHROME;
  7. 颜色实验输入 #5D4037:HCT 三分量与亮度刷新,withTone 30/50/80 色阶连续;调和目标填 #388E3C,harmonize 与"匹配饱和度"两个结果可不同(后者经内置 colormath 的 HSL 路径计算);
  8. 输入非法 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 踩坑复盘

  1. 只改 ArkTS 页面不算 KMP 适配,必须让三个上游模块真正编译成 ohosArm64 / ohosX64 动态库;
  2. settings.gradle.kts 里 eazytec 双仓必须排在 pluginManagement 与 dependencyResolutionManagement 最前,否则配置期直接 Unresolved reference: ohosArm64;
  3. ohos 线不发布 *-desktop 子构件:jvm 交叉验证必须换 Maven Central 上游同线;material3 与 Compose 主线分别版本化,1.9.1 的 material3 不存在,实际是 1.9.0;
  4. colormath 全网无 ohos 制品(REST 检索 eazytec 双仓 + Maven Central 确认),唯一安全解是官方源码整源内置——手写 shim 要复刻一整套传递函数,漂移风险不可接受;
  5. compose 的 ARGB 生态是 Int:桥内颜色统一 Int(hex 解析 toULong(16).toInt()),出参无符号展示才 .toUInt().toString(),否则 Color(...) / Hct.fromInt(...) / hex6(...) 全线类型不匹配;
  6. 本机 16 GB 同时跑 DevEco + 模拟器时,Gradle 默认并行扇出 + 三个 JVM 叠加会耗尽原生内存(malloc failed、守护进程崩溃、hs_err_pid*.log 满天飞):固化保守并发与两个编译器 JVM 堆上限,链接前先 .\gradlew --stop 释放内存;
  7. 上游 PaletteonColorScheme.kt 用的 35 角色构造器在新版 material3 标记弃用(建议 fixed 容器角色),坚持"上游零修改"则保留警告(与上游自家构建一致),不擅自升级调用;
  8. ArkTS 严格模式禁用内联对象类型(as { styles: X[] } 直接报 arkts-no-obj-literals-as-types),且本 SDK 拒绝 Record<string, string> 的标识符键字面量:请求/响应一律先声明 interface 再构造字面量;
  9. finalizedBy 的拷贝任务在 link 失败后仍会执行(空集无害),重跑 link 后自然补齐;
  10. 命令行 hvigor 打包报 PackageHap ... spawn java ENOENT:打包进程从 PATH 找 java,跑 hvigorw 前把 DevEco 自带 JBR 加入 PATH,或在 DevEco Studio 里直接构建;
  11. build-profile.json5 的 signingConfigs 条目缺 material 字段会被 hvigor schema 拒绝——仓库内留空数组且 product 不引用签名,勾选自动签名后由 IDE 回填;
  12. HAP 缺当前 ABI 的 so 会报安装失败 9568347,abiFilters 必须双 ABI,每个 ABI 三件套缺一不可;
  13. 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 复刻层退役;
  • 桥补 score op 支持壁纸取色演示(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 三条经验

  1. 先验证生态制品线再定适配路线:Compose 五件套(含 animation)的 ohos klib 齐全后,UI 库的正确答案就是"整库编译"——91 个上游文件零改动;唯一的 colormath 缺口用"官方源码整源内置"补位而不是手写 shim,"换源不换版"让漂移风险为零;
  2. 版本矩阵要整条线看:Kotlin 钉版由 Compose 线的 stdlib 元数据反推(2.4.20-0.1.0-07),Gradle 按线配对(9.6.1),material3 与主线分别版本化——三个版本号互相牵制,漏看任何一个都会在解析期或编译期炸出不同错误;
  3. 资源受限机器上的构建纪律是工程问题: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。

参考文档

Logo

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

更多推荐