本文记录 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.AppGalleryKitcheckPinShortcutPermittedrequestNewPinShortcut。验证的是同一份 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 公共模块提供 AppShortcutTargetAppShortcut、目录、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 Multiplatform2.2.21-1.0.0JVM、Kotlin/Native 和 KLIB
JDK21Gradle、Kotlin 编译和 Native 任务
OpenHarmony 目标ohosArm64ARM64 真机动态库
DevEco product6.0.0(20)工程兼容和目标版本
ABIarm64-v8aHAP 原生库架构
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.solibapp_shortcuts_api.h,只导出 AppShortcutCatalogAppShortcutGetAppShortcutRunChecksAppShortcutFree 四个符号,并复制到 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.ktJSON 编码和转义
example/nativeApp/NativeBridge.ktC ABI、JSON 返回和内存释放
entry/src/main/cpp/napi_init.cppN-API 导出和参数检查
entry/src/main/ets/appshortcut/AppShortcutManager.etsWant 和系统确认事务
entry/src/main/ets/entryability/EntryAbility.ets启动参数路由
entry/src/main/ets/pages/Index.ets真机示例页面
entry/src/main/resources/base/profile/shortcuts.jsonAPI 20 入口声明

4.3 关键 API 对照

层次API作用
KotlinAppShortcutEngine.catalog生成入口目录
KotlinAppShortcutEngine.find按 ID 查找入口
NativeAppShortcutGet返回 JSON 入口
N-APIgetShortcut向 ArkTS 暴露入口
ArkTScheckPinShortcutPermitted获取一次性 tid
ArkTSrequestNewPinShortcut打开系统确认页
AbilityonCreate / 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 通过 onCreateonNewWant 收到对应 shortcutId

用例 4:重复 ID

再次添加同一 ID 时系统拒绝重复入口,页面显示错误文本而不是假装成功。

6.4 验证结论

公共测试、Kotlin/Native ARM64 链接、ELF 依赖检查、Hvigor HAP 构建、签名安装、入口切换和系统确认页均已验证。确认页显示名称与页面选中项一致,说明从 KMP 目录到系统桌面事务的链路已连通。

七、运行效果

7.1 真机截图

在这里插入图片描述

截图中可以看到系统桌面长按菜单(移除、添加应用锁)、应用快捷入口列表“应用设置 / 新建任务 / 今日专注”、统一应用图标,以及桌面上的应用主图标。入口名称与 shortcuts.json、KMP 目录和页面按钮一致。

7.2 应用内页面效果

应用页面采用浅色紧凑布局:首屏内容从状态栏下方开始,入口按钮使用分段选择样式,详情区显示标签、说明、入口 ID 和目标 EntryAbility,添加操作进入系统确认页,错误和提交状态显示在详情区。

八、遗留问题与改进方向

8.1 踩坑复盘

  1. 只改 ArkTS 页面不算 KMP 适配:必须把共享模型编译成 ohosArm64 动态库。
  2. N-API 不负责业务判断:C++ 只做类型检查、字符串转换和释放。
  3. 资源重载不能混用:静态资源重载接收资源索引名,自定义重载接收 PNG/WebP 沙箱路径。
  4. profile 和运行时请求必须成对管理:缺少 profile 会导致确认后系统服务无法落盘。
  5. 参数名称必须稳定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 三条经验

  1. 先让公共模型在 JVM 和 Native 通过,再接入 ArkUI 和系统服务;
  2. 用 JSON 和少量 C ABI 代替跨语言对象传递;
  3. 把 profile、系统确认和桌面回跳分别记录,避免把“弹窗出现”误认为“快捷入口已经落盘”。

9.4 适配成果

当前 kmp-app-shortcuts 已完成公共入口模型、六项自检、Kotlin/Native + C ABI + N-API 桥接、API 20 Stage profile、AppGalleryKit 系统确认、EntryAbility.onCreate/onNewWant 路由、ArkUI 真机示例页面、签名 HAP 构建、设备安装和桌面快捷入口效果图。项目目录、文档、图片和源码地址统一使用 AtomGit。

参考文档

Logo

一站式 AI 云服务平台

更多推荐