一提到跨平台 UI 开发,大家第一时间想到的通常是 Flutter、React Native,或者最新的 Compose Multiplatform。上个月我开始接触一个叫 Kuikly 的轻量级框架,配合 OpenHarmony 适配层 KuiklyUI-OH,结果第一天从环境搭建到华为云真机部署,一趟走下来比我想象中顺畅,也踩了不少文档里根本不会写的坑。这篇文章就记录 Day1 的完整过程:从一个干净的系统开始,把 KuiklyUI-OH 的编译环境配好,创建一个示例页面,再通过华为云远程真机安装调试,最终让应用在真机上跑起来。如果你正准备评估 Kuikly-OH 做跨平台业务,或者只是想在 OpenHarmony 设备上快速验证一个 Kotlin 界面方案,这篇实战记录应该能帮你省下半天时间。
1. 项目背景与整体设计思路
1.1 为什么是 Kuikly-OH
Kuikly 是一个基于 Kotlin 的跨平台 UI 框架,它的核心思路是把界面描述和业务逻辑统一写在 Kotlin 代码里,再通过编译器或运行时映射到不同平台的原生组件。KuiklyUI-OH 是 Kuikly 面向 OpenHarmony 的适配层,让同一套 UI 代码能够跑在 OpenHarmony 设备上,而不需要额外去写 ArkTS 页面。
我选择它的原因很简单:团队成员都是 Kotlin 背景,服务端和 Android 这边已经积累了大量的 Kotlin 业务代码。如果继续用双端原生去开发,OpenHarmony 这边还得单独招人或培养 ArkTS 能力,学习成本和维护成本都不低。Kuikly-OH 允许我把原生的 OpenHarmony Ability 作为壳工程,内部 UI 走 KuiklyUI-OH 渲染,这样既能保留 OpenHarmony 的系统能力,又能在 UI 层做跨端复用。
另外,KuiklyUI-OH 不是把 WebView 套一层壳,而是类似 Compose 的声明式 UI 模型,有状态管理和重组机制,界面刷新效率明显比 Web 方案高,也不会有 HTML/CSS 解析的额外开销。这一点在设计复杂交互页面时非常重要,尤其是在配置较低的 IoT 设备或中低端手机上,体验差距能直接感受出来。
1.2 要解决的核心问题
我们在项目中遇到的第一个问题是“重复实现”。同样的登录页、设置页、数据卡片,Android 写一遍,OpenHarmony 又得写一遍,而且两边的导航逻辑、状态处理还不完全一致,时间一长代码就开始分叉。Kuikly-OH 要解决的就是这个跨端复用问题:UI 层尽量共享,平台层只保留入口和系统服务调用。
第二个问题是“OpenHarmony 生态相对年轻”。虽然 ArkTS 和 ArkUI 发展很快,但一些成熟的三方库、图表库、路由库还没有完全跟上。与其等生态补齐,不如直接把已有的 Kotlin 跨平台能力带过来。Kuikly-OH 的依赖管理兼容 Maven 仓库,很多 JVM 库在 commonMain 里可以直接引用,这让业务代码的移植成本大幅下降。
第三个问题是“真机调试太麻烦”。OpenHarmony 设备不像 Android 那样随手就能借一台,很多时候还得靠华为云远程真机。正好 Kuikly-OH 工程编译出来的 HAP 包可以通过 hdc 工具安装到云真机上,和本地设备操作几乎一样。Day1 我就把这条链路完整打通了,后面团队再开发就可以按这个流程来。
1.3 技术选型对比
为了评估“值不值得切到 Kuikly-OH”,我简单列了一个对比表,把主流的跨平台方案放在一起看了下。
| 方案 | UI 跨端能力 | OpenHarmony 支持 | 团队上手成本 | 包体/性能特点 |
|---|---|---|---|---|
| Flutter | 高,自带渲染引擎 | 官方适配还不算特别成熟 | 需要学 Dart | 包体相对较大,渲染一致性好 |
| React Native | 高,映射原生组件 | 支持有限,需要维护原生桥 | 需要学 JS/TS 生态 | 依赖 JS 引擎,首帧开销略高 |
| Compose Multiplatform | 高,声明式 UI | 当前主要支持 Android/iOS/Desktop | 需要会 Kotlin + Compose 概念 | 性能好,生态逐渐成熟 |
| KuiklyUI-OH | 中高,纯 Kotlin 声明式 | 原生支持 OpenHarmony 适配 | Kotlin 开发者上手快 | 轻量,适合中低端设备 |
当然,这种对比只能作为参考。每个团队的技术栈、目标设备、交付周期都不一样。我最终选 Kuikly-OH,并不是因为它比 Flutter“更厉害”,而是因为在我们现有的 Kotlin 技术栈里,它是通向 OpenHarmony 的最短路径。如果你们团队本身就是 TS 为主,那可以直接考虑其他方案,没必要强行换语言。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 环境搭建:从零起步
2.1 前置依赖清单
动手之前先把所有依赖列清楚,避免装到一半发现缺东西。我用的机器是 Windows 11,但命令同样适用于 macOS 和 Linux,只是环境变量写法略有差异。下面是 Day1 需要的软件清单:
- JDK 17 或更高版本(我用的是 JDK 17.0.9)
- Kotlin 2.0 以上(框架本身要求 Kotlin 2.x)
- Gradle 8.5 以上(最好直接使用 wrapper 指定版本)
- Android SDK 34/35(用于基础构建工具和 Android 目标平台)
- DevEco Studio 5.0(用于安装 OpenHarmony SDK,也可以只装命令行工具)
- OpenHarmony SDK(API 12 或对应版本)
- hdc 工具(OpenHarmony Device Connector,用于连接设备)
- Git(拉取模板工程)
如果你本地已经装过 Android Studio 和 JDK,那么 Android SDK 基本不用重复装,只要把环境变量指对就行。DevEco Studio 和 Android Studio 可以共存,但要注意 OpenHarmony SDK 的路径不要和 Android SDK 混在一起,后面环境变量配置时容易踩坑。
2.2 JDK、Android SDK 与 Kotlin 配置
先检查 JDK 是否就绪。命令行输入:
bash复制java -version
如果输出的是 Java 17 以上版本,说明 JDK 没问题。如果没装或者版本太低,建议直接装 JDK 17,因为 Kotlin 2.0 和 Gradle 8.5 对 JDK 17 的兼容性最稳。
接着配置 JAVA_HOME 和 ANDROID_HOME。在 Windows 的命令行里可以临时设置:
cmd复制set JAVA_HOME=C:\Program Files\Java\jdk-17.0.9
set ANDROID_HOME=%LOCALAPPDATA%\Android\Sdk
在 macOS/Linux 的 ~/.zshrc 或 ~/.bashrc 里可以写:
bash复制export JAVA_HOME=/Library/Java/JavaVirtualMachines/jdk-17.jdk/Contents/Home
export ANDROID_HOME=$HOME/Library/Android/sdk
建议把这两个变量写进全局配置,因为 Gradle 和 Kuikly 构建脚本都会用到。之前我吃过一个亏:只在命令行里临时设置了 ANDROID_HOME,结果 IDE 里的构建任务始终找不到 SDK,白白排查了十几分钟。
Kotlin 本身不需要单独安装,编译时会通过 Gradle 插件下载对应版本。但你需要确认 Gradle 能正常使用。一般我们用项目的 gradle-wrapper.properties 指定 Gradle 版本,不需要全局安装。首次构建时 Gradle Wrapper 会下载,如果下载很慢,可以换用国内 Maven 镜像仓库,这个后面讲。
2.3 OpenHarmony SDK 与 DevEco Studio 的联动
OpenHarmony 应用打包需要 OHOS SDK 里的编译工具,最简单的方式是安装 DevEco Studio。安装完成后,在 DevEco Studio 的 SDK Manager 里勾选需要的 SDK 版本,我这里选的是 API 12。SDK 默认安装在类似 C:\Users\你的用户名\AppData\Local\OpenHarmony\Sdk 的位置。
但我在 Kuikly-OH 工程里不希望依赖 IDE,因为后面要接入 CI 或者命令行构建。这时候需要把 SDK 路径暴露给构建脚本。在环境变量里新增:
bash复制export OHOS_SDK_HOME=/path/to/OpenHarmony/Sdk
同时在项目的 local.properties 里加上:
properties复制sdk.dir=/path/to/OpenHarmony/Sdk
hdc 工具一般在 OHOS_SDK_HOME\toolchains\hdc.exe,建议把它也加入 PATH。后续连接华为云远程真机或者本地设备都需要用到。
2.4 常见环境变量设置
为了省事,我把所有环境变量写在一个文件里。Windows 用户用 setx 永久生效,macOS/Linux 用户写进 shell 配置文件。我这边大概是这样的:
bash复制export JAVA_HOME=/Library/Java/JavaVirtualMachines/jdk-17.jdk/Contents/Home
export ANDROID_HOME=$HOME/Library/Android/sdk
export OHOS_SDK_HOME=$HOME/OpenHarmony/Sdk
export PATH=$JAVA_HOME/bin:$ANDROID_HOME/platform-tools:$OHOS_SDK_HOME/toolchains:$PATH
注意 ANDROID_HOME 和 OHOS_SDK_HOME 不要指向同一个目录,两边编译工具链会有冲突。另外,如果你之前装过老版本的 hdc,记得更新到新版,否则连接 OpenHarmony 设备时可能提示版本不匹配,我后面就遇到了这个坑。
3. 创建并编译 KuiklyUI-OH 工程
3.1 生成项目骨架
Kuikly-OH 官方提供了一个模板工程,通过 Git 拉下来就能直接用。执行:
bash复制git clone https://gitee.com/example/kuikly-oh-template.git KuiklyOhDemo
cd KuiklyOhDemo
如果没有现成模板,也可以手动创建,但结构会比较繁琐,建议直接用模板。工程拉下来后,第一件事不是急着打开 IDE,而是先看一眼目录结构,理解 Gradle 是如何组织多平台模块的。
3.2 项目目录结构与依赖关系
我的模板工程核心目录长这样:
text复制KuiklyOhDemo/
├── settings.gradle.kts
├── build.gradle.kts
├── gradle/
│ ├── wrapper/
│ └── libs.versions.toml
├── common/
│ ├── build.gradle.kts
│ └── src/
│ ├── commonMain/kotlin/
│ ├── androidMain/kotlin/
│ └── ohosMain/kotlin/
├── androidApp/
│ ├── build.gradle.kts
│ └── src/main/
└── ohosApp/
├── build.gradle.kts
└── entry/
common 模块是跨平台核心,commonMain 里写 UI 和业务逻辑,androidMain 放 Android 平台相关实现,ohosMain 放 OpenHarmony 相关实现。androidApp 和 ohosApp 分别是两个平台的壳工程,职责只负责加载 common 模块里的页面。
这种分层的好处是:以后不管是支持新平台,还是替换某个平台的原生实现,都只需在对应 source set 里做改动,公共代码不用动。在 settings.gradle.kts 里可以清楚看到模块依赖关系:
kotlin复制include(":common")
include(":androidApp")
include(":ohosApp")
common/build.gradle.kts 里需要声明 Kuikly 依赖:
kotlin复制plugins {
kotlin("multiplatform")
id("io.github.kuikly.kuikly-gradle-plugin")
}
kotlin {
androidTarget()
ohosTarget() // 这是 Kuikly 插件提供的新 target
sourceSets {
commonMain.dependencies {
implementation("io.github.kuikly:kuikly-core:1.0.0")
implementation("io.github.kuikly:kuikly-ui-oh:1.0.0")
}
}
}
如果 ohosTarget() 在你的 Gradle 版本里报错,请先检查 Kuikly Gradle 插件版本是否支持当前 Kotlin 版本,这个兼容性问题比较常见。
3.3 编写第一个跨平台页面
作为 Day1 Demo,我不打算写复杂的业务,就在 commonMain 里做一个简单的欢迎页面,包含一段文字和一个按钮。KuiklyUI-OH 的 API 长得和 Compose 很像,Kotlin 开发者基本能无缝上手。
kotlin复制// file: common/src/commonMain/kotlin/com/example/kuiklyoh/App.kt
package com.example.kuiklyoh
import io.github.kuikly.ui.Component
import io.github.kuikly.ui.remember { mutableStateOf }
@Component
fun App() {
var count by remember { mutableStateOf(0) }
Column(
modifier = Modifier.fillMaxSize().padding(24.dp),
horizontalAlignment = Alignment.CenterHorizontally,
verticalArrangement = Arrangement.Center
) {
Text(
text = "Hello Kuikly-OH",
style = TextStyle(fontSize = 24.sp)
)
Button(
onClick = { count++ },
modifier = Modifier.padding(top = 16.dp)
) {
Text("Clicked: $count times")
}
}
}
这个组件就是一个最简单的跨平台页面。Modifier、Column、Text、Button 这些 API 在不同平台上是同一套实现,OpenHarmony 端会被映射成对应的原生组件,而不是用 WebView 渲染。
如果编译平台是 Android,androidApp 里需要一个 Activity 来加载这个组件。而 OpenHarmony 端需要在 ohosApp 的 EntryAbility 中加载。这样平台壳工程只留一个入口,UI 逻辑全部下沉到 common 模块。
3.4 本地编译与踩坑
第一次编译用的是 Gradle wrapper。在工程根目录执行:
bash复制./gradlew :common:build
这个命令会把 common 模块编译出 Android 和 OpenHarmony 两个目标平台的产物。如果只想编译 OpenHarmony 的 HAP 包,可以执行:
bash复制./gradlew :ohosApp:assembleHap
第一次编译需要下载大量依赖,包含 Kotlin 编译器、Kuikly 库、OpenHarmony SDK 工具链等,时间比较长。这里有几个容易踩的坑:
第一个是 Gradle 版本不匹配。项目要求 Gradle 8.5+,如果你全局 Gradle 是 8.2,构建时会出现类似 Unsupported Kotlin plugin version 的错误。解决办法是修改 gradle/wrapper/gradle-wrapper.properties 里的版本号,并保留 wrapper 方式构建。
第二个是依赖下载超时。默认仓库在国外,如果网络不稳定,构建会卡在下载 Kuikly 相关依赖。我的做法是在 settings.gradle.kts 里配置华为云镜像仓库,或者使用阿里云 Maven 镜像:
kotlin复制repositories {
maven("https://mirrors.huaweicloud.com/repository/maven/")
maven("https://maven.aliyun.com/repository/central")
mavenCentral()
}
注意:配置镜像仓库只影响依赖下载速度,不影响编译过程是否合法合规。编译成功后,会生成 OpenHarmony 的 HAP 包,路径一般在 ohosApp/build/outputs/hap/debug/entry-default-signed.hap。这个 HAP 就是我们后面要安装到云真机上的安装包。
4. 华为云真机部署实操
4.1 华为云远程真机服务介绍
做 OpenHarmony 开发,最头疼的就是没真机。虽然可以用 DevEco Studio 的模拟器,但模拟器在传感器、性能和真实系统行为上还是和真机有差距。华为云提供了一个远程真机测试服务,在控制台里可以申请一台远程设备,通过浏览器远程操作,或者通过命令行通道把 HAP 包安装到远程设备上。
这个服务的核心价值就是“人在工位,设备在机房”,你不需要肉身去插线,也不需要准备一个设备池。团队协作时,每个人都能按需申请,用完释放,比自己在桌上堆一堆开发板高效得多。Day1 我选择用华为云远程真机部署,就是为了验证整个 CI/CD 链路是否可行。
4.2 申请和连接远程真机
先在华为云控制台找到“云真机”或“移动应用测试”相关入口,选择一台 OpenHarmony 设备。设备列表里会显示型号、系统版本和当前状态。选择空闲设备后,点击“远程真机调试”,平台会分配一个专用连接通道。
连接成功后,页面会显示一个远程桌面画布,同时提供一行 hdc 连接命令提示。我在本地终端测试是否识别到设备:
bash复制hdc list targets
正常情况下会输出一行设备序列号。如果显示 [Empty],说明 hdc 服务没有和设备握手成功,可以重启 hdc 服务:
bash复制hdc kill
hdc start
如果远程真机的连接通道是通过平台工具的隧道模式建立的,需要确保本地安装了对应版本的 hdc,且网络策略允许与远程设备通信。我在实际操作中遇到过 hdc 版本旧导致连不上,去 DevEco Studio 的 SDK 目录下找到新版 hdc 覆盖本地 PATH 里的旧版本就好了。
4.3 打包安装与调试
连接成功后,直接安装 HAP 包:
bash复制hdc install C:\work\KuiklyOhDemo\ohosApp\build\outputs\hap\debug\entry-default-signed.hap
安装成功后会输出 install bundle successfully。接着启动应用:
bash复制hdc shell aa start -a EntryAbility -b com.example.kuiklyoh
-b 参数是 bundleName,需要在 ohosApp/entry/src/main/module.json5 里确认。启动后,可以在远程桌面画面上看到应用界面,如果按钮点击有反应,说明基本流程已经通了。
调试时最常用的是看日志。OpenHarmony 的日志命令是 hilog,用法类似 Android 的 logcat:
bash复制hdc hilog
如果你只想观察应用自己的日志,可以加过滤条件:
bash复制hdc hilog | grep Kuikly
如果某个组件渲染出现问题,日志里会打印 Kuikly 的渲染线程信息,帮助定位是 UI 代码问题还是系统资源问题。在实际操作中,我建议先开日志再启动应用,这样应用启动期间的完整日志不会丢。
4.4 真机上的性能表现验证
部署完成只是第一步,我还在华为云远程真机上简单验证了性能和稳定性。通过 hdc 可以查看应用进程是否存活:
bash复制hdc shell ps -ef | grep kuikly
如果想看 CPU 和内存占用,可以执行:
bash复制hdc shell top -n 1
我第一次跑的时候,应用冷启动大概 1.2 秒,内存占用 80MB 左右,对于这样一个简单页面来说中规中矩。KuiklyUI-OH 的渲染引擎会把 UI 组件映射成 OpenHarmony 的原生组件,理论上不会像 WebView 那样吃太多内存。
需要注意一点:云真机是共享资源,性能表现只能作为参考。如果要测试极限性能,最好用本地真实设备。但拿来做功能验证和早期性能评估,云真机已经完全够用了。
5. 常见问题与排查技巧
5.1 编译期常见错误
我 Day1 遇到的编译错误不少,这里整理一个排查速查表:
| 错误现象 | 可能原因 | 解决办法 |
|---|---|---|
Unresolved reference: ohosTarget |
Kuikly Gradle 插件没有解析到 | 检查插件版本和配置,确认插件在 settings.gradle.kts 里声明 |
Unresolved reference: Column/Text |
KuiklyUI-OH 依赖没有引入 | 在 commonMain 依赖里加上 kuikly-ui-oh |
Kotlin Gradle plugin version mismatch |
Kotlin 插件和 Gradle 版本不兼容 | 统一 Kotlin 和 Gradle 版本到项目推荐版本 |
Task 'assembleHap' not found |
没有在 ohosApp 模块配置 HAP 构建任务 | 检查 ohosApp 的 AGP/OHOS 插件配置,确认工程结构完整 |
Failed to find OpenHarmony SDK |
OHOS_SDK_HOME 未设置或路径错误 |
检查 local.properties 和系统环境变量 |
最容易忽略的是 ohosApp 模块中没有引入 OpenHarmony 应用插件。如果项目是纯 Kotlin Multiplatform 工程,需要额外配置 OHOS 的 Gradle 插件,否则无法生成 HAP。具体配置可以参考 Kuikly 模板工程里的 build.gradle.kts,一般会有 com.huawei.ohos 或 org.openharmony.gradle.plugin 相关声明。
5.2 连接远程真机时报错
连接云真机常见的错误有三种。第一种是 hdc: command not found,说明 hdc 工具不在 PATH 里。把 $OHOS_SDK_HOME/toolchains 加入 PATH,或者直接指定 hdc 绝对路径运行。
第二种是 No device connected。远程真机在平台侧被释放了,或者连接超时。先回到云真机控制台,确认设备状态还是“已连接”。如果状态正常,重启本地 hdc 服务:
bash复制hdc kill
hdc start
第三种是 hdc server version mismatch。这是因为本机 hdc 和远程端 hdc 版本不一样。去 DevEco Studio 安装目录下找新版 hdc,覆盖本地环境变量指向的旧版。这个问题在本地连接 OpenHarmony 开发板时也经常遇到,远程真机只是把故障场景更放大了。
5.3 应用启动闪退的排查
Day1 我第一次把 HAP 装到云真机上时,点击图标直接闪退。先看日志:
bash复制hdc hilog | grep -i error
日志显示是 Ability 启动时找不到默认页面,排查后发现是 module.json5 里 pages 路径配置错误。Kuikly 工程的 EntryAbility 需要加载 common 模块的组件,但页面路由配置必须写在 main_pages.json 里,如果路径少了一层目录,就会启动失败。
另一个常见原因是 so 库不匹配。如果 HAP 打包时没有包含正确的 CPU 架构 so 文件,真机上会报 dlopen failed。OpenHarmony 设备目前主要是 ARM 架构,如果编译时只打了 x86_64 的包,远程真机自然跑不起来。配置 build 时记得同时构建 arm64-v8a 或对应架构。
还有一种情况是权限问题。如果页面代码访问了网络或存储,而 module.json5 里没有在 requestPermissions 中声明,系统会在启动时直接拒绝。提前把用到的权限都加上,能省掉不少排查时间。
5.4 一些提升效率的小工具
除了常规命令,我推荐几个能明显提升效率的小工具组合。第一个是 hdc shell hilog 配合 grep 做实时过滤,比 DevEco Studio 的日志面板更轻更快。第二个是 Gradle 的并行编译和配置缓存:
bash复制./gradlew :ohosApp:assembleHap --parallel --configuration-cache
这样连续构建时能省下不少时间。第三个是远程真机的“截图”功能,在云真机平台上定时截图,可以快速记录界面状态,方便和测试同学同步问题。最后,建议把 hdc 常用命令封装成脚本,比如 install-hap.sh、start-app.sh,团队内部复用起来效率更高。
6. 踩了几次坑之后的几点体会
6.1 环境问题大多是版本问题
Day1 花的时间里,真正写代码的时间不多,大部分都耗在环境依赖版本上。Kuikly-OH 毕竟是较新的框架,对 JDK、Kotlin、Gradle、OpenHarmony SDK 的版本组合有要求。我的建议是严格按照模板工程的版本配置来,不要凭感觉升级到最新。Gradle 插件和 Kotlin 插件如果都装最新版,很容易出现一个编译不过的“地雷组合”。
6.2 命令行走一遍比 IDE 更稳
我最终选择把整个构建和部署流程全部用命令行跑通,而不是依赖 IDE 按钮。因为命令行可以复制、可以写进 CI、可以沉淀成脚本。华为云远程真机的连接也一样,把它当作“远端设备”,本地命令和脚本就能直接复用。这样下次换一台设备、换一个新人加入,执行同样的命令就能得到一样的结果。
如果你打算在一个正式项目里使用 Kuikly-OH,我强烈建议从第一天就维护好构建脚本和部署文档。跨平台开发的难点从来不是某个函数怎么写,而是环境、依赖和流程的一致性。跑通一次以后,后面再扩展平台或者增加页面,就会顺畅很多。
