开源鸿蒙平台 CMP 三方库「smooth-rounded-corner-shape」适配全流程
本文记录
smooth-rounded-corner-shape接入 OpenHarmony 的完整过程,覆盖 Compose 平滑圆角库盘点、鸿蒙 Kotlin 工具链选型、androidx.graphics:graphics-shapes几何内核移植、ohosArm64/ohosX64目标、Kotlin/Native 动态库、C ABI/N-API 桥接、ArkUI 真机页面、HAP 构建和真机验收。
smooth-rounded-corner-shape是一个极小的 Compose Multiplatform 平滑圆角库:SmoothRoundedCornerShape/AbsoluteSmoothRoundedCornerShape/Path.smoothRoundedRectangle在RoundedCornerShape之上增加smoothing参数(0~1,约等于 Figma 的Corner smoothing百分比,0.6f即 iOS 同款连续圆角),自身不含任何平滑数学——每个角的三次贝塞尔全部由 AndroidX 的graphics-shapes(RoundedPolygon+CornerRounding)算出。本次适配与社区 KStore 路线(“真编译整库 + 补一个 actual”)的方向相反:graphics-shapes与compose.ui/compose.foundation一样没有 ohos 制品,Compose 面在当前工具链下编不进鸿蒙,因此把上游实际依赖的 graphics-shapes 1.0.1 的RoundedPolygon(vertices, perVertexRounding)管线逐表达式移植成纯 Kotlin(不升版本、不改数学、不重写算法),上游 4 个 Compose 文件原文隔离到src/composeMain等待制品,移植正确性由 21 项 JVM 对拍用例对着真实的 graphics-shapes 1.0.1 逐 float 验证——ArkUI 页面作为验收宿主,交付物是矢量 SVG 路径。
项目地址: AtomGit/oh-tpc/smooth-rounded-corner-shape
上游仓库: iamcalledrob/smooth-rounded-corner-shape
开发工具: 华为云码道
环境安装: 环境安装指南
一、背景
1.1 为什么做 OpenHarmony 平台 KMP/CMP 适配
smooth-rounded-corner-shape 解决一个具体问题:RoundedCornerShape 的圆角在直线与圆弧相切处曲率突变,视觉上"生硬";超椭圆(squircle)过渡没有这个切点。iOS 的连续圆角、Figma 的 Corner smoothing 滑杆都是这个思路。上游把 smoothing 接进 Compose 的 Shape 体系:方向敏感的 SmoothRoundedCornerShape(CornerBasedShape 子类,跟随 LayoutDirection)、绝对方向的 AbsoluteSmoothRoundedCornerShape、直接产出 Path 的 Path.smoothRoundedRectangle。KMP 项目把这些能力放在 commonMain,由 Android、桌面共享。OpenHarmony 应用同样需要这套圆角能力,但上游构建(Kotlin 1.9.23 / Compose 1.6.11 / AGP 8.2.0)不会生成 OpenHarmony 变体。
如果只把示例页面改成 ArkTS,页面可以画一个普通圆角矩形,却无法证明上游的平滑数学在 OpenHarmony ARM64 设备上完成编译、链接和执行。适配需要同时验证几何内核、交叉编译工具链、Native 动态库、C ABI、N-API、ArkUI 页面和 HAP 交付链路。
| 障碍 | 具体问题 |
|---|---|
| 目标缺失 | 必须增加 ohosArm64() / ohosX64() 才能生成 OpenHarmony KLIB。 |
| 工具链不一致 | 上游 Kotlin 1.9.23 + AGP 8.2.0 + vanniktech 发布插件与鸿蒙工具链无法共存于一个 Gradle 构建,根构建必须整体替换。 |
| 依赖矩阵 | 平滑数学全部在 androidx.graphics:graphics-shapes,Compose 面还依赖 compose.ui / compose.foundation——三者都没有 ohos 变体,一个都解析不到。 |
| 语言边界不同 | ArkTS 不能直接持有 Kotlin 对象或调用 @CName 符号,需要 C ABI、C++ N-API 和 JSON。 |
| 几何语义跨不了桥 | CornerBasedShape 的 start/end 方向语义、CornerSize(percent) 解析规则是 Compose 层概念,C ABI 只能同步一来一回。 |
| 交付链路复杂 | .so、CMake、N-API、HAP、签名和 hdc 都要单独验收。 |
因此,本项目把边界放在:graphics-shapes 1.0.1 几何内核的逐表达式移植、根构建替换(cmp_qrose 模式)和 C ABI/N-API 桥接层。圆角的方向/百分比语义完全在桥接层对齐 Compose 行为,ArkTS 只负责调用 Native、解析 JSON 和用 Path().commands() 画结果。
1.2 库提供的能力
Demo 提供一个真实的几何渲染会话,对应桥接的全部入口:
- 平滑角渲染:
SmoothCornerGeometry.cubics(...)按 smoothing / 四角半径 / 宽高产出闭合轮廓,svgPath摊平成M ... C ... Z绝对路径,ArkUIPath().commands()直接消费——矢量无损、分辨率无关; - 平滑档位:smoothing 滑杆 0~1 实时重绘,
0退化为纯圆角、0.6是 Figma 60% / iOS 档、1两翼曲线相接; - 对照轮廓:同一参数但 smoothing=0 的红色描边叠在平滑轮廓之下,切点差异一目了然;
- RTL 语义:开关镜像 start/end 四角,对应上游
SmoothRoundedCornerShape的LayoutDirection.Rtl行为; - 百分比语义:
percent=true时圆角按min(宽, 高)的百分比解析,对应 ComposeCornerSize(percent); - 版本握手:
smoothCornerVersion()返回 fork 版本号1.0.4-ohos.1,来自真实 Native 调用而非静态文字。
响应包含:
| 字段 | 作用 |
|---|---|
ok | 操作是否成功,失败时 error 带原文。 |
width / height / smoothing / rtl | 回显本次几何参数。 |
cubicCount | 三次贝塞尔段数:全直角 = 4,四角圆角(smoothing=0)= 9,四角全平滑 = 17。 |
path | 绝对坐标 SVG 路径字符串,坐标 3 位小数。 |
内核侧完整能力(RoundedCorner 切割量计算、侧翼曲线、圆弧 cubic 近似、零长度段丢弃、首角圆弧对半拆分闭合)全部随移植进入 .so,JVM 侧保留 21 项对拍用例对着真实 graphics-shapes 1.0.1 逐 float 验证,证明 ohos .so 里执行的几何没有漂移。
1.3 实现适配
| 维度 | 要求 |
|---|---|
| 内核移植 | graphics-shapes 1.0.1 的 RoundedPolygon 管线逐表达式移植,魔数、运算顺序、分支顺序全部保留原样;不升 1.1.0(对拍基准必须等于上游声明的依赖版本)。 |
| 上游保真 | 4 个 Compose 文件 + 2 个 actual 原文移入 src/composeMain(不注册源码集),git 识别为 100% 重命名,零修改留档。 |
| 平台目标 | 根构建直接声明 ohosArm64 + ohosX64,保留 jvm() 作对拍车道,库模块出 KLIB,example/nativeApp 出双 ABI 动态库。 |
| 构建隔离 | 根构建替换为极简 ohos 构建(cmp_qrose 模式);AGP / Compose 插件 / vanniktech 发布 / signing 全部剥离——本 fork 不发 Maven Central。 |
| 桥接稳定 | 使用 2 个 C ABI 和 UTF-8 JSON,不传递 Kotlin 对象地址;异常永远不跨 FFI。 |
| UI 完整 | 双页签覆盖全部桥接参数:宽高、smoothing、四角联动/独立、RTL、百分比、色板、对照轮廓和版本握手。 |
| 仓库规范 | 项目说明、文章、效果图和源码链接统一使用 AtomGit。 |
本项目的 ArkUI 页面不是 Compose Multiplatform UI 实现。上游的公共面就是 Compose
Shape,当前工具链编不进鸿蒙,所以鸿蒙侧的公共面是几何内核本身;ArkTS Demo 只是把桥暴露的几何能力画出来。
二、实现路线图
第 1 阶段:项目初始化 ── 盘点模块与依赖边界、确认 graphics-shapes 无 ohos 制品、确定交付物
第 2 阶段:目标与依赖打通 ── 根构建替换、eazytec Maven 置前、Kotlin 2.2.21-1.0.0 钉版、源码集拆分与 composeMain 隔离
第 3 阶段:内核移植 ── graphics-shapes 1.0.1 逐表达式移植、公共入口 SmoothCornerGeometry、JVM 对拍
第 4 阶段:桥协议与原生桥接 ── JSON 契约、@CName C ABI、C++ N-API、参数校验和内存释放
第 5 阶段:ArkUI 宿主 ── 双页签、滑杆实时重绘、红色对照轮廓和版本握手
第 6 阶段:示例与验证 ── HAP、签名、设备安装、真机运行和效果图
三、逐步实现过程
第 1 阶段:项目初始化
1.1 工程边界
smooth-rounded-corner-shape/ 库模块(进 ohos 构建,jvm + ohosArm64 + ohosX64)
src/commonMain/.../geoshape/ 新增:GraphicsShapesPort.kt(内核移植)+ SmoothCornerGeometry.kt(公共入口)
src/composeMain/ 上游 4 个 Compose 文件 + 2 个 actual 原文,不注册源码集
src/jvmTest/.../geoshape/ 新增:21 项对拍用例(对真实 graphics-shapes 1.0.1)
example/nativeApp/ Kotlin/Native 动态库(Bridge + MiniJson)
example/harmonyApp/ DevEco Stage、CMake N-API 和 ArkTS 双页签
README.OpenHarmony.md / _CN.md 鸿蒙适配说明(中英双份)
README.upstream.md 上游原文保存
doc/ 本文与效果图
构建拓扑走 cmp_qrose 模式(根构建即 ohos 构建):settings.gradle.kts 只 include :smooth-rounded-corner-shape 与 :example:nativeApp,rootProject.name = "ohos_smooth_corner_shape"。上游根构建里的 vanniktech 发布、signing、AGP、Compose 插件全部随根构建替换剥离——本 fork 不发 Maven Central,上游的 Android / Desktop 变体继续用上游 1.0.4。
1.2 工具链矩阵
| 项目 | 配置 |
|---|---|
| Kotlin Multiplatform | HarmonyOS Kotlin 2.2.21-1.0.0(eazytec Maven,与已过审的 cmp_qrose 同款钉法) |
| JDK | 21(Temurin,org.gradle.java.home 写进 gradle.properties) |
| OpenHarmony 目标 | ohosArm64 + ohosX64(jvm() 保留作对拍车道) |
| Gradle | 8.14.1(wrapper 随钉法调整;2.2.21 线使用 8.x) |
| 几何依赖 | androidx.graphics:graphics-shapes:1.0.1——上游钉的版本,仅 jvmTest 对拍解析 |
| DevEco product | 5.1.0(18) 兼容 |
| DevEco Studio | 5.1.0 Release |
| ABI | arm64-v8a + x86_64 |
| Bundle | org.terminator.ohos.smoothcorner |
1.3 源码依赖度盘点
| 模块 | 文件 | 依赖形态 | 处置 |
|---|---|---|---|
| 上游 commonMain | 4 | compose.foundation.shape.* + compose.ui.* + graphics-shapes.*,零平台专属 API | 移入 composeMain,不编译(等 ohos 制品) |
| 上游 androidMain / nonAndroidMain | 2 | toComposePath actual(android.graphics.Path / Compose Path 摊平) | 随 actual 移入 composeMain 留档 |
| graphics-shapes 1.0.1 | 6 | 纯 Kotlin 数学(无 Android 依赖),Point 是 androidx.collection.FloatFloatPair 的 typealias | 逐表达式移植成零依赖纯 Kotlin(见第 3 阶段) |
值得说明的是 graphics-shapes 的依赖面:它的 commonMain 本身是纯 Kotlin 数学库,唯一的"外部依赖"是 androidx.collection 的 FloatFloatPair(被 typealias 成内部 Point)和 MutableFloatList——而 androidx.collection 同样没有 ohos 变体。移植时用零依赖的 Pt 类与 FloatArray 等价替换(只影响存储,不影响算术),这是移植能做到"零第三方依赖"的原因。
1.4 页面边界
标题区 SmoothRoundedCornerShape · OpenHarmony / 版本握手行(Kotlin/Native 内核 · 1.0.4-ohos.1)
Tab 切换 形状预览 / 关于
预览区 Stack:平滑轮廓(填充或描边)+ 红色对照轮廓(smoothing=0,可开关)
参数区 宽度 / 高度 / 平滑 smoothing 滑杆,四角联动开关 + 圆角半径滑杆(联动)或四角独立滑杆
语义开关 RTL(镜像 start/end)、百分比圆角(CornerSize %)、填充/描边、对照轮廓
色板区 五套前景/背景配色 + 重置按钮
状态行 最近一次渲染结果(如"17 段三次贝塞尔 · 300×300 · smoothing=0.6")
关于区 版本握手、库简介、调用链、桥接协议、验收要点、仓库与社区链接
页面启动时先调 smoothCornerVersion() 做握手,再以默认参数(300×300、r=50、smoothing=0.6)发起首次渲染。
第 2 阶段:目标与依赖打通
2.1 制品线验证(本适配的成败手)
KStore 那类存储库的适配首先验证"社区 Maven 上有没有 ohos 制品",结论是"有,真编译整库"。本库验证的结论恰好相反,三个关键对象逐一查证:
androidx.graphics:graphics-shapes → Maven Central / google() 只有 android + jvm 变体,无 ohos
androidx.compose.ui / compose.foundation → 无 ohos 变体;社区 CMP 1.9.2 是应用侧独立发行版,
不是能从 Maven 消费的 Gradle KMP 依赖
androidx.collection(Point 的老家) → 多平台 native 变体不含 ohos
结论:上游的公共 API 在当前工具链下编不进鸿蒙,但它的数学内核是纯 Kotlin。于是路线确定——把数学搬进 commonMain(移植),把 Compose 面原样封存(composeMain),鸿蒙交付几何。这与 KStore"真编译整库"不矛盾:路线选择必须基于当天的制品可达性,存储库的制品线在,图形库的制品线不在。
2.2 工具链钉版(为什么是 2.2.21-1.0.0 + Gradle 8.14.1)
本库 commonMain 移植后没有任何外部依赖,没有 KStore 那种"kotlinx 制品线 stdlib 元数据倒逼 Kotlin 升版"的约束。工具链因此跟随社区已过审的 cmp_qrose 钉法:Kotlin 钉 2.2.21-1.0.0,Gradle Wrapper 钉 8.14.1,JDK 钉 21。两条工具链线的经验不能互相套用:2.4.20 线(KStore / continuous-corner 用)支持 Gradle 9.x,2.2.21 线用 8.14.1;本仓在 2.2.21 线上从零构建,直接落 8.14.1。
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_smooth_corner_shape"
include(":smooth-rounded-corner-shape")
include(":example:nativeApp")
2.3 源码集拆分与 composeMain 隔离
这是本库特有的一刀:continuous-corner 只挪"依赖 graphics-shapes 的实现文件",本库则是整个上游公共 API都依赖 Compose 面——4 个 commonMain 文件(SmoothRoundedCornerShape、AbsoluteSmoothRoundedCornerShape、Path.smoothRoundedRectangle、RoundedPolygon.toComposePath expect)加 2 个 actual(androidMain / nonAndroidMain)全部原封不动移入 src/composeMain 的 kotlin/、androidMain/kotlin/、nonAndroidMain/kotlin/ 三个子目录,不注册为源码集,配一篇 README.md 说明隔离原因与接回条件。挪完之后 commonMain 清零,新的公共面就是移植内核的 SmoothCornerGeometry。git 对这批文件识别为 100% 重命名——上游保真是可审计的。
库模块构建收敛为纯 KMP:
kotlin {
applyDefaultHierarchyTemplate()
jvmToolchain(21)
jvm() // 对拍车道,不是为了跑 Compose
ohosArm64()
ohosX64()
sourceSets {
val jvmTest by getting {
dependencies {
implementation(kotlin("test"))
implementation(libs.androidx.graphics.shapes) // 真实 1.0.1,仅测试解析
implementation(libs.androidx.collection)
}
}
}
}
实测:
./gradlew :smooth-rounded-corner-shape:compileKotlinOhosArm64 :smooth-rounded-corner-shape:compileKotlinOhosX64
# BUILD SUCCESSFUL,双架构 klib
2.4 测试的处置:对拍统一 JVM
与 KStore 相同的两条纪律:测试统一在 JVM 跑,根构建全局禁用 ohos 测试任务:
subprojects {
tasks.matching { it.name.contains("Ohos", ignoreCase = true) && it.name.contains("Test") }
.configureEach { enabled = false }
}
不同之处在测试的来源:上游库没有自带测试,本 fork 的对拍用例是新写的——它们不是"上游测试照跑",而是把真实 graphics-shapes 当作参照实现,验证移植内核与它逐 float 一致(见第 3 阶段)。
第 3 阶段:内核移植(本适配的核心)
3.1 移植面收窄
上游对 graphics-shapes 的调用只有一处、形态非常收敛:
RoundedPolygon(
vertices = floatArrayOf(0,0, w,0, w,h, 0,h),
perVertexRounding = listOf(CornerRounding(topLeft, smoothing), ...同四角...)
).cubics // → List<Cubic> → Compose Path
移植面因此只需要:RoundedPolygon(FloatArray, List<CornerRounding>) 构造器与 .cubics 的输出逐位一致。GraphicsShapesPort.kt(全 internal)覆盖以下管线:
RoundedCorner:角切割量expectedRoundCut = radius·(cosA+1)/sinA;空间不足时先保双侧圆角再分平滑余量的三级cutAdjusts分配;computeFlankingCurve侧翼曲线(圆切线与直边求交作锚点、2/3 取点);Cubic.circularArc圆弧 k 常量近似;两侧独立的actualSmoothing线性缩放。Cubic:8 浮点平铺存储、split(首角圆弧对半拆分闭合用)、reverse、straightLine、circularArc、zeroLength。Point代数:零依赖Pt替换FloatFloatPairtypealias,只保留管线用到的运算。Feature(Corner/Edge)与cubics构建器:零长度段丢弃并把锚点前移、首特征三段时把中段圆弧split(0.5f)拆到起点两侧、末段复制并吸附首锚点保证精确闭合——这三条直接决定段数(全平滑矩形是 17 段不是 16 段),是"看起来对"和"逐位对"的分水岭。- 不移植:
Morph、transformed/normalized/ bounds、numVertices工厂、MutableCubic、调试日志。
3.2 移植的求值顺序纪律
对拍能逐 float 通过的前提,是移植不只是"算法一样",而是表达式一样。浮点运算不满足结合律,(a+b)+c ≠ a+(b+c) 在第 15 位有效数字上的差异会经过侧翼曲线的乘除放大到 1e-4 量级。移植遵守三条纪律:
- 常量原样搬运:
4f/3f、0.999f、1e-3(sinAngle 阈值)、2/3取点比例全部保留原字面量与原类型,不"顺手化简"; - 运算顺序原样保留:
circularArc里distance(...) * 4f / 3f * (sqrt(...) - sqrt(...)) / (1 - cosa) * (±1f)的乘除链不重排; - 控制流原样保留:
cutAdjusts的三级分配与calculateActualSmoothingValue的两段线性缩放,分支顺序与上游一致——极端参数(超大半径、退化小尺寸)下分支顺序决定走哪条数值路径。
文件头按 Apache-2.0 声明来源与版权;公共面只有 SmoothCornerGeometry.cubics / percentRadius / svgPath 与数据载体 ShapeCubic。
3.3 方向与百分比语义放在哪
上游有两层语义,移植内核不掺和,由桥接层对齐:
- RTL:
SmoothRoundedCornerShape.createOutline在LayoutDirection.Rtl时交换 start/end。桥接层按同一规则交换后再调内核,与 Compose 行为逐角一致; - 百分比:Compose 的
CornerSize(percent)按min(宽, 高)解析。桥接层percent=true时走percentRadius。
内核只做纯几何,Composable 语义的任何后续修正都不会碰移植代码。
第 4 阶段:原生桥接(技术难点)
4.1 三层桥接
ArkTS
│ JSON string
▼
C++ N-API entry(libsmoothcorner_napi.so)
│ const char*
▼
Kotlin/Native C ABI(libohossmoothcorner.so)
│ SmoothCornerRender / SmoothCornerVersion
▼
UTF-8 JSON + DisposeString
4.2 Kotlin/Native 导出函数
@CName("SmoothCornerRender")
fun smoothCornerRender(request: String): String // 几何参数一口进,SVG 路径一出
@CName("SmoothCornerVersion")
fun smoothCornerVersion(): String = "1.0.4-ohos.1" // 版本握手
| C ABI 符号 | ArkTS 方法 | 覆盖内核 |
|---|---|---|
SmoothCornerRender | smoothCornerRender(req) | SmoothCornerGeometry.cubics + RTL/percent 语义对齐 + svgPath |
SmoothCornerVersion | smoothCornerVersion() | fork 版本握手(真机版本行的数据来源) |
请求与响应:
请求 : {"width":300,"height":300,"topStart":50,"topEnd":50,"bottomEnd":50,
"bottomStart":50,"smoothing":0.6,"rtl":false,"percent":false}
响应 : {"ok":true,"width":300,"height":300,"smoothing":0.6,"rtl":false,
"cubicCount":17,"path":"M ... C ... Z"}
| {"ok":false,"error":"..."}
4.3 C++ N-API 方法分发
napi_property_descriptor desc[] = {
{"smoothCornerRender", nullptr, NapiSmoothCornerRender, nullptr, nullptr, nullptr,
static_cast<napi_property_attributes>(napi_default | napi_enumerable), nullptr},
{"smoothCornerVersion", nullptr, NapiSmoothCornerVersion, nullptr, nullptr, nullptr,
static_cast<napi_property_attributes>(napi_default | napi_enumerable), nullptr},
};
每个回调统一三步:napi_typeof 校验必须是 string、OH_LOG_INFO 打字节数、调 KN 函数并包装返回。KN 返回的字符串必须经 libohossmoothcorner_symbols()->DisposeString 归还 Kotlin 运行时,绝不 free()。libohossmoothcorner_api.h 按社区已审仓库同款手写(只声明 ExportedSymbols 结构与两个业务符号),不拖整份生成头。
4.4 CMake 和类型声明
add_library(ohossmoothcorner SHARED IMPORTED)
set_target_properties(ohossmoothcorner PROPERTIES
IMPORTED_LOCATION ${KN_SO}
IMPORTED_NO_SONAME TRUE)
add_library(smoothcorner_napi SHARED napi_init.cpp)
target_link_libraries(smoothcorner_napi PUBLIC
libace_napi.z.so
libhilog_ndk.z.so
ohossmoothcorner)
OHOS_ARCH 为空或 libohossmoothcorner.so 缺失直接 FATAL_ERROR(提示先跑 link 任务);$ORIGIN rpath 加 POST_BUILD 拷贝保证 NAPI so、KN so、libc++ 三件套同目录,dlopen 时互相可见。KN 侧 .so 由 linkDebugShared* 任务经 copyKnSo* 自动拷入 entry/libs/<abi>/,构建链路无手工步骤。
export const smoothCornerRender: (request: string) => string;
export const smoothCornerVersion: () => string;
第 5 阶段:ArkUI 宿主
5.1 版本握手和实时重绘
aboutToAppear(): void {
try { this.version = smoothCornerVersion(); } catch (e) { this.version = '版本握手失败'; }
this.render(); // 默认参数 300×300 / r=50 / smoothing=0.6 首绘
}
private render(): void {
const raw: string = smoothCornerRender(this.buildRequest(this.smoothing));
const res = JSON.parse(raw) as RenderResponse;
if (res.ok && res.path !== undefined) {
this.pathStr = res.path;
this.info = `${res.cubicCount} 段三次贝塞尔 · ${res.width}×${res.height} · smoothing=${res.smoothing}...`;
} else {
this.info = `错误: ${res.error ?? 'unknown'}`; // ok:false 通道,ArkTS 侧永不崩
}
}
所有滑杆 / 开关的 onChange 直接调 render()——桥是同步 C 调用,单次几何计算在微秒量级,无需异步调度,拖动即所见即所得。
5.2 页面交互
形状预览 Tab 自上而下:预览 Stack(平滑轮廓 + 红色对照轮廓叠加)、状态行、宽高 / smoothing 滑杆、四角联动开关(联动时一根"圆角半径"滑杆,独立时四根方向角滑杆)、RTL / 百分比 / 填充描边 / 对照轮廓四个开关、五套色板与重置按钮。关于 Tab 展示版本握手行、库简介、ArkTS → NAPI → C ABI → 几何内核 → SVG path → Path().commands() 分层说明、桥接协议、验收要点与仓库 / 社区链接。
红色对照轮廓是本 Demo 的关键设计:同一参数但 smoothing=0 的路径以红描边叠在填充轮廓之下,平滑角与经典圆角在"直线与圆弧相切处"的差异被直接可视化,不需要任何口头解释。
5.3 对比度和错误边界
深色前景配浅色背景、五套色板覆盖深浅两用;ok:false 时错误原文进状态行;Native 调用本身抛错(理论不该发生)同样被 try/catch 收进状态行。半径拉到超过半边时内核自动钳制,预览不越界、不崩。
第 6 阶段:示例与验证
6.1 工程结构
example/
├── nativeApp/
│ ├── build.gradle.kts 双 ABI sharedLib(baseName=ohossmoothcorner)+ cxxShared + copyKnSo*
│ └── src/ohosMain/kotlin/org/terminator/ohos/smoothcorner/
│ └── Bridge.kt @CName × 2 + MiniJson(扁平 JSON,继承自 cmp_qrose 审核模式)
└── harmonyApp/
├── build-profile.json5 signingConfigs 留空(签名由 DevEco 自动生成回填)
├── AppScope/app.json5 bundleName org.terminator.ohos.smoothcorner、versionName 1.0.4-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/libohossmoothcorner_api.h
├── src/main/cpp/types/libsmoothcorner_napi/{index.d.ts, oh-package.json5}
└── src/main/ets/pages/Index.ets ArkUI 双页签验收页面
6.2 构建、签名和安装
./gradlew :smooth-rounded-corner-shape:jvmTest # 21 项对拍(真实 graphics-shapes 1.0.1)
./gradlew :example:nativeApp:linkDebugSharedOhosArm64 :example:nativeApp:linkDebugSharedOhosX64
cd example/harmonyApp
hvigorw assembleHap -p product=default -p buildMode=debug --no-daemon
签名在 DevEco Studio 的 File → Project Structure → Signing Configs 中勾选自动生成,证书写入本机 ~/.ohos/config,不进入仓库(仓库内 build-profile.json5 的 signingConfigs 保持空数组,勾选自动签名后由 IDE 回填完整配置)。
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.smoothcorner -m entry
四、完整代码对照
4.1 整体架构
androidx.graphics.shapes 1.0.1(上游依赖的数学,无 ohos 制品)
│ 逐表达式移植
geoshape/GraphicsShapesPort.kt(全 internal:RoundedPolygon / RoundedCorner / Cubic / Pt / Feature)
│
geoshape/SmoothCornerGeometry.kt(公共入口:cubics / percentRadius / svgPath + ShapeCubic)
│
Bridge.kt + MiniJson
│ SmoothCornerRender / SmoothCornerVersion
libsmoothcorner_napi.so
│ smoothCornerRender / smoothCornerVersion
Index.ets
│ JSON parse + @State 更新
▼
ArkUI 真机页面(Path().commands() 双页签)
smooth-rounded-corner-shape/src/composeMain/**(上游 Compose API 原文,不编译,等 ohos 制品接回)
4.2 文件清单
| 文件 | 职责 |
|---|---|
settings.gradle.kts | 极简 ohos 构建、eazytec Maven 置前、ohos_smooth_corner_shape |
gradle/libs.versions.toml | Kotlin 2.2.21-1.0.0 钉版 + 对拍专用 graphics-shapes 1.0.1 / collection |
build.gradle.kts(根) | 版本 1.0.4-ohos.1、全局禁用 Ohos*Test* 任务 |
smooth-rounded-corner-shape/build.gradle.kts | 纯 KMP:jvm + ohosArm64 + ohosX64,composeMain 不注册的说明 |
.../geoshape/GraphicsShapesPort.kt | graphics-shapes 1.0.1 逐表达式移植(全 internal,Apache-2.0 头) |
.../geoshape/SmoothCornerGeometry.kt | 公共入口:cubics / percentRadius / svgPath + ShapeCubic |
.../src/composeMain/** | 上游 Compose API 原文 + 隔离说明(git 100% rename) |
.../jvmTest/...SmoothCornerGeometryParityTest.kt | 21 项对拍用例 |
example/nativeApp/build.gradle.kts | 双 ABI sharedLib、cxxShared 定位 libc++、copyKnSo* 回填 |
nativeApp/.../Bridge.kt | 2 个 C ABI、RTL/percent 对齐、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 | 鸿蒙适配主文档(16 节) |
4.3 关键 API 对照
| 层次 | API | 作用 |
|---|---|---|
| 内核 | SmoothCornerGeometry.cubics(w, h, 四角, smoothing) | 闭合轮廓的 cubic 列表(绝对坐标 [0,w]×[0,h]) |
| 内核 | SmoothCornerGeometry.percentRadius(w, h, percent) | min(w,h)·percent/100,对齐 CornerSize(percent) |
| 内核 | SmoothCornerGeometry.svgPath(cubics) | M ... C ... Z 绝对路径,3 位小数 |
| 内核 | RoundedPolygon(vertices, perVertexRounding) | 移植的 graphics-shapes 1.0.1 构造管线 |
| Native | SmoothCornerRender / SmoothCornerVersion | C ABI JSON 导出 |
| N-API | smoothCornerRender(req) / smoothCornerVersion() | 向 ArkTS 暴露能力 |
| ArkTS | Path().commands(pathStr) | 矢量渲染验收 |
4.4 ArkTS 与 Kotlin 的边界
const res = JSON.parse(smoothCornerRender(JSON.stringify({
width: 300, height: 300,
topStart: 50, topEnd: 50, bottomEnd: 50, bottomStart: 50,
smoothing: 0.6, rtl: false, percent: false
})));
// res.ok === true;res.path 形如 "M0 50 C ... Z",res.cubicCount === 17
// SmoothRoundedCornerShape.createOutline 的 LayoutDirection 映射,在桥内复刻
val topLeft = if (!rtl) topStart else topEnd
val topRight = if (!rtl) topEnd else topStart
val bottomLeft = if (!rtl) bottomStart else bottomEnd
val bottomRight = if (!rtl) bottomEnd else bottomStart
val cubics = SmoothCornerGeometry.cubics(width, height, topLeft, topRight, bottomRight, bottomLeft, smoothing)
两者之间只传输扁平 JSON 字符串与 SVG 路径文本,不传输 Kotlin 对象或未校验的动态结构。
五、关键决策说明
决策 1:移植内核而不是等制品、也不是 ArkTS 重写
graphics-shapes 没有 ohos 制品,"等"没有时间表;ArkTS 重写则把圆角切割、侧翼曲线、圆弧近似的每个浮点常量重新发明一遍,等于换一套需要重新证明的实现。移植是唯一同时满足"零漂移风险下界可控"与"当下可交付"的路线——而且移植面可以收窄到上游实际调用的那条管线,Morph / bounds / 变换等用不到的代码一个字节都不搬。
决策 2:对拍基准钉上游声明的 1.0.1,不升 1.1.0
对拍的意义是"鸿蒙 .so 里的几何 = 上游渲染的几何",基准必须与上游 build.gradle.kts 里声明的 graphics-shapes:1.0.1 完全一致。1.1.0 与 1.0.1 的行为差异属上游升级范畴,应随上游 bump 一起做;鸿蒙 fork 单方面"超前"会让对拍失去基准资格。
决策 3:根构建替换(cmp_qrose 模式)+ composeMain 隔离
上游根构建(AGP / Compose 1.6.11 / vanniktech 发布 / signing)对鸿蒙没有保留价值,且 AGP 与鸿蒙 Kotlin 插件无法共存,整个替换为极简 ohos 构建。上游 Compose 面是库的公共 API,不能删——原文移入 src/composeMain 不注册源码集,配 README 写明接回条件(compose.ui / graphics-shapes 出 ohos 变体),git 100% rename 可审计。
决策 4:RTL / 百分比语义放桥侧,内核只做纯几何
CornerBasedShape 的 start/end 语义与 CornerSize(percent) 的解析规则属于 Compose 层。语义集中在桥接层,将来 Compose 面接回时,内核与 21 项对拍都零改动。
决策 5:交付 SVG path 而不是 PNG
qrose 的条码输出位图可行;圆角轮廓是矢量语义,转 PNG 是有损降级。ArkUI Path().commands() 直接吃 SVG 命令串,矢量无损、分辨率无关,没有理由多绕一层像素。
决策 6:手写 MiniJson,不上 kotlinx.serialization
桥协议只有扁平数字与布尔,60 行手写解析器足够;KN 侧少一个序列化依赖,.so 少几百 KB,也避免为鸿蒙线找 serialization ohos 制品的额外验证面。
六、测试与验证
6.1 测试环境
本次验证使用 Windows(x86_64)、JDK 21(Temurin,org.gradle.java.home 钉版)、DevEco Studio 5.1.0 Release(含 hvigor / ohpm / hdc 命令行工具)与鸿蒙真机(arm64)。
6.2 JVM 对拍(内核没漂的唯一可信证明)
./gradlew :smooth-rounded-corner-shape:jvmTest
21 项用例全部通过。同一个构造器分别喂给真实的 androidx.graphics:graphics-shapes:1.0.1(google() 仓)与移植内核,逐 float 对比(容差 1e-4——即 graphics-shapes 自身的 DistanceEpsilon):
| 组 | 用例 | 覆盖点 |
|---|---|---|
| 平滑档位 ×4 | smoothing 0 / 0.3 / 0.6 / 1 | 0=纯圆弧,1=双翼相接,0.6=Figma 60% 档 |
| 圆角形态 ×4 | README 示例 10/5/0/20、锐圆混排、全锐角、全锐角+平滑 | 每角独立、零半径走直线分支 |
| 钳制路径 ×3 | 单角超半边、同边两角相争、超大+最大平滑 | cutAdjusts 三级分配的两个不足分支 |
| 极端参数 ×3 | 2×2 退化尺寸、半径=半短边、1080×480 卡片 | 阈值边界与大坐标 |
| 语义收敛 ×7 | smoothing>1 收敛、闭合性、越界、percentRadius ×2、SVG 形状、全锐角段数=4 | 公共入口的契约 |
段数事实(对拍确认,Demo「关于」页文案与之对应):全直角 = 4 段;四角圆角(smoothing=0)= 9 段;四角全平滑 = 17 段——12 段角部曲线 + 4 段直边,再因首角圆弧对半拆分闭合多 1 段。这条事实链还纠正过一次直觉错误(开发时曾按 16 段写断言,对拍立刻打回):只要 21 个用例保持绿色,鸿蒙 .so 里的几何就是上游渲染的几何。
6.3 HAP 验证
:smooth-rounded-corner-shape:compileKotlinOhosArm64 / OhosX64: BUILD SUCCESSFUL
linkDebugSharedOhosArm64 / linkDebugSharedOhosX64: BUILD SUCCESSFUL(copyKnSo* 自动回填三件套)
llvm-nm: SmoothCornerRender / SmoothCornerVersion 均为全局 T 符号
BuildNativeWithNinja: finished(双 ABI 编出 libsmoothcorner_napi.so)
CompileArkTS: finished
PackageHap: finished
BUILD SUCCESSFUL
install bundle successfully
start ability successfully
entry-default-unsigned.hap 约 6.8 MB,内含双 ABI 各三件套(libsmoothcorner_napi.so ≈65KB / libohossmoothcorner.so / libc++_shared.so);HAP 内 strip 后 KN so arm64 ≈1.9MB、x86_64 ≈1.9MB(debugShared 未 strip 时分别约 3.7MB / 3.4MB,含 Kotlin/Native 运行时)。
6.4 功能用例
- 启动后标题下出现
Kotlin/Native 内核 · 1.0.4-ohos.1(来自真实 Native 调用,非静态文字); - 默认参数 300×300 / r=50 / smoothing=0.6 首绘,四角为超椭圆过渡,红色对照(smoothing=0)可见经典圆角与平滑角的切点差异;
- 拖动 smoothing 滑杆 0→1,轮廓从纯圆角连续过渡到双翼相接,每次
onChange实时重绘; - 关闭四角联动,四个方向角独立滑杆分别生效;开 RTL,圆角位置左右镜像;
- 开百分比圆角,
50%与联动模式min(宽,高)/2绝对值视觉一致; - 半径滑杆拉满,轮廓自动钳制不越界、应用不崩;
- 切换五套色板与填充/描边,渲染语义不变;
- 「关于」页版本握手、调用链、协议与验收要点完整展示。
6.5 验证结论
几何内核、交叉编译工具链、Native 动态库、C ABI、N-API、ArkUI 页面和 HAP 已完成链路验证。应用在鸿蒙真机上成功安装并进入前台,页面展示了真实的几何会话:版本握手、平滑角渲染、对照轮廓与参数实时重绘(见下节效果图)。
七、运行效果
7.1 真机截图

7.2 命令速查
git clone https://atomgit.com/oh-tpc/smooth-rounded-corner-shape.git
cd smooth-rounded-corner-shape
# Windows: gradle.properties 已写 org.gradle.java.home(JDK 21),命令行另需 JDK bin 进 PATH
./gradlew :smooth-rounded-corner-shape:jvmTest # 21 项对拍
./gradlew :smooth-rounded-corner-shape:compileKotlinOhosArm64 :smooth-rounded-corner-shape:compileKotlinOhosX64
./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.smoothcorner -m entry
hdc shell hilog | grep SmoothCorner
八、遗留问题与改进方向
8.1 踩坑复盘
- 只改 ArkTS 页面不算 KMP 适配,必须让内核真正编译成
ohosArm64/ohosX64动态库,并用对拍证明它算的就是上游那套数学; settings.gradle.kts里 eazytec 双仓必须排在pluginManagement与dependencyResolutionManagement最前,否则配置期直接Unresolved reference: ohosArm64;- 上游根构建的 AGP + Compose 插件 + Kotlin 1.9.23 与鸿蒙工具链不共存,根构建必须整体替换,保留任何一行 AGP 痕迹都会在配置期冲突;
- graphics-shapes 内部的
Point是androidx.collection.FloatFloatPair的 typealias、MutableFloatList也是 androidx.collection 的——两个"藏得最深"的依赖都没有 ohos 变体,移植时用零依赖的Pt/FloatArray等价替换(只影响存储,不影响算术); - 移植必须守住求值顺序纪律:浮点乘除链重排、魔数"化简"、分支顺序调整,任何一处都会让对拍在极端参数上打回——本仓的对拍用例真的抓到过一次(段数断言按直觉写成 16,实测 17:首角圆弧为精确闭合被对半拆分多出 1 段);
- ArkTS 不能直接 import KN so 调
@CName业务符号(HarmonyOS Kotlin 的 CAdapter 不导出),必须走 CMake NAPI 包装层,$ORIGINrpath 保证三件套同目录互相可见; - KN 返回字符串必须
DisposeString归还运行时,N-API 层不能free(); - hvigor 命令行打包阶段
spawn java ENOENT——PackageHap 需要系统 PATH 里有 JDK,命令行构建时把 JDK bin 加进 PATH(DevEco GUI 内不受影响); - MiniJson 的取值函数漏写默认参数值会在 KN 编译期报
No value passed for parameter 'default'——协议里"缺字段按 0 再走参数校验"的语义要显式落成默认值; - HAP 缺当前 ABI 的 so 会报安装失败
9568347,abiFilters必须双 ABI,每个 ABI 三件套缺一不可; entry/libs/里的 so 与 DevEco 生成的.clang-tidy/.clangd是构建产物,gitignore 必须覆盖,否则混进提交;- 签名材料由 DevEco 自动写入
build-profile.json5,提交前注意不要把带material的签名配置推进仓库,证书绝不进入 AtomGit。
8.2 已知问题
- ArkUI
Path().commands()的坐标单位是 px,而桥交付的几何按 vp 数值给出:真机上图形按屏幕密度比缩小、显示在组件左上角(形状本身逐位正确,对照轮廓与平滑轮廓的比例关系不受影响)。修复方向是渲染侧按density缩放坐标、或以 px 设置组件尺寸,内核与协议零改动; - 鸿蒙侧没有 Compose
Shape:交付物是几何(SVG path / cubic 列表),SmoothRoundedCornerShape不能直接塞进 ArkUI 组件的 shape 属性——那是 compose.ui / graphics-shapes 出 ohos 制品后的事; - 上游 Compose API 暂不编译:
composeMain里的 4+2 个文件是原文快照,接回前提是制品就绪; - 只覆盖矩形管线:上游公共 API 本身只有矩形 smooth corner,移植面与上游公共面严格一致,没有"顺手多移植";
- 对拍在 JVM 不在 ohos:KN 与 JVM 共用同一份 commonMain 代码与同一套 stdlib 数学,浮点求值顺序一致;真机验收做最终确认;
- path 坐标 3 位小数:SVG payload 体积优先;需要全精度的下游可扩展
cubics通道; - android 目标剥离(上游 1.0.4 继续服务 Android/Desktop),发布、signing 一并剥离,本 fork 不发 Maven Central;
- HAP 安装必须使用与 bundle name
org.terminator.ohos.smoothcorner匹配的签名。
8.3 未来优化方向
- 修复 px/vp 单位对齐(渲染侧 density 缩放),让预览图形精确占满画布;
- 开放
cubics全精度通道(绕过 SVG 3 位小数),并按需暴露任意顶点RoundedPolygon桥(内核已具备,仅协议扩展); - 跟踪
graphics-shapes与社区 CMP 的 ohos 制品动态,制品就绪后恢复 composeMain 源码集并回归对拍; - 若上游 bump graphics-shapes(1.1.x),同步移植面并扩对拍矩阵,保持"上游依赖版本 = 对拍基准版本";
- 评估
.har形态发布,让 ArkTS 工程直接依赖而不是走 so 三件套; - 在 CI 中加入 ohos 编译、ELF 符号检查和 HAP 构建;补充真机(x86_64)验收矩阵和自动化截图脚本。
九、总结
9.1 核心难点回顾
上游 smooth-rounded-corner-shape 1.0.4(公共面 = Compose Shape,数学 = graphics-shapes 1.0.1)
→ 验证制品线:graphics-shapes / compose.ui / androidx.collection 均无 ohos 变体
→ 路线定型:数学移植 + Compose 面封存(与 KStore"真编译整库"互为镜像)
→ 根构建替换(cmp_qrose 模式,Kotlin 2.2.21-1.0.0 + Gradle 8.14.1 + JDK 21)
→ graphics-shapes 1.0.1 逐表达式移植(求值顺序纪律)+ 公共入口 SmoothCornerGeometry
→ 21 项 JVM 对拍对真实 1.0.1 逐 float 验证(容差 = 库自身的 DistanceEpsilon)
→ ohosArm64 / ohosX64 KLIB + 双 ABI sharedLib + @CName × 2
→ C++ N-API(DisposeString 纪律)+ ArkTS JSON 解析
→ ArkUI Path().commands() 真机双页签验收(平滑/对照/RTL/百分比/钳制)
9.2 封装层次
- 内核层:graphics-shapes 1.0.1 的
RoundedPolygon管线逐表达式移植,全 internal,Apache-2.0 声明; - 入口层:
SmoothCornerGeometry.cubics / percentRadius / svgPath,纯几何、无 Compose 语义; - 桥接层:RTL / 百分比语义在 Kotlin 桥内对齐 Compose,2 个 C ABI 输出 JSON + SVG path;
- N-API 层:参数检查、字符串转换和 Native 内存释放(
DisposeString); - ArkTS 层:页面状态、滑杆实时重绘、对照轮廓叠加、色板与错误文本;
- 隔离层:上游 Compose API 原文在
composeMain留档,等制品接回。
9.3 三条经验
- 路线选择必须基于当天的制品可达性:KStore 验证出"制品在 → 真编译整库",本库验证出"制品不在 → 逐表达式移植 + 对拍兜底",两条路线在同一社区规范下互为镜像;先查制品再定路线,不要拿着上一个库的结论套下一个库;
- 移植类适配的可信度来自对拍,不来自"看着像":把真实上游依赖当参照实现、逐 float 对比(容差取库自身的一致性阈值),极端参数用例(超大半径、退化尺寸)是对拍真正的价值区;段数从 16 改 17 那次打回,就是这套门槛在起作用;
- 语义放桥侧、数学放内核:start/end 镜像与百分比解析属于 Compose 层,集中在对齐层;内核保持纯几何,将来 Compose 面接回或语义修正,移植代码与对拍矩阵零改动。
9.4 适配成果
当前适配已完成:
- smooth-rounded-corner-shape
1.0.4(1.0.4-ohos.1)几何内核的双 ABI Kotlin/Native 编译(graphics-shapes 1.0.1 逐表达式移植,上游 Compose 文件零修改留档); - 21 项 JVM 对拍全绿(对真实
androidx.graphics:graphics-shapes:1.0.1,容差 1e-4); - 2 个 C ABI 符号和 C++ N-API 包装层(
llvm-nm验证全局 T 符号); - DevEco Stage ArkUI 双页签验收页面(平滑角渲染、红色对照、smoothing / 四角 / RTL / 百分比实时重绘、版本握手);
- HAP 构建(双 ABI 六件套 so,约 6.8MB)、签名、设备安装和真机验证;
- AtomGit 项目文档、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。
参考文档
更多推荐

所有评论(0)