开源鸿蒙平台 KMP_CMP 三方库「桌面快捷入口」适配全流程
本文记录
kmp-app-shortcuts接入开源鸿蒙(OpenHarmony)桌面快捷入口能力的完整过程,覆盖 KMP/CMP 工程盘点、ohosArm64目标、Kotlin/Native 动态库、C ABI/N-API 桥接、ArkUI 真机页面、API 20 Stage 快捷入口 profile、AppGalleryKit 系统确认、签名 HAP 和真机验收。本次适配复用 Kotlin 侧的入口模型、Want 目标、参数路由、JSON 契约和验收逻辑,再由 ArkTS 调用 HarmonyOS
@kit.AppGalleryKit的checkPinShortcutPermitted与requestNewPinShortcut。验证的是同一份 KMP 代码在 OpenHarmony ARM64 设备上的运行结果,而不是重新写一套只在页面里生效的快捷入口数据。
项目地址: AtomGit/oh-tpc/kmp-app-shortcuts
开发工具: 华为云码道
一、背景
1.1 为什么做开源鸿蒙平台 KMP/CMP 适配
桌面快捷入口是 HarmonyOS/OpenHarmony Launcher 提供的系统能力。应用声明入口的标签、图标、目标 Ability 和参数后,可以通过 AppGalleryKit 发起一次用户确认流程;用户确认后,桌面入口独立存在,点击时再把 Want.parameters 传回应用。
如果只把页面重新写成 ArkTS,页面可能显示几个入口按钮,却无法证明公共 Kotlin 模型、Native 动态库、N-API 和系统确认页已经连通。适配需要解决 KMP 目标、工具链、跨语言对象、Stage profile、Want 路由、用户确认和签名交付等边界。
| 障碍 | 具体问题 |
|---|---|
| 目标缺失 | KMP 默认只有 JVM,必须增加 ohosArm64() 才能生成 ARM64 Native 动态库。 |
| 系统 API 约束 | 静态资源和自定义资源重载参数不同,资源类型传错会返回 401 Parameter error。 |
| 语言边界不同 | ArkTS 不能直接持有 Kotlin data class,必须经过 C ABI、C++ N-API 和 JSON。 |
| 入口配置边界 | API 20 Stage 使用 EntryAbility.metadata 引用 $profile:shortcuts。 |
| 用户确认要求 | 三方应用不能静默写入桌面,必须等待系统确认。 |
1.2 库提供的能力
app-shortcuts 公共模块提供 AppShortcutTarget、AppShortcut、目录、ID 查找、六项自检和无依赖 JSON 序列化。
| ID | 标签 | 说明 | Want 参数 |
|---|---|---|---|
open-focus | 今日专注 | 打开每日专注页面 | shortcutId=open-focus |
new-task | 新建任务 | 直接进入新任务页面 | shortcutId=new-task |
open-settings | 应用设置 | 打开应用设置页面 | shortcutId=open-settings |
1.3 实现适配
| 维度 | 要求 |
|---|---|
| 代码复用 | 入口模型、目标 Ability、参数路由、JSON 和自检由 Kotlin 共享。 |
| 平台目标 | 公共模块和示例加入 ohosArm64,生成 libapp_shortcuts.so。 |
| 桥接稳定 | 使用少量 C ABI 函数和 JSON,避免跨语言对象地址。 |
| UI 完整 | 页面支持入口切换、添加到桌面、系统状态和错误展示。 |
| 仓库规范 | 项目说明、文章、效果图和源码链接统一使用 AtomGit。 |
二、实现路线图
第 1 阶段:项目初始化 ── 盘点 KMP 模块、入口模型和 OpenHarmony 边界
第 2 阶段:目标与依赖打通 ── 加入 ohosArm64、独立 example 和 Native 任务
第 3 阶段:模型与序列化 ── 建立 Target、Shortcut、目录、自检和 JSON 契约
第 4 阶段:原生桥接 ── Kotlin/Native C ABI、C++ N-API 和内存释放
第 5 阶段:系统能力封装 ── Stage profile、Want 参数和系统确认
第 6 阶段:示例与验证 ── ArkUI 页面、签名 HAP、设备安装和效果图
三、逐步实现过程
第 1 阶段:项目初始化
1.1 盘点
公共 API 和工程边界:
app-shortcuts/ KMP 入口模型、目录、自检和 JSON 边界
vico/ 参考工程一致的库聚合层
sample/ android/desktop/shared/web/ios 主机入口
example/shared/ 示例门面和 JVM 验收测试
example/nativeApp/ ohosArm64 Kotlin/Native 动态库
example/ohosApp/ DevEco Stage 工程和 ArkUI 页面
scripts/ Native、HAP 和签名工程辅助脚本
docs/openharmony/ 验收记录和真机效果图
guide/ 集成指南
1.2 固定工具链和版本矩阵
| 项目 | 配置 | 用途 |
|---|---|---|
| Kotlin Multiplatform | 2.2.21-1.0.0 | JVM、Kotlin/Native 和 KLIB |
| JDK | 21 | Gradle、Kotlin 编译和 Native 任务 |
| OpenHarmony 目标 | ohosArm64 | ARM64 真机动态库 |
| DevEco product | 6.0.0(20) | 工程兼容和目标版本 |
| ABI | arm64-v8a | HAP 原生库架构 |
1.3 页面边界
页面围绕“当前入口、目标 Ability、入口 ID 和确认状态”组织:标题区、入口区、详情区、添加按钮和自检状态区。入口切换只读取 KMP/N-API 目录,添加按钮才创建 Want 并调用 AppGalleryKit。
第 2 阶段:目标与依赖打通
2.1 加入 ohosArm64 目标
kotlin {
explicitApi()
jvm()
jvmToolchain(21)
ohosArm64()
}
2.2 Native focused build
Native 任务生成 libapp_shortcuts.so 和 libapp_shortcuts_api.h,只导出 AppShortcutCatalog、AppShortcutGet、AppShortcutRunChecks、AppShortcutFree 四个符号,并复制到 entry/libs/arm64-v8a 和 C++ include 目录。
第 3 阶段:模型与序列化
3.1 JSON 契约
KMP AppShortcutEngine -> Kotlin/Native C ABI -> C++ N-API
-> AppShortcutClient.ets -> Index.ets -> Want.parameters
3.2 公共模型
public data class AppShortcutTarget(
val bundleName: String,
val moduleName: String,
val abilityName: String,
)
public data class AppShortcut(
val id: String,
val label: String,
val description: String,
val iconResource: String,
val target: AppShortcutTarget,
val parameters: Map<String, String> = emptyMap(),
)
初始化代码拒绝空目标、空 ID、超长 ID、空标签、超长说明和空参数名。六项自检覆盖入口数量、ID 唯一性、目标 Ability、shortcutId 一致性、查找确定性和 ID 字节上限。
第 4 阶段:原生桥接(技术难点)
4.1 三层桥接
ArkTS JSON string -> C++ N-API entry -> Kotlin/Native C ABI
-> AppShortcutEngine + UTF-8 JSON + explicit free
C++ 只负责参数检查、UTF-8 字符串转换和 Native 释放,不复制 Kotlin 入口规则。
4.2 CMake 边界
add_library(app_shortcuts SHARED IMPORTED)
add_library(entry SHARED napi_init.cpp)
target_include_directories(entry PRIVATE "include")
target_link_libraries(entry PRIVATE app_shortcuts libace_napi.z.so)
第 5 阶段:系统能力封装
5.1 API 20 Stage profile
{
"metadata": [
{
"name": "ohos.ability.shortcuts",
"resource": "$profile:shortcuts"
}
]
}
resources/base/profile/shortcuts.json 维护 shortcutId、标签、图标和 wants。不要重新加入旧版根级 module.json5.shortcuts 字段。
5.2 AppGalleryKit 添加流程
const want: Want = {
bundleName: shortcut.target.bundleName,
moduleName: shortcut.target.moduleName,
abilityName: shortcut.target.abilityName,
parameters: shortcut.parameters,
};
const result = await productViewManager.checkPinShortcutPermitted(
context,
shortcut.id,
want,
labelResourceName(shortcut.id),
shortcut.iconResource,
);
await productViewManager.requestNewPinShortcut(context, result.tid);
五参数重载接收资源索引名;六参数自定义资源重载要求真实 PNG/WebP 沙箱路径,不能把 app_icon 资源名传给 foregroundIcon。
5.3 Ability 参数回传
private applyWant(want: Want): void {
const value = want.parameters?.['shortcutId'];
const id = typeof value === 'string' ? value : '';
AppStorage.setOrCreate('requestedShortcutId', id);
}
onCreate(want: Want, _launchParam: AbilityConstant.LaunchParam): void {
this.applyWant(want);
}
onNewWant(want: Want, _launchParam: AbilityConstant.LaunchParam): void {
this.applyWant(want);
}
第 6 阶段:示例与验证
export JAVA_HOME="/path/to/jdk-21"
./scripts/build-openharmony.sh
./scripts/build-hap.sh example/ohosApp
hdc list targets -v
hdc -t <设备序列号> install -r \
example/ohosApp/entry/build/default/outputs/default/entry-default-signed.hap
hdc -t <设备序列号> shell aa start \
-a EntryAbility -b com.ohos.appshortcuts.sample
签名证书、profile、p12 和密码只保存在本机,不要提交到 AtomGit。
四、完整代码对照
4.1 整体架构
KMP AppShortcutEngine -> Kotlin/Native -> libapp_shortcuts.so
-> C++ N-API -> AppShortcutClient.ets -> Index.ets
-> AppGalleryKit -> system confirmation -> launcher
-> EntryAbility.onCreate / onNewWant
4.2 文件清单
| 文件 | 职责 |
|---|---|
app-shortcuts/AppShortcut.kt | 目标、数据模型和初始化校验 |
app-shortcuts/AppShortcutEngine.kt | 目录、查找和公共自检 |
app-shortcuts/AppShortcutJson.kt | JSON 编码和转义 |
example/nativeApp/NativeBridge.kt | C ABI、JSON 返回和内存释放 |
entry/src/main/cpp/napi_init.cpp | N-API 导出和参数检查 |
entry/src/main/ets/appshortcut/AppShortcutManager.ets | Want 和系统确认事务 |
entry/src/main/ets/entryability/EntryAbility.ets | 启动参数路由 |
entry/src/main/ets/pages/Index.ets | 真机示例页面 |
entry/src/main/resources/base/profile/shortcuts.json | API 20 入口声明 |
4.3 关键 API 对照
| 层次 | API | 作用 |
|---|---|---|
| Kotlin | AppShortcutEngine.catalog | 生成入口目录 |
| Kotlin | AppShortcutEngine.find | 按 ID 查找入口 |
| Native | AppShortcutGet | 返回 JSON 入口 |
| N-API | getShortcut | 向 ArkTS 暴露入口 |
| ArkTS | checkPinShortcutPermitted | 获取一次性 tid |
| ArkTS | requestNewPinShortcut | 打开系统确认页 |
| Ability | onCreate / onNewWant | 接收桌面入口参数 |
五、关键决策说明
决策 1:ohosArm64 是适配验收的一部分
只有真正链接 ARM64 动态库,才能证明共享 Kotlin 代码进入 OpenHarmony 运行时。
决策 2:JSON 作为跨语言数据契约
JSON 让 ArkTS、C++ 和 Kotlin/Native 的边界清楚可调试,并避免暴露内部对象布局。
决策 3:profile 声明和动态确认分开
profile 描述系统识别的 ID、标签、图标和目标;AppGalleryKit 负责当前一次添加事务,两者缺一都不完整。
决策 4:页面入口和桌面入口共用 shortcutId
页面选中状态、profile、Want 参数和 Ability 路由使用同一个 ID,避免桌面显示正确但回跳选错页面。
决策 5:库验证和设备验证分开
JVM 验证入口规则,Native 验证 ABI,Hvigor 验证 HAP,真机验证确认页、桌面入口和 Ability 回跳。
六、测试与验证
6.1 测试环境
本次真机验证使用 macOS、JDK 21、Kotlin Multiplatform 2.2.21-1.0.0、DevEco Studio API 20 ARM64 SDK、签名 HAP、USB HarmonyOS ARM64 真机和 hdc 序列号 FMR0223825079397。
6.2 静态检查与单元测试
./gradlew :app-shortcuts:jvmTest :sample:shared:jvmTest
(cd example && ./gradlew :shared:jvmTest :nativeApp:prepareOhos)
6.3 功能验证用例
用例 1:默认页面和自检
启动应用后显示“桌面快捷入口”和 6/6 自检通过,目录由 Native getCatalog() 读取。
用例 2:三个入口切换
点击“今日专注”“新建任务”“应用设置”,标题、说明、ID 和选中按钮同步更新,不依赖桌面服务。
用例 3:系统确认和桌面回跳
点击“添加到桌面”,系统弹窗显示当前标签。点击“确定”后入口由 Launcher 管理;点击桌面入口后,EntryAbility 通过 onCreate 或 onNewWant 收到对应 shortcutId。
用例 4:重复 ID
再次添加同一 ID 时系统拒绝重复入口,页面显示错误文本而不是假装成功。
6.4 验证结论
公共测试、Kotlin/Native ARM64 链接、ELF 依赖检查、Hvigor HAP 构建、签名安装、入口切换和系统确认页均已验证。确认页显示名称与页面选中项一致,说明从 KMP 目录到系统桌面事务的链路已连通。
七、运行效果
7.1 真机截图

截图中可以看到系统桌面长按菜单(移除、添加应用锁)、应用快捷入口列表“应用设置 / 新建任务 / 今日专注”、统一应用图标,以及桌面上的应用主图标。入口名称与 shortcuts.json、KMP 目录和页面按钮一致。
7.2 应用内页面效果
应用页面采用浅色紧凑布局:首屏内容从状态栏下方开始,入口按钮使用分段选择样式,详情区显示标签、说明、入口 ID 和目标 EntryAbility,添加操作进入系统确认页,错误和提交状态显示在详情区。
八、遗留问题与改进方向
8.1 踩坑复盘
- 只改 ArkTS 页面不算 KMP 适配:必须把共享模型编译成
ohosArm64动态库。 - N-API 不负责业务判断:C++ 只做类型检查、字符串转换和释放。
- 资源重载不能混用:静态资源重载接收资源索引名,自定义重载接收 PNG/WebP 沙箱路径。
- profile 和运行时请求必须成对管理:缺少 profile 会导致确认后系统服务无法落盘。
- 参数名称必须稳定:
shortcutId是页面选择、桌面显示和 Ability 回跳的唯一关联字段。
8.2 已知问题
- 桌面能力依赖 Launcher、AppGalleryService 和设备版本,确认 UI 可能有差异;
- 系统可能返回“快捷入口 ID 已存在”,这是桌面状态冲突;
- 签名配置只适用于本地开发机,不能直接复制到其他环境;
- 多模块应用需要为每个目标 module 维护对应 profile 和 Want。
8.3 未来优化方向
- 增加跨平台
expect/actual快捷入口安装接口; - 用生成配置减少 KMP ID 与 ArkTS profile 的重复维护;
- 增加设备能力探测、服务不可用提示和重试策略;
- 为 Compose Multiplatform 提供统一的快捷入口管理组件;
- 在持续集成中加入 Native 链接、HAP 构建和 profile 结构检查。
九、总结
9.1 核心难点回顾
KMP AppShortcutEngine -> Kotlin/Native ARM64 -> C ABI -> C++ N-API
-> ArkTS JSON 校验 -> AppGalleryKit -> 系统确认页
-> Launcher 桌面入口 -> EntryAbility shortcutId 路由
9.2 封装层次
- KMP 层:定义入口、目标、参数、目录和 JSON;
- Native 层:生成 ARM64 动态库并输出有限 C ABI;
- N-API 层:完成参数检查、字符串转换和内存释放;
- ArkTS 层:管理页面、资源映射、系统事务、错误和 Ability 生命周期;
- DevEco 层:完成 CMake、profile、HAP、签名、安装和运行。
9.3 三条经验
- 先让公共模型在 JVM 和 Native 通过,再接入 ArkUI 和系统服务;
- 用 JSON 和少量 C ABI 代替跨语言对象传递;
- 把 profile、系统确认和桌面回跳分别记录,避免把“弹窗出现”误认为“快捷入口已经落盘”。
9.4 适配成果
当前 kmp-app-shortcuts 已完成公共入口模型、六项自检、Kotlin/Native + C ABI + N-API 桥接、API 20 Stage profile、AppGalleryKit 系统确认、EntryAbility.onCreate/onNewWant 路由、ArkUI 真机示例页面、签名 HAP 构建、设备安装和桌面快捷入口效果图。项目目录、文档、图片和源码地址统一使用 AtomGit。
参考文档
更多推荐




所有评论(0)