本文记录 SVG Icon Compose 接入 OpenHarmony 的完整过程,覆盖 SVG 图标库盘点、鸿蒙 Kotlin / Compose / Skiko 制品线选型、真实 runtime 编译、ohosArm64 / ohosX64 目标、SVG 代码生成、Kotlin/Native 动态库、C ABI/N-API 桥接、ArkUI 页面、HAP 构建和真机效果。

SVG Icon Compose 是面向 Compose Multiplatform 的 SVG 图标库:支持把 SVG XML 解析为 Kotlin 模型,也支持直接使用 Kotlin DSL 构造图形,通过 Compose Canvas 绘制,并由独立 Gradle 插件将 SVG 文件生成为 Icons.xxx。本次适配使用 HarmonyOS Kotlin、Compose 和 Skiko 制品,将原有 runtime/commonMain 的模型、解析、绘制及动画源码整体编译为 OpenHarmony KLIB,保留原有 Compose API。示例沿用 cmp_qrose 已审核的 example/nativeApp / example/harmonyApp 分层,通过原库绘制逻辑生成离屏 PNG,再交给 ArkUI 显示。这里需要区分两个结果:原有动画代码完成 OHOS 编译;ArkTS 示例接收的是静态 PNG,不承担 Compose 动画播放。

项目地址: AtomGit/oh-tpc/svg-icon-compose.git

上游仓库: DevSrSouza/svg-icon-compose

开发工具: 华为云码道

环境安装: 环境安装指南

一、背景

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

SVG Icon Compose 把图标资源、解析模型和绘制逻辑放在公共源码中。业务可以传入 SVG 字符串,用 DSL 构造 Svg,也可以把 .svg 文件放入资源目录,由构建插件生成 Icons.Check 这样的访问器,再调用 SvgIcon(svg = ...)。图标的路径、viewBox、填充、描边、颜色继承和变换可以随业务代码一起维护,减少各平台重复实现。

OpenHarmony 接入这类库,需要让 Compose 图形类型、Skia 绘制后端与 Kotlin/Native 工具链一起工作。Svg 模型已经使用 Compose 的 Color、Offset 等类型,单独复制路径字符串无法保留整套公共 API。适配必须验证公共 runtime、编译期生成插件、Native 动态库、跨语言调用与最终图像输出。

障碍具体问题
目标缺失原有库需要增加 ohosArm64() / ohosX64(),才能生成 OpenHarmony KLIB。
工具链不一致官方 Kotlin / Compose 构建与鸿蒙定制工具链需要隔离,独立的 Gradle 插件 included build 也要同步切换。
依赖矩阵Compose runtime、foundation、ui、Material3、runtime annotation、coroutines 和 Skiko 必须解析到兼容的 OHOS 制品。
语言边界不同ArkTS 不能直接调用 Kotlin Composable,也不能直接持有 Svg 与 Compose Canvas 对象。
解析与绘制边界DSL 中存在某种节点,不代表 XML 解析器已经支持该节点;渐变模型也不代表渐变绘制已经完成。
交付链路复杂双 ABI .so、C++ runtime、生成头文件、CMake、N-API、HAP 与签名需要逐层对齐。

因此,本项目把适配面放在:公共 runtime 的真实 OHOS 编译、构建期代码生成兼容、离屏绘制入口与 C ABI/N-API 桥接。图形语义继续由原库负责,ArkTS 负责输入、调用、PNG 解码和展示。

1.2 库提供的能力

Demo 围绕一条真实的 SVG 渲染链路组织交互:

  • 静态图标:使用原项目 check.svg,展示折线路径、圆角端点与 currentColor;
  • 样式与继承:使用 paint-order-demo.svg,观察填充、描边及绘制顺序;
  • 路径与圆弧:使用 heart-pulse.svg,输出心形与折线路径的静态图像;
  • 渐变边界:输入包含渐变节点的 XML,展示明确的不支持提示;
  • 动画源快照:使用包含 animateTransform 的 loader.svg,只输出初始静态形态;
  • 参数调节:修改 SVG 源码、输出尺寸与 tint 后重新渲染,成功时显示 PNG,失败时展示原因。

Native 成功响应包含:

字段作用
ok渲染是否成功;失败响应包含 error。
width / heightPNG 的实际像素宽高。
pngBase64PNG 字节的 Base64 编码,ArkTS 解码后创建 PixelMap。

原有 SvgIcon、AnimatedSvgIcon、XML 解析、DSL 与动画模型都保留在 runtime 中。构建插件仍在开发机生成 Kotlin 图标源码;ArkTS 页面通过 Native 调用获得静态 PNG。两条使用路径共享图形模型与绘制实现。

1.3 实现适配

维度要求
代码复用整体编译 runtime 的 18 个 commonMain Kotlin 文件,保留原有 API;新增离屏绘制入口,并修复 currentColor 继承问题。
平台目标runtime 输出双 OHOS 目标 KLIB,example/nativeApp 输出双 ABI 动态库,保留 jvm("desktop") 交叉验证。
构建隔离用 -Pohos=true 选择 build-ohos.gradle.kts,原有构建与 sample 入口继续保留。
桥接稳定一个 C ABI 输入 SVG 字符串、尺寸和 ARGB,返回 JSON;不跨语言传递 Kotlin 对象。
依赖纪律OHOS 配置定向使用社区制品,桌面配置使用官方制品,避免不同平台版本规则互相影响。
UI 完整页面包含样例选择、尺寸滑块、颜色选择、源码编辑、渲染按钮、预览和状态提示。
仓库规范Native 与 ArkUI 工程遵循已审核目录,提供中英文 OpenHarmony README,项目与文章链接统一使用 AtomGit。

本项目的 ArkUI 页面使用 PNG 作为显示载体。Compose 绘制逻辑在 Kotlin/Native 内部执行;如需直接使用 SvgIcon / AnimatedSvgIcon,业务仍需提供可运行的 Compose 宿主、Composition 和相应帧时钟。

二、实现路线图

第 1 阶段:项目初始化     ── 盘点 runtime、Gradle 插件与样例,确定公共代码和页面边界
第 2 阶段:目标与依赖打通 ── OHOS 构建分流、双目标、Kotlin / Compose / Skiko 对齐、代码生成兼容
第 3 阶段:桥协议建模     ── SVG + size + tint、离屏 PNG、Base64 JSON、输入与错误边界
第 4 阶段:原生桥接       ── @CName C ABI、生成头文件、C++ N-API、参数校验和内存释放
第 5 阶段:ArkUI 宿主     ── 五类样例、源码编辑、尺寸与颜色、PixelMap 生命周期
第 6 阶段:示例与验证     ── 桌面测试、双 ABI 编译、HAP 检查、手工签名和真机效果

三、逐步实现过程

第 1 阶段:项目初始化

1.1 工程边界
runtime/                              原有 SVG 模型、XML 解析、Compose 绘制与动画
runtime/build-ohos.gradle.kts          新增:desktop + ohosArm64 + ohosX64 构建
gradle-plugin/                        独立复合构建,生成 Icons.xxx
gradle-plugin/build-ohos.gradle.kts    新增:与鸿蒙 Kotlin 工具链对齐的插件构建
sample/                               原有桌面示例与 13 个 SVG 资源
example/nativeApp/                    Kotlin/Native 动态库、请求校验与离屏 PNG
example/harmonyApp/                   DevEco Stage、CMake N-API 和 ArkTS 页面
README.OpenHarmony.md / _CN.md        鸿蒙适配说明(中英双份)

目录分层参照 cmp_qrose:库源码留在库模块,Native 桥单独成模块,DevEco 宿主放在 example/harmonyApp。构建选择采用属性分流:传入 -Pohos=true 时,根工程与 runtime 选择鸿蒙构建脚本,并包含 :example:nativeApp;不传该属性时继续走原有构建,包含 :sample。

这与 KStore 参考工程替换根构建的方式有所不同:SVG Icon Compose 保留桌面示例和原有构建入口,同时维持相同的 Native / ArkUI 交付目录。

1.2 工具链矩阵
项目配置
库版本上游基线 0.1.1,鸿蒙适配版本 0.1.1-ohos.1
Kotlin / Compose 编译器插件HarmonyOS 2.4.20-0.1.0-07
Gradle / JDKwrapper 8.14.1,Gradle 使用 JDK 21
OHOS Compose1.12.0-0.1.0-09,runtime annotation 单独对齐
OHOS coroutines / Skiko1.11.0-0.1.0-07 / 0.150.1-0.1.0-06
桌面交叉验证官方 Compose 1.12.0、Material3 1.9.0、Skiko 0.150.1
OpenHarmony 目标ohosArm64 + ohosX64,另保留 jvm("desktop")
DevEco / HvigorSDK 26.0.0.105、Hvigor 6.26.4;实际 compile / target API 26
DevEco Studio26.0.0 Release
兼容声明 / ABI5.1.0(18);arm64-v8a + x86_64
Bundleorg.ohos.svgiconcompose
export JAVA_HOME="/path/to/jdk-21"
./gradlew --version

Gradle 使用独立 JDK 21。DevEco 内置 JDK 25 不适合作为本工程 Gradle 8.14.1 的运行环境;Hvigor 则使用 DevEco 自带环境。compatibleSdkVersion 是兼容声明,API 18 设备运行尚未验证。

1.3 源码依赖度盘点
模块依赖形态处置
runtime18 个公共 Kotlin 文件,依赖 Compose 图形、布局与运行时类型整体编译,增加双 OHOS 目标,保留静态及动画 API。
gradle-pluginJVM Gradle 插件,使用 KotlinPoet 生成 Kotlin 源码在开发机运行,新增鸿蒙模式构建脚本。
sample原有桌面示例,含 13 个 SVG 源文件保留原构建入口;鸿蒙 Native 示例复用其 SVG 资源做代码生成。
example/nativeApp原库 + Compose Canvas + Skia构建双 ABI sharedLib,桌面复用同一份离屏实现。

runtime 的适配重点在依赖与绘制入口。模型已经绑定 Compose 类型,保留整库可以让路径解析、样式、变换和动画描述继续使用同一套语义。

新增公开绘制入口位于 SvgDrawing.kt,委托原有绘制函数:

fun DrawScope.drawSvgIcon(
    svg: Svg,
    tint: Color = Color.Black,
    strokeWidth: Float? = null,
    textMeasurer: TextMeasurer? = null
) = drawSvg(svg, tint, strokeWidth, textMeasurer)

这个入口允许在离屏 CanvasDrawScope 中绘制原有 Svg,使 Compose 图标与 PNG 快照共用实际绘制代码。

1.4 页面边界
标题区          SVG Icon Compose / OpenHarmony SVG 图标示例
样例区          静态图标 / 样式与继承 / 路径与圆弧 / 渐变边界 / 动画源快照
说明区          当前 SVG 文件及其展示内容、能力边界
尺寸区          输出尺寸滑块(24..384,步长 24,默认 96 px)
颜色区          currentColor 四色选择,显式填色保留 SVG 自身颜色
源码区          可编辑 SVG 文本域
操作区          渲染 SVG 按钮,渲染期间禁用重复操作
预览区          PixelMap 图片,固定显示区域内等比展示
状态区          成功尺寸与 PNG 字节数,或错误原因

页面在 aboutToAppear() 发起首次渲染。修改源码后点击渲染,切换样例或颜色时重新渲染;尺寸滑块修改参数后,通过渲染按钮提交。

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

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

先确认社区 Maven 上的 Compose、协程和 Skiko 能提供 OHOS 变体。插件仓库与依赖仓库都把以下两个入口置前:

maven("https://maven.eazytec-cloud.com/nexus/repository/maven-public/")
maven("https://maven.eazytec-cloud.com/nexus/content/groups/harmonyos")

OHOS 配置中的主要版本映射为:

依赖族OHOS 解析版本说明
org.jetbrains.compose*1.12.0-0.1.0-09不覆盖以 -internal 结尾的 group。
runtime-annotation*1.9.2-20260525000011独立规则,处理与主 Compose 版本不同的 annotation 制品。
kotlinx-coroutines*1.11.0-0.1.0-07解析带 OHOS 目标的协程运行时。
org.jetbrains.skiko0.150.1-0.1.0-06与鸿蒙 Compose / Skia 后端对齐。

版本规则仅作用于名称含 ohos 的配置。桌面配置继续使用官方依赖;其中 Material3 采用自己的版本序列,不能因为 Compose UI 是 1.12.0 就填写不存在的同号 Material3。

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

OHOS Kotlin 与 Compose 编译器插件统一使用 2.4.20-0.1.0-07,与本次采用的鸿蒙依赖制品线对齐。根工程 settings.gradle.kts 负责构建分流:

rootProject.name = "svg-icon-compose"
include(":runtime")

if (providers.gradleProperty("ohos").orNull == "true") {
    rootProject.buildFileName = "build-ohos.gradle.kts"
    project(":runtime").buildFileName = "build-ohos.gradle.kts"
    include(":example:nativeApp")
} else {
    include(":sample")
}

根 build-ohos.gradle.kts 声明同版插件:

plugins {
    kotlin("multiplatform") version "2.4.20-0.1.0-07" apply false
    kotlin("plugin.compose") version "2.4.20-0.1.0-07" apply false
}

gradle-plugin 是独立 included build,它有自己的 settings 和插件解析过程。因此它也按同一个 ohos 属性选择鸿蒙构建脚本,使用同版 Kotlin JVM 插件与 Kotlin Gradle Plugin 编译依赖。只修改主工程不足以覆盖这个独立构建。

2.3 编译真实引擎

runtime/build-ohos.gradle.kts 保留公共源码,增加桌面与双 OHOS 目标:

plugins {
    kotlin("multiplatform")
    kotlin("plugin.compose")
    `maven-publish`
}

kotlin {
    applyDefaultHierarchyTemplate()
    jvmToolchain(21)
    jvm("desktop")
    ohosArm64()
    ohosX64()
    compilerOptions {
        freeCompilerArgs.add("-Xcontext-parameters")
    }
    sourceSets {
        commonMain.dependencies {
            api("org.jetbrains.compose.runtime:runtime:1.12.0")
            api("org.jetbrains.compose.foundation:foundation:1.12.0")
            api("org.jetbrains.compose.ui:ui:1.12.0")
            implementation("org.jetbrains.compose.material3:material3:1.9.0")
        }
        commonTest.dependencies { implementation(kotlin("test")) }
    }
}

上述声明由根构建的 OHOS 解析规则定向换成社区版本。实际编译命令:

./gradlew -Pohos=true :runtime:compileKotlinOhosArm64 :runtime:compileKotlinOhosX64

静态绘制和动画源码均进入双 OHOS 目标编译。动画是否在设备上连续播放,仍取决于业务提供的 Compose 宿主与帧时钟。

2.4 测试依赖与代码生成的处置

公共测试使用 kotlin("test"),本次在 JVM 侧执行 desktopTest;Native 示例把 src/skiaMain/kotlin 同时接入 desktopMain 与 ohosMain,使离屏绘制逻辑在桌面测试与设备库之间复用。

Native 示例同时应用本仓库的 SVG 插件,用原项目资源生成图标:

import io.github.fuyuz.svgicon.gradle.SvgIconExtension

extensions.configure<SvgIconExtension> {
    svgDir.set(rootProject.layout.projectDirectory.dir("sample/src/commonMain/svgicons"))
    packageName.set("io.github.fuyuz.svgicon.harmony.generated")
}

生成的 Icons.Check 是 Svg 值,可直接传给 SvgIcon(svg = Icons.Check, ...),也可传给离屏绘制入口。插件在构建机运行,不在 ArkTS 页面执行。validatePlugins 与生成图标的 PNG 测试一起验证了这条链路。

第 3 阶段:桥协议建模

3.1 JSON 契约
SVG XML + 输出像素尺寸 + ARGB tint
        ↓ N-API 参数检查
SvgIconRender / renderRequest
        ↓ XML 结构检查 + 原库 svg(source)
原库 Svg 模型 + Compose CanvasDrawScope
        ↓ Skia 离屏渲染
PNG → Base64 JSON
        ↓ ArkTS 解码
PixelMap → ArkUI Image

输入为三个独立参数,响应为 JSON 字符串:

调用 : renderSvg(svgXml, 96, signedArgb)
成功 : {"ok":true,"width":96,"height":96,"pngBase64":"..."}
失败 : {"ok":false,"error":"..."}

size 是 PNG 像素尺寸,合法范围为整数 24..1024;页面为便于交互,只开放 24..384。预览控件的布局尺寸与 PNG 实际像素数是两个独立参数。

tint 是有符号 ARGB Int32,例如深绿色 0xFF0F766E | 0。它用于 currentColor,SVG 中的显式颜色继续使用其自身值。

3.2 载荷为什么选择 PNG

ArkTS 可以直接显示系统 PixelMap,而 Kotlin/Native 可以复用 Compose 的 DrawScope 生成图片。桥因此传输稳定的 SVG 文本与 PNG 字节,不暴露 Svg 对象地址,也不要求 ArkTS 理解 Compose 图形类型。

离屏绘制的核心调用如下,完整实现负责关闭 Skia 资源:

val surface = Surface.makeRasterN32Premul(size, size)
surface.canvas.clear(0)
val density = Density(1f, 1f)
val measurer = TextMeasurer(
    defaultFontFamilyResolver = createFontFamilyResolver(),
    defaultDensity = density,
    defaultLayoutDirection = LayoutDirection.Ltr,
    cacheSize = 0,
)
CanvasDrawScope().draw(
    density, LayoutDirection.Ltr,
    surface.canvas.asComposeCanvas(),
    Size(size.toFloat(), size.toFloat())
) {
    drawSvgIcon(svg, Color(tint), textMeasurer = measurer)
}

这里使用 surface.canvas.asComposeCanvas() 适配 Skia Canvas;随后 makeImageSnapshot() 并编码为 PNG。Data、快照 Image 与 Surface 均在 finally 中关闭。文本测量器用于 DSL 绘制能力,不能据此推断 XML <text> 已被解析器支持。

3.3 错误通道

Kotlin 的 renderRequest 捕获解析、校验和绘制异常,生成 ok:false,并转义错误消息中的引号、反斜杠及 JSON 控制字符。N-API 入口则直接抛出参数异常;ArkTS 同时捕获异常与检查 ok。

输入限制包括:

检查位置约束
N-API参数类型、有限整数、尺寸范围、Int32 tint、1…131072 UTF-8 字节、无 NUL。
Kotlin最大 128 KiB、唯一且闭合的 svg 根、合法属性、最多 4096 个元素、最多 64 层嵌套。
绘制前有效且有限的 viewBox,宽高必须大于零。
能力边界拒绝已知未实现 XML 解析的 text/defs/use/clipPath/mask/gradient 等节点。

结构校验负责在调用原解析器前约束输入,几何、样式与 SVG 语义仍由原 runtime 实现。对于已知不支持的节点,返回明确原因,避免静默忽略后把空图当作成功。

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

4.1 三层桥接
ArkTS
  │ svg: string / size: number / tint: number
  ▼
C++ N-API entry(libsvgicon_napi.so)
  │ const char* / int32_t / int32_t
  ▼
Kotlin/Native C ABI(libohossvgicon.so)
  │ SvgIconRender
  ▼
UTF-8 JSON + DisposeString

N-API 包装库与 Kotlin/Native 库职责分开:前者注册 ArkTS 可调用方法并检查参数,后者执行 SVG 解析和绘制。ArkTS 应导入 libsvgicon_napi.so。

4.2 Kotlin/Native 导出函数
@file:OptIn(kotlin.experimental.ExperimentalNativeApi::class)

package example.nativeapp

@CName("SvgIconRender")
fun svgIconRender(svg: String, size: Int, tint: Int): String =
    renderRequest(svg, size, tint)
C ABI 符号ArkTS 方法覆盖内核
SvgIconRenderrenderSvg(svg, size, tint)结构检查、原库 XML 解析、Compose 静态绘制、PNG 编码和 JSON 响应。

头文件使用 Kotlin/Native 链接任务生成的 libohossvgicon_api.h,由同步任务复制到 CMake include 目录,保证 C ABI 声明与实际产物一致。

4.3 C++ N-API 方法分发
napi_property_descriptor descriptors[] = {
    {"renderSvg", nullptr, RenderSvg, nullptr, nullptr, nullptr,
     static_cast<napi_property_attributes>(napi_default | napi_enumerable), nullptr}
};

回调先验证 SVG 字符串、尺寸和 tint,再调用 KN 导出。Kotlin/Native 返回的字符串通过运行时释放:

const char *raw = SvgIconRender(svg.data(),
    static_cast<int32_t>(size), static_cast<int32_t>(tint));
// 完整实现先检查 symbols、raw 和 N-API 返回状态。
napi_value result = nullptr;
const napi_status status = napi_create_string_utf8(
    env, raw, NAPI_AUTO_LENGTH, &result);
symbols->DisposeString(raw);

symbols 来自 libohossvgicon_symbols()。字符串复制到 ArkTS 后必须 DisposeString,不能使用 C 的 free()。N-API 入口还拒绝嵌入 NUL 的 SVG,避免 C 字符串截断。

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

add_library(svgicon_napi SHARED napi_init.cpp)
target_include_directories(svgicon_napi PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/include)
target_link_libraries(svgicon_napi PUBLIC libace_napi.z.so ohossvgicon)

CMake 根据 OHOS_ARCH 选择 entry/libs/<ABI>,检查 KN 与 C++ runtime 是否存在、ELF machine 是否匹配,并设置 $ORIGIN RPATH。构建完成后把 KN 库与 libc++_shared.so 复制到 N-API 输出目录。

export const renderSvg: (svg: string, size: number, tint: number) => string;

双 ABI 库分别来自对应工具链;不能把 ARM64 的 libc++_shared.so 复制到 x86_64 目录。同步任务在链接完成后定位 cpf-llvm-19-*-21 工具链下的运行时,也支持 -PohosCxxRuntimeDir=<toolchain>/lib 指定来源。

第 5 阶段:ArkUI 宿主

5.1 注入参数和触发操作
import { renderSvg } from 'libsvgicon_napi.so';
import { image } from '@kit.ImageKit';
import { util } from '@kit.ArkTS';

// render() 中的关键步骤,完整异常与资源管理见 Index.ets。
const argb: number = 0xFF000000 | Number.parseInt(this.tint.substring(1), 16);
const parsed = JSON.parse(
  renderSvg(this.svgText, this.outputSize, argb)
) as RenderResult;
if (!parsed.ok || !parsed.pngBase64) {
  this.status = `渲染失败:${parsed.error ?? '未返回 PNG'}`;
  return;
}
const bytes = new util.Base64Helper().decodeSync(parsed.pngBase64);
const buffer = bytes.buffer.slice(bytes.byteOffset, bytes.byteOffset + bytes.byteLength);
const source = image.createImageSource(buffer);
const decoded = await source.createPixelMap();

这里先检查成功状态再解码图片,避免把错误 JSON 交给图片解码器。outputSize 表达输出像素数,也避开 ArkUI 自定义组件已有 size 方法的命名冲突。

5.2 页面交互

五个样例按钮自动换行排列。选中样例后填充对应源码并渲染,四色按钮控制 currentColor;文本域允许修改路径、viewBox、显式填色与样式,再点击渲染查看结果。

成功后状态行显示 渲染成功 · 宽 × 高 px · PNG 字节数 B。动画源样例的说明与页面底部文字都标明只显示初始 PNG;渐变样例展示不支持提示。桥参数范围与页面滑块范围分别维护,避免把交互上限误写为 Native 能力上限。

5.3 对比度和错误边界

选中样例使用深绿底白字,未选中项使用浅底深色文字;颜色按钮用边框标记当前 tint。预览区与源码区分开,错误原因显示在状态区。

图片解码是异步操作,页面以递增的 revision 标识请求:旧请求完成时若已过期,立即释放其 PixelMap;替换预览前释放旧图,完成解码后释放 ImageSource,页面退出时清理图片。Native 渲染接口本身仍是同步调用,async render() 只说明后续图片解码可异步,不代表绘制已在后台线程执行。

第 6 阶段:示例与验证

6.1 工程结构
example/
├── nativeApp/
│   ├── build.gradle.kts                      双 ABI sharedLib + SVG 生成 + syncHarmonyLibs
│   └── src/
│       ├── commonMain/kotlin/RenderRequest.kt 请求、结构检查、JSON 和 Base64
│       ├── skiaMain/kotlin/SnapshotRenderer.kt Compose / Skia 离屏 PNG
│       ├── ohosMain/kotlin/Bridge.kt          @CName("SvgIconRender")
│       ├── commonTest/kotlin/                请求边界与错误协议
│       └── desktopTest/kotlin/               PNG 像素、颜色、变换、DSL 与生成图标
└── harmonyApp/
    ├── build-profile.json5                   仓库保存未签名模板,本机由 DevEco 配置签名
    ├── AppScope/app.json5                    org.ohos.svgiconcompose
    └── entry/
        ├── build-profile.json5               CMake + arm64-v8a / x86_64
        ├── libs/{arm64-v8a,x86_64}/           Gradle 同步的 KN 与 C++ runtime
        └── src/main/
            ├── cpp/CMakeLists.txt            ELF 检查、IMPORTED 库与 $ORIGIN
            ├── cpp/napi_init.cpp             N-API 注册、参数检查和 DisposeString
            ├── cpp/include/libohossvgicon_api.h
            ├── cpp/types/libsvgicon_napi/    ArkTS 类型与本地包声明
            └── ets/pages/
                ├── Index.ets                渲染交互与 PixelMap 生命周期
                └── SvgSamples.ets           原项目样例与渐变边界输入
6.2 构建、签名和安装

从仓库根目录执行:

export JAVA_HOME="/path/to/jdk-21"
./gradlew -Pohos=true :runtime:desktopTest :example:nativeApp:desktopTest \
  :example:nativeApp:syncHarmonyLibs :gradle-plugin:validatePlugins

syncHarmonyLibs 依赖双 ABI 链接与头文件生成,复制 libohossvgicon.so、匹配架构的 libc++_shared.so 和生成头文件。只执行链接任务不会完成示例工程所需的同步步骤。

用 DevEco 打开 example/harmonyApp 并完成工程依赖同步后,可用已验证的环境方式构建 HAP:

export SVGICON_DEVECO_APP="/Applications/DevEco-Studio.app"
cd example/harmonyApp
env NODE_HOME="$SVGICON_DEVECO_APP/Contents/tools/node" \
    JAVA_HOME="$SVGICON_DEVECO_APP/Contents/jbr/Contents/Home" \
    DEVECO_SDK_HOME="$SVGICON_DEVECO_APP/Contents/sdk" \
    "$SVGICON_DEVECO_APP/Contents/tools/hvigor/bin/hvigorw" \
    --mode module -p product=default -p module=entry@default \
    -p buildMode=debug assembleHap --no-daemon

签名与真机操作由使用者手工完成。在 DevEco Studio 的 File → Project Structure → Signing Configs 配置自己的账号、证书与设备,仓库内保持未签名模板,不填写个人签名材料。

以下命令用于已生成匹配签名 HAP 后的手工安装与启动,不属于本文自动构建记录:

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.ohos.svgiconcompose -m entry

四、完整代码对照

4.1 整体架构

sample/src/commonMain/svgicons/*.svg
    │ Gradle 插件(开发机)
    └── Icons.xxx(Svg)───────────────┐
                                     │
SVG XML → 原库 svg(source) → Svg 模型 ─┤
                                     ▼
runtime/commonMain(路径、样式、变换、currentColor)
    │ drawSvgIcon / 原有 Compose 绘制
SnapshotRenderer(Skia Surface + CanvasDrawScope)
    │ PNG bytes
RenderRequest + Bridge
    │ SvgIconRender / JSON + Base64
libsvgicon_napi.so
    │ renderSvg(svg, size, tint)
Index.ets
    │ JSON parse → Base64 decode → PixelMap
    ▼
ArkUI 页面(尺寸、颜色、源码、预览与状态)

Icons.xxx 证明构建期生成可以接入公共模型;ArkTS 示例的运行输入是 XML 字符串。两种入口汇入同一绘制层,生成插件不会进入设备运行时。

4.2 文件清单

文件职责
settings.gradle.kts社区仓库、included build 与 ohos 属性分流。
根 build-ohos.gradle.ktsKotlin / Compose 编译器插件、适配版本与 OHOS 依赖规则。
runtime/build-ohos.gradle.ktsdesktop、双 OHOS 目标与公共 Compose 依赖。
gradle-plugin/settings.gradle.kts / build-ohos.gradle.kts独立插件构建的鸿蒙模式。
runtime/.../SvgDrawing.kt公开 DrawScope.drawSvgIcon,复用原绘制函数。
runtime/.../SvgDrawingContext.kt / SvgStyleApplier.kt独立保存并传播 currentColor。
runtime/.../SvgAnimationDrawing.kt动画绘制根上下文同步使用 tint 作为 currentColor。
runtime/.../CurrentColorTest.kt5 项颜色继承回归测试。
example/nativeApp/build.gradle.kts代码生成、sharedLib、共享离屏源码与双 ABI 同步。
nativeApp/.../RenderRequest.kt / SnapshotRenderer.kt / Bridge.kt校验、PNG、JSON 和 C ABI。
harmonyApp/.../cpp/napi_init.cpp / CMakeLists.txtN-API 参数转换、字符串释放、ELF 与链接检查。
harmonyApp/.../ets/pages/Index.ets / SvgSamples.etsArkUI 交互、资源释放和样例。
README.OpenHarmony.md / README.OpenHarmony_CN.md中英文源码集成、构建、能力边界和手工验收说明。

4.3 关键 API 对照

层次API作用
解析svg(source)将支持范围内的 SVG XML 解析为 Svg。
模型Svg 与 Kotlin DSL 节点构造可交给原库绘制的图形。
生成generateSvgIcons / Icons.Check把原始 SVG 资源生成为公共 Kotlin 源码。
ComposeSvgIcon(svg = ...)在 Compose 宿主中绘制静态图标。
ComposeAnimatedSvgIcon保留的动画入口,需要运行时宿主与帧时钟。
绘制DrawScope.drawSvgIcon在既有 DrawScope 中复用原库绘制。
NativeSvgIconRenderSVG、尺寸、ARGB 到 PNG JSON。
N-APIrenderSvg向 ArkTS 暴露同步渲染调用。
ArkTSBase64Helper / createImageSource / createPixelMap把 PNG 响应转换为页面图片。

4.4 ArkTS 与 Kotlin 的边界

const result = JSON.parse(
  renderSvg('<svg viewBox="0 0 24 24"><circle cx="12" cy="12" r="8"/></svg>',
    96, 0xFF0F766E | 0)
) as RenderResult;
// result.ok 为 true 时,解码 result.pngBase64,再创建 PixelMap。
validateDocument(source)
val document = svg(source)
val png = renderSvgPng(document, size, tint)
// 完整实现还检查字节数与 viewBox,并把 PNG 编码为 Base64 JSON。

边界上传输文本、整数与 JSON,Kotlin 对象及 Skia 资源由 Native 侧管理,ArkTS 管理自己的 PixelMap。输入中的显式 fill / stroke 与 tint 独立,不能把 tint 理解为覆盖整张图的强制单色滤镜。

五、关键决策说明

决策 1:真编译整库而不是提取零依赖内核

runtime 的模型和绘制已经使用 Compose 类型,鸿蒙生态也提供了对应制品。整体编译保留 XML、DSL、绘制及动画 API 的共同基础;离屏入口直接委托原函数,避免另写一套路径或样式渲染逻辑。公共代码的实际改动包括绘制入口与 currentColor 修复,并用回归测试验证。

决策 2:工具链采用 Kotlin 2.4.20-0.1.0-07,OHOS 依赖定向对齐

鸿蒙目标需要同一条可兼容的 Kotlin / Compose / Skiko 制品线。Gradle 8.14.1 与 JDK 21 是本项目已验证组合;桌面配置保留官方制品,OHOS 配置使用社区版本。runtime annotation 的独立版本、Material3 的独立编号以及 included build 的 Kotlin 版本都要分别处理。

决策 3:按审核目录分层,通过属性保留原构建入口

example/nativeApp 负责 Native 产物,example/harmonyApp 负责 CMake 与 ArkUI,符合已审核的交付层次。-Pohos=true 将鸿蒙工具链配置隔离到专用脚本,同时保留原有 sample 入口;独立 Gradle 插件也按属性选择构建。没有把 ArkUI、生成器与库本体混成一个模块。

决策 4:C ABI 返回 PNG JSON,明确静态快照边界

PNG 让 ArkTS 直接显示由原库实际绘制的结果,并通过 width / height 与像素测试验证输出。Base64 JSON 简化了示例桥接,也带来编码、字符串复制与解码开销;当前定位是有界的图标快照调用。动画播放需要持续帧调度,当前接口只返回一张图,因此页面将动画源明确标为快照。

决策 5:desktopTest 交叉验证 + PNG 像素断言

仅验证 KLIB 编译无法说明图片颜色和几何正确。runtime 的原有测试、新增颜色回归,以及共享离屏实现的 PNG 测试覆盖不同层次:检查图像尺寸、透明背景、显式填色、变换、DSL 剪裁与文字、生成图标及 JSON 响应。JVM 交叉验证覆盖公共逻辑,真机画面补充设备显示证据,两者的验证范围分别记录。

六、测试与验证

6.1 测试环境

构建验证于 2026-10-21 在 macOS arm64、JDK 21、Gradle 8.14.1、HarmonyOS Kotlin 2.4.20-0.1.0-07 与 DevEco SDK 26.0.0.105 环境完成。Hvigor 版本为 6.26.4,本次 HAP 实际 compile / target API 为 26。

签名和真机操作由使用者手工进行,本文采用其提供的运行效果图。图片未提供可核对的设备型号、系统版本和完整操作日志,因此设备兼容范围只记录到已展示的功能,不额外推断 API 18 或模拟器验收结果。

6.2 JVM 交叉验证

./gradlew -Pohos=true :runtime:desktopTest :example:nativeApp:desktopTest

测试报告共 830 项通过,0 失败、0 错误、0 跳过:

测试范围用例数钉住什么
runtime 原有测试815原有 SVG 模型、XML 解析、路径、样式、变换和动画相关公共逻辑。
CurrentColorTest5无父描边、显式父描边、嵌套禁用绘制、透明度与原构造方式兼容。
RenderRequestTest5尺寸与字节边界、XML 结构、层数与元素数、不支持节点、JSON 转义。
SnapshotRendererTest5PNG 尺寸及像素、颜色与变换、DSL 剪裁和文字、生成图标、可解码响应。

其中 currentColor 修复来自真实绘制问题:旧实现从父级 strokeColor 取色,父级未描边时子级填色可能透明,父级显式描边时子级 currentColor 可能跟随错误颜色。现由根上下文写入 tint,独立保存 currentColor 并向子级传播;子级 fill / stroke 的 Color.Unspecified 从该值解析。静态与动画绘制入口同步修正。

这些测试在 JVM 执行;双 OHOS 目标另行验证编译与链接,不能把 830 项桌面测试写成 830 项设备测试。

6.3 HAP 验证

:runtime:compileKotlinOhosArm64 / OhosX64: BUILD SUCCESSFUL
:example:nativeApp:linkDebugSharedOhosArm64 / OhosX64: BUILD SUCCESSFUL
:example:nativeApp:syncHarmonyLibs: 完成双 ABI 库与生成头同步
:gradle-plugin:validatePlugins: BUILD SUCCESSFUL
Gradle configuration cache: 已存储并复用
DevEco / Hvigor assembleHap: BUILD SUCCESSFUL
HAP 内双 ABI ELF 架构与库依赖检查: 通过
KN 全局符号 SvgIconRender: 已验证导出
N-API NEEDED libohossvgicon.so 与 $ORIGIN: 已验证

已检查的未签名产物为:

example/harmonyApp/entry/build/default/outputs/default/entry-default-unsigned.hap
63,821,488 bytes(约 60.9 MiB)

HAP 包含双 ABI 各三件套,共 6 个 .so:libsvgicon_napi.so、libohossvgicon.so 和 libc++_shared.so。检查确认架构匹配,N-API 按库名依赖 KN 动态库,RPATH 未夹带本机绝对路径。未签名包检查没有签名条目;该构建记录不包含自动签名、自动安装或自动真机运行。

6.4 功能用例

下面按页面能力列出验收项,并区分现有证据:

  1. 静态图标:页面进入“静态图标”,显示 check.svg 源码、96 px 与深绿 tint,预览可见深绿勾形;使用者提供的效果图已展示该场景。
  2. 尺寸调节:修改尺寸后点击渲染,核对状态行的 PNG 宽高及图像比例;已有桌面尺寸断言,其他设备尺寸交互需继续手工验收。
  3. 颜色继承:切换 tint,核对 currentColor 变化,显式填色保持;已有回归与像素测试,完整设备颜色组合需继续验收。
  4. 样式与继承:切换 paint-order-demo.svg,比较填充和描边的覆盖关系;当前图片未覆盖此页。
  5. 路径与圆弧:切换 heart-pulse.svg,核对静态路径和比例;当前图片未覆盖此页。
  6. 渐变边界:应提示不支持,不产生“渐变绘制成功”的状态;请求层已有拒绝用例,设备提示需继续核对。
  7. 动画源快照:loader.svg 应显示初始形态,不连续旋转;该项用于核对快照语义,Compose 动画宿主需独立验收。
  8. 异常与资源:非法 XML、连续切换和退出页面后返回,应能恢复操作;协议边界已有测试,设备资源稳定性仍需多轮操作观察。

6.5 验证结论

公共 runtime、代码生成、双 OHOS 目标、Native 动态库、C ABI、N-API 与未签名 HAP 已完成构建及产物检查;830 项 JVM 测试通过。使用者提供的真机效果图补充了基础 check.svg 图标展示证据。

其余样例交互、连续渲染稳定性、模拟器运行、API 18 设备兼容性与 Compose 动画宿主不在当前图片的证明范围内,仍按对应入口逐项验收。

七、运行效果

7.1 真机截图

在这里插入图片描述

7.2 命令速查

git clone https://atomgit.com/oh-tpc/svg-icon-compose.git
cd svg-icon-compose
export JAVA_HOME="/path/to/jdk-21"

# 公共 runtime 双 OHOS 编译
./gradlew -Pohos=true :runtime:compileKotlinOhosArm64 :runtime:compileKotlinOhosX64

# 830 项桌面测试、双 ABI 构建同步与 Gradle 插件检查
./gradlew -Pohos=true :runtime:desktopTest :example:nativeApp:desktopTest \
  :example:nativeApp:syncHarmonyLibs :gradle-plugin:validatePlugins

# DevEco 工程完成依赖同步后构建 HAP
export SVGICON_DEVECO_APP="/Applications/DevEco-Studio.app"
cd example/harmonyApp
env NODE_HOME="$SVGICON_DEVECO_APP/Contents/tools/node" \
    JAVA_HOME="$SVGICON_DEVECO_APP/Contents/jbr/Contents/Home" \
    DEVECO_SDK_HOME="$SVGICON_DEVECO_APP/Contents/sdk" \
    "$SVGICON_DEVECO_APP/Contents/tools/hvigor/bin/hvigorw" \
    --mode module -p product=default -p module=entry@default \
    -p buildMode=debug assembleHap --no-daemon

# 手工完成匹配设备与 bundle 的签名后安装
hdc install -r entry/build/default/outputs/default/entry-default-signed.hap
hdc shell aa start -a EntryAbility -b org.ohos.svgiconcompose -m entry

八、遗留问题与改进方向

8.1 踩坑复盘

  1. ArkUI 能显示图片,只能说明宿主页面可运行;需要让公共 runtime 真正编译到 OHOS,并通过原库绘制生成图像,才能形成库适配链路。
  2. 主构建与 gradle-plugin included build 独立解析插件,两边都需要鸿蒙仓库、构建脚本和一致的 Kotlin 版本。
  3. Kotlin 与 Compose 编译器插件使用同一条鸿蒙版本,Compose、协程、Skiko 在 OHOS 配置中对齐,不能只改顶层 UI 版本。
  4. runtime annotation 有独立可用版本;对所有 Compose 构件无差别套用单一版本,可能解析到不存在的制品。以 -internal 结尾的 group 也要避开通用覆盖规则。
  5. Material3 与 Compose UI 使用独立版本序列,桌面配置必须填写真实可用的 Material3 版本。
  6. Gradle 8.14.1 使用 JDK 21;DevEco 的 JDK 25 与本工程 Gradle 环境要分开配置。
  7. Skia Canvas 通过 asComposeCanvas() 接入 Compose;离屏渲染还要准备正确的 Density、LayoutDirection 与文字测量器。
  8. currentColor 不能从父级显式描边色推导,应独立保存 tint 并传播;根无描边、嵌套样式与透明度都需回归。
  9. XML 解析和 DSL 绘制有不同覆盖面,渐变模型存在也不代表渐变已能绘制;明确拒绝已知未实现 XML 比返回空图更便于定位问题。
  10. Native 链接与示例库同步是不同步骤,示例构建前应执行 syncHarmonyLibs,同时准备 KN、C++ runtime 与生成头文件。
  11. 双 ABI 的 ELF machine、NEEDED 与 $ORIGIN 要检查,避免混用架构或将开发机路径打入 HAP。
  12. KN 返回字符串必须 DisposeString;Skia image/data/surface、ArkTS PixelMap/ImageSource 也各自需要释放。
  13. ArkUI 组件状态使用 outputSize,避免与组件已有 size 方法发生冲突。
  14. async 包装不会自动把同步 N-API 渲染移到后台;尺寸与输入长度限制有助于约束示例负载,较大图形仍需评估线程方案。
  15. 签名材料只在本机 DevEco 配置,文档与提交保留未签名模板;构建成功、效果图与完整功能验收分别记录。

8.2 已知问题

  • ArkTS 桥仅输出静态 PNG,不提供 Compose Composition、帧时钟或动画播放接口;动画源码双目标编译通过不等同于设备动画验收通过。
  • 上游 XML 解析器没有完整实现文字、定义引用、剪裁、遮罩和渐变等节点,快照桥对已知缺失项明确拒绝;DSL 的文字和剪裁已有桌面像素测试,但不因此扩大 XML 支持范围。
  • 渐变只有相关模型定义,实际绘制尚需实现与验证;不能把“切换 DSL”作为已经支持渐变的解决方案。
  • 当前渲染调用同步执行,Base64 JSON 会增加内存与复制开销;不适合直接当作高帧率图像通道。
  • 原库也不是完整浏览器 SVG 引擎;复杂外部资源、滤镜、字体和部分描边语义需要逐项核对。
  • API 18 仅有兼容配置,x86_64 已验证编译打包,尚无对应设备运行证据。
  • 尚未发布并验收独立 OHOS Maven 制品;当前以仓库源码集成为准,保留 maven-publish 配置不代表公开发布已经完成。

8.3 未来优化方向

  • 引入 N-API 异步工作队列,评估缓存与输出复用,降低连续渲染对 UI 线程的影响;
  • 评估 PNG 二进制缓冲区或文件通道,减少 Base64 编解码与字符串复制;
  • 按 XML 节点逐步补齐解析与绘制支持,对文字、引用、剪裁及渐变分别建立像素回归;
  • 增加真正的 Compose 宿主示例,独立验收 SvgIcon、AnimatedSvgIcon 与动画控制;
  • 在 CI 中加入 OHOS 编译、生成插件检查、PNG 像素测试、双 ABI ELF 与 HAP 检查;
  • 补充设备型号、系统版本、模拟器、最低兼容 API 与连续操作的验收记录,再评估 Maven / HAR 交付形态。

九、总结

9.1 核心难点回顾

原有 SVG runtime(模型、XML、DSL、Compose 绘制与动画)
    → 确认鸿蒙 Kotlin / Compose / coroutines / Skiko 制品线
    → Kotlin 2.4.20-0.1.0-07 + Gradle 8.14.1 + JDK 21
    → 主构建与 included build 属性分流,保留原构建入口
    → runtime 双 OHOS KLIB + SVG 代码生成兼容
    → currentColor 修复 + Compose / Skia 离屏绘制
    → 双 ABI sharedLib + @CName("SvgIconRender")
    → C++ N-API 参数检查与 DisposeString
    → ArkTS Base64 / PixelMap 与资源管理
    → HAP 检查 + 手工签名验收 + 基础图标运行效果

9.2 封装层次

  • 资源层:原项目 SVG 文件与 Gradle 生成的 Icons.xxx;
  • runtime 层:公共模型、解析、样式、变换、静态绘制与保留的动画 API;
  • 离屏层:Compose CanvasDrawScope 与 Skia Surface,生成真实 PNG;
  • Native 层:双 ABI 动态库、输入边界、PNG Base64 JSON 与 C ABI;
  • N-API 层:ArkTS 参数校验、C 字符串转换与运行时内存释放;
  • ArkTS 层:样例、尺寸、颜色、源码编辑、解码、预览和错误提示;
  • DevEco 层:CMake、资源、HAP、使用者手工签名与设备运行。

9.3 三条经验

  1. 先盘点类型与依赖再选路线:这类 SVG 库的公共模型依赖 Compose,鸿蒙制品可用时,整体编译和复用原绘制入口能够保留 API 与图形语义。
  2. 构建兼容要覆盖独立插件与传递依赖:主工程、included build、runtime annotation、Material3 和 Skiko 各有自己的约束,单点换版本无法替代完整矩阵验证。
  3. 图形库需要图像证据:公共逻辑测试、PNG 像素断言、设备截图与动画验收回答不同问题。区分解析、绘制、快照和连续动画的覆盖面,才能准确描述适配成果。

9.4 适配成果

当前适配已完成:

  • SVG Icon Compose 0.1.1-ohos.1 的公共 runtime 双 OHOS 编译,保留静态、DSL、解析与动画源码;
  • 鸿蒙 Kotlin / Compose / coroutines / Skiko 依赖对齐,以及 -Pohos=true 构建分流;
  • 独立 Gradle 插件兼容、原项目 SVG 生成与 Icons.Check 实际渲染测试;
  • currentColor 绘制修复、5 项回归,以及共享 Compose / Skia 离屏 PNG 实现;
  • 一个 C ABI、C++ N-API 包装、双 ABI 库和生成头同步;
  • ArkUI 五类样例、源码编辑、尺寸与颜色控制、PNG 预览及资源管理;
  • 830 项 JVM 测试通过、双 ABI 编译链接与未签名 HAP 检查,HAP 约 60.9 MiB;
  • 使用者提供的基础静态图标真机效果图、中英文 OpenHarmony README 与本文本地图片。

适配过程 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开发者与企业提供一个覆盖软硬件全栈、一站式门户。

更多推荐