【开源鸿蒙跨平台开发–KuiklyUI–04】 深度解析 KuiklyUI-mini:打造轻量级跨平台渲染引擎模板

前言

随着 OpenHarmony 的推进,跨平台开发框架再次成为热点。腾讯开源的 Kuikly 渲染引擎以其高性能和灵活性受到了不少关注。然而,官方提供的完整模板项目往往包含复杂的业务逻辑和庞大的依赖体系,对于初学者或者想要快速集成到现有项目的开发者来说,上手门槛略高。

为了解决这个问题,我基于官方模板剥离出了 KuiklyUI-mini —— 一个专注于核心渲染能力的精简版脚手架。本文将带大家深入剖析这个项目,看看如何快速构建一个鸿蒙跨平台应用。

为什么需要 KuiklyUI-mini?

官方示例通常展示了“大而全”的最佳实践,涵盖了网络库封装、复杂路由、状态管理等。但在实际开发中,我们往往只需要最核心的 渲染(Render)桥接(Bridge) 功能。

KuiklyUI-mini 的设计理念就是 “Less is More”

  • 去繁就简:移除非必要的业务代码,只保留核心渲染链路。
  • 结构清晰:重构目录结构,让 Android、iOS、HarmonyOS 三端关系一目了然。
  • 快速上手:配置好环境即可运行,适合作为学习 Kuikly 原理的“最小可运行单元”。

项目架构概览

项目采用典型的 Monorepo 风格管理多端代码:

KuiklyUI-mini
├── ohosApp             // 鸿蒙主工程 (ArkTS) - 核心关注点
│   ├── entry           // 渲染入口与桥接实现
│   └── ...
├── androidApp          // Android 原生工程 (Kotlin)
├── iosApp              // iOS 原生工程 (Swift/Objective-C)
└── buildSrc            // 统一构建版本管理

环境要求紧跟最新标准:

  • DevEco Studio: 5.0+
  • OpenHarmony API: 12+
  • JDK: 17

核心代码实战解析

1. 渲染引擎的“心脏”:Index.ets

在鸿蒙端,Index.ets 是承载 Kuikly 渲染视图的容器。不同于官方的复杂封装,这里展示了最原始的初始化过程:

// entry/src/main/ets/pages/Index.ets

Stack() {
  Kuikly({
    pagerName: this.pageName ?? 'router',
    pagerData: this.pageData ?? {},
    delegate: this.kuiklyViewDelegate, // 注入自定义代理
    nativeManager: globalNativeManager, // 注入原生管理器
    onRenderException: (exception: Error, reason: ErrorReason) => {
      // 统一异常捕获
      this.exception = `${exception.name}:\n${exception.message}`;
    },
  })
}

关键点

  • 通过 Kuikly 组件直接挂载渲染树。
  • delegate 负责将原生组件(Native View)暴露给 JS 端。
  • nativeManager 负责原生模块(Native Module)的方法调用。

2. 自定义组件桥接:KuiklyViewDelegate

如何让 JS/DSL 能够使用鸿蒙的原生组件?秘密就在 KuiklyViewDelegate 中。

// entry/src/main/ets/kuikly/KuiklyViewDelegate.ets

export class KuiklyViewDelegate extends IKuiklyViewDelegate {
  getCustomRenderViewCreatorRegisterMap(): Map<string, KRRenderViewExportCreator> {
    const map = new Map();
    // 注册组件:将字符串 'KRMyView' 映射到原生类
    map.set(KRMyView.VIEW_NAME, () => new KRMyView());
    return map;
  }
  // ... 模块注册同理
}

3. 原生组件实现:KRMyView

KRMyView 演示了如何包装一个 ArkUI 组件并暴露属性。

// entry/src/main/ets/kuikly/components/KRMyView.ets

@Observed
export class KRMyView extends KuiklyRenderBaseView {
  static readonly VIEW_NAME = 'KRMyView';
  cssMessage: string | null = null; // 对应 DSL 中的 message 属性

  // 1. 创建 ArkUI 视图内容
  createArkUIView(): ComponentContent<KuiklyRenderBaseView> {
    const uiContext = this.getUIContext() as UIContext;
    return new ComponentContent(uiContext, wrapBuilder(createMyView), this);
  }

  // 2. 响应属性更新
  setProp(propKey: string, propValue: KRAny): boolean {
    if (propKey === 'message') {
      this.cssMessage = propValue as string;
      return true; // 消费该属性
    }
    return super.setProp(propKey, propValue);
  }
}

// 3. 实际的 ArkUI UI 描述
@Component
struct KRMy {
  @ObjectLink renderView: KRMyView;
  build() {
    Column() {
      Text(this.renderView.cssMessage)
        .textAlign(TextAlign.Center)
    }
  }
}

这种模式非常清晰:KRMyView 负责逻辑映射,KRMy 负责 UI 绘制,完美契合 ArkUI 的声明式开发范式。

实战:如何将官方 KuiklyUI-main 精简为 Mini 版

如果你也想从零开始构建一个类似的精简工程,或者想把现有的 Kuikly 项目瘦身,请严格按照以下步骤操作。我们将把官方那个庞大的单体仓库(Monorepo)改造成一个轻量级的应用工程。

第一步:做减法 —— 移除源码依赖

官方 KuiklyUI-main 项目包含了框架本身的所有源码(Core, Compose, Render)。作为应用开发者,我们不需要维护框架源码,只需引用编译好的制品。

  1. 删除模块:直接删除根目录下的 corecomposecore-render-*core-annotationscore-ksp 以及 demo 目录。
  2. 保留核心:只保留 ohosAppandroidAppiosAppbuildSrc 和根目录的构建脚本。

第二步:HarmonyOS 端改造 (ohosApp)

这是最关键的一步,我们需要将“源码引用”改为“NPM 包引用”。

1. 修改依赖配置

打开 ohosApp/entry/oh-package.json5,将本地源码路径替换为官方发布的 NPM 包。

// 修改前
"dependencies": {
  "libentry.so": "file:./src/main/cpp/types/libentry",
  "render": "file:../../core-render-ohos" // 指向本地源码
}

// 修改后
"dependencies": {
  "libentry.so": "file:./src/main/cpp/types/libentry",
  "@kuikly-open/render": "2.7.0" // 👈 替换为官方 NPM 包
}

注意:执行 ohpm install 确保依赖安装成功。

2. 移植核心桥接代码

由于删除了 demo 模块,我们需要把原 Demo 中用于桥接的关键代码“偷”过来,放到 entry/src/main/ets/kuikly 目录下:

  • Adapter: 复制 KRLogAdapter, KRRouterAdapter 等基础适配器。
  • Delegate: 创建 KuiklyViewDelegate.ets,用于注册你的自定义组件。
  • NativeManager: 复制 MyNativeManager.ets,这是 JS 调用 Native 能力的通道。
3. 重写入口页面

官方的 Index.ets 加载了复杂的 Demo 路由。我们需要将其重写为最简单的形态:

// entry/src/main/ets/pages/Index.ets
import { Kuikly } from '@kuikly-open/render';

@Entry @Component struct Index {
  // ... 初始化 Delegate 和 NativeManager
  build() {
    Stack() {
      Kuikly({
        pagerName: 'router', // 默认加载的页面名
        delegate: this.delegate,
        nativeManager: this.nativeManager
      })
    }
  }
}

第三步:Android & iOS 端改造

移动端的改造逻辑类似,主要是移除 Gradle/Podfile 中的 :core 模块依赖。

  • Android (androidApp/build.gradle.kts):
    dependencies {
        // implementation(project(":core-render-android")) // ❌ 移除源码依赖
        implementation("com.tencent.kuikly:render-android:2.7.0") // ✅ 改为 Maven 依赖
    }
    
  • iOS (iosApp/Podfile):
    移除对本地 core-render-ios 的 path 引用,改用 Pod 远程依赖(或直接引用 Framework)。

第四步:构建脚本瘦身 (buildSrc)

官方 buildSrc 里藏了很多发布逻辑(Publishing)。

  • 打开 buildSrc/build.gradle.kts,移除所有 maven-publish 相关的插件和 Task。
  • 精简 Dependencies.kt,只保留 App 运行所必须的库(如 Kotlin Stdlib, Coroutines)。

成果验收

完成以上步骤后,你的项目目录应该长这样,清爽无比:

KuiklyUI-mini/
├── androidApp/      # 纯净的 Android 工程
├── iosApp/          # 纯净的 iOS 工程
├── ohosApp/         # 纯净的 HarmonyOS 工程
│   ├── oh_modules/  # 依赖已指向 @kuikly-open/render
│   └── entry/       # 业务代码
└── build.gradle.kts

现在,你拥有了一个可以快速启动的 Kuikly 跨平台脚手架,所有的改动都聚焦在业务本身,不再受框架源码干扰。

进阶:跨平台业务逻辑 (Shared)

在精简后的项目中,我们新建了一个名为 shared 的 Kotlin Multiplatform (KMP) 模块。这是 Kuikly 架构的灵魂所在——“一套 Kotlin 代码,多端原生运行”

1. 模块定位

shared 模块不依赖于任何特定的平台 UI(如 Android XML 或 iOS Storyboard),而是使用 Kuikly 提供的 声明式 DSL 来描述界面。这些代码会被编译成:

  • Android: .aar
  • iOS: .framework 静态库
  • HarmonyOS/Web: .js 产物 (通过 JS 引擎运行)

2. 实战:用 Kotlin 写一个跨平台页面

shared/src/commonMain/kotlin 下,我们可以定义通用的业务页面。以下是 RouterPage.kt 的核心逻辑,它展示了如何用 Kotlin 描述一个包含 Header 和列表的页面:

// shared/src/commonMain/kotlin/com/goway/kuiklymini/RouterPage.kt

@Page("router") // 1. 定义路由名称
internal class RouterPage : BasePager() {

    // 2. 响应式数据定义
    private var features by observableList<String>()

    override fun created() {
        super.created()
        // 3. 数据初始化
        features.addAll(listOf("⚡️ 原生性能", "🎯 多端一致", "🎨 声明式 UI"))
    }

    // 4. UI 构建 (DSL)
    override fun body(): ViewBuilder {
        val ctx = this
        return {
            View { // 根容器
                attr {
                    flex(1f)
                    backgroundColor(Color(0xFFF5F7FA))
                }
                
                // 顶部 Header
                View {
                    attr { alignItemsCenter(); marginTop(60f) }
                    Text {
                        attr {
                            text("KuiklyUI")
                            fontSize(36f)
                            fontWeightBold()
                        }
                    }
                }

                // 列表渲染
                vfor({ ctx.features }) { item, index ->
                    Text {
                        attr {
                            text(item)
                            fontSize(16f)
                            padding(10f)
                        }
                    }
                }
            }
        }
    }
}

3. 多端集成方式

精简后的项目通过以下方式集成这个 shared 模块:

  • Android (androidApp):
    build.gradle.kts 中直接引用:

    implementation(project(":shared"))
    
  • iOS (iosApp):
    通过 CocoaPods 自动集成编译好的 Framework:

    # Podfile
    pod 'shared', :path => '../shared'
    

这样,你只需在 shared 模块中修改一次代码,Android 和 iOS 端的 UI 就会同时更新,真正实现了业务逻辑的复用。

4. 鸿蒙构建脚本对比

由于 HarmonyOS 目前对 Kotlin Native 的支持需要特殊的编译链配置,构建脚本是跨平台开发的关键一环。我们对比了官方 KuiklyUI-main 和精简版 KuiklyUI-mini 的构建脚本,看看简化了哪些部分。

差异概览
特性 官方 Demo 脚本 (2.0_ohos_demo_build.bat) 精简版脚本 (2.0_ohos_build.bat)
Gradle 版本管理 包含 Gradle 版本降级/恢复逻辑 (8.0 <-> 8.x) 移除,直接使用项目当前 Gradle 版本
构建目标 :demo:linkSharedDebugSharedOhosArm64 :shared:linkDebugSharedOhosArm64
产物路径 demo/build/bin/... shared/build/bin/...
复杂度 高 (120+ 行,含环境检查、版本切换、备份恢复) 低 (100 行,专注编译与复制)
关键代码对比

1. 构建命令简化

官方脚本为了兼容性,在构建前会强制切换 Gradle 版本,这在单一项目中是不必要的:

REM 官方脚本:包含复杂的 Gradle 版本切换逻辑
echo [Step 2] Switch gradle version to 8.0...
powershell -Command "(Get-Content gradle\wrapper\gradle-wrapper.properties) ..."
call gradlew.bat -c settings.2.0.ohos.gradle.kts :demo:linkSharedDebugSharedOhosArm64

精简版脚本则直接执行构建,假设环境已就绪:

REM 精简版脚本:直接构建 shared 模块
echo [Step 1] Building ohos artifact...
REM 这里的 target 变更为 :shared 模块
call gradlew.bat -c settings.ohos.gradle.kts :shared:linkDebugSharedOhosArm64 --stacktrace

2. 产物复制路径调整

由于模块结构变化,.so 动态库和 .h 头文件的源路径也发生了改变:

REM 官方脚本:从 demo 模块复制
set TARGET_SO_PATH=demo\build\bin\ohosArm64\sharedDebugShared\libshared.so

REM 精简版脚本:从 shared 模块复制
set TARGET_SO_PATH=shared\build\bin\ohosArm64\debugShared\libshared.so

这个脚本的精简,使得开发者在调试鸿蒙侧 C++ 互操作时,能够更直观地理解编译流程,减少了因环境切换脚本出错导致的构建失败。

5. Shared 模块构建脚本对比

官方项目中的 demo 模块对应 Mini 项目中的 shared 模块。通过对比这两个文件的构建脚本 (build.gradle.kts),我们可以清晰地看到“源码依赖”向“制品依赖”的转变。

1. 插件与依赖简化

官方 Demo (demo/build.gradle.kts):
严重依赖本地 core 模块源码,且包含 Compose Multiplatform 相关插件。

plugins {
    // ...
    kotlin("plugin.compose") // 依赖 Compose 插件
    id("org.jetbrains.compose")
}

kotlin {
    sourceSets {
        val commonMain by getting {
            dependencies {
                implementation(project(":core")) // 👈 源码依赖
                implementation(project(":compose"))
                // ...
            }
        }
    }
}

精简版 Shared (shared/build.gradle.kts):
移除 Compose 插件(使用 Kuikly DSL),并将依赖替换为官方发布的 Maven 包。

plugins {
    kotlin("multiplatform")
    id("com.android.library")
    id("com.tencent.kuikly-open.kuikly") // 👈 核心插件
}

kotlin {
    sourceSets {
        val commonMain by getting {
            dependencies {
                // 👇 替换为 Maven 依赖
                implementation("com.tencent.kuikly-open:core:${Version.getKuiklyVersion()}")
            }
        }
    }
}
2. 目标平台裁剪

官方 Demo 为了展示全平台能力,配置了 macos 等目标,而 Mini 版专注于移动端(Android/iOS/HarmonyOS)。

  • 移除: macosX64(), macosArm64()
  • 保留: androidTarget, iosX64, iosArm64, js(IR) (用于鸿蒙)

这种裁剪使得构建速度更快,配置更简单。

6. Gradle 版本升级对比

官方 KuiklyUI-main 使用的是较旧的 Gradle 7.6.3,而 KuiklyUI-mini 为了适配最新的 DevEco Studio 5.0+ 和 Android Gradle Plugin 8.x,将 Gradle 升级到了 8.5。

官方 Main (gradle/wrapper/gradle-wrapper.properties):

distributionUrl=https://services.gradle.org/distributions/gradle-7.6.3-bin.zip

精简版 Mini (gradle/wrapper/gradle-wrapper.properties):

distributionUrl=https\://services.gradle.org/distributions/gradle-8.5-bin.zip

这一升级带来了更好的构建性能和对 JDK 17 的原生支持,但也要求开发者确保本地环境(JDK 版本)同步更新。

7. 工程目录结构对比

为了更直观地展示“瘦身”效果,我们将改造前后的目录结构进行了对比。

改造前 (KuiklyUI-main)
包含完整的框架源码 (core-*) 和复杂的 compose 模块,不仅体积大,而且编译时间长。

KuiklyUI-main
├── androidApp         // 原生 Android 壳工程
├── buildSrc           // 复杂的构建逻辑
├── compose            // Compose Multiplatform 源码 (移除)
├── core               // Kuikly 核心源码 (移除)
├── core-annotations   // 注解源码 (移除)
├── core-render-*      // 各端渲染实现源码 (移除)
├── demo               // 官方示例业务代码 (重构为 shared)
├── docs               // 文档 (移除)
└── ohosApp            // 鸿蒙壳工程

改造后 (KuiklyUI-mini)
只保留了应用开发所需的“壳”工程和核心业务模块 (shared),结构清晰,聚焦业务。

KuiklyUI-mini
├── androidApp         // 纯净的 Android 壳工程
├── iosApp             // 纯净的 iOS 壳工程
├── ohosApp            // 纯净的 HarmonyOS 壳工程
├── shared             // 核心业务模块 (原 demo 精简)
│   └── src/commonMain // 跨平台 UI 代码 (Kotlin DSL)
├── buildSrc           // 精简后的构建逻辑
└── build.gradle.kts   // 根构建脚本

在这里插入图片描述
在这里插入图片描述

通过这种结构优化,我们将一个框架级的仓库成功降级为一个应用级的脚手架。

项目地址

项目我放在了AtomGit平台 KuiklyUI-mini
在这里插入图片描述

总结

KuiklyUI-mini 并不是要替代官方模板,而是提供另一种选择。
如果你正在寻找一个干净的起点来探索鸿蒙跨平台渲染,或者需要一个轻量级的 Demo 来验证技术方案,那么这个项目绝对值得一试。

未来计划:

  • ohos x86架构模拟器适配


关于作者
Goway_Hui,一名热衷于跨平台技术与鸿蒙开发的探索者。

如果觉得本项目对你有帮助,欢迎点赞收藏!

Logo

作为“人工智能6S店”的官方数字引擎,为AI开发者与企业提供一个覆盖软硬件全栈、一站式门户。

更多推荐