Kuikly 是什么?和KMP有什么关系?

Kuikly 是腾讯开源的 Kotlin Multiplatform 跨端框架。

它处理的不是单纯的业务逻辑复用,而是把 UI、布局、事件、Bridge 和部分业务逻辑都放到 KMP 工程里,再分别编译到 Android、iOS、鸿蒙、Web、小程序和 macOS。

它解决什么问题

Android 开发看跨平台框架时,通常会先问两个问题:一份代码能不能跑到多个平台,跑起来以后是不是还能保留接近原生的体验。Kuikly 的设计比较明确,它用 Kotlin Multiplatform 写跨端业务,再用各个平台自己的 Render 层做真正的 UI 渲染。

它的工程里有两个核心概念:KuiklyCoreKuiklyRenderKuiklyCore 负责跨平台的声明式 UI、响应式更新、布局算法和 Bridge 通信;KuiklyRender 负责把 Core 声明出来的控件渲染到具体平台上。Android 上是 Android 渲染器,iOS 上是 iOS 渲染器,鸿蒙、Web、小程序也都有对应实现。

这和很多"写一套 UI,然后用统一引擎画出来"的方案不太一样。Kuikly 更接近一套跨端 UI 协议:业务代码在 KMP 侧声明页面结构和状态变化,平台侧提供承载容器、生命周期、图片、日志、路由、线程等适配能力。

从开源仓库看,Kuikly 目前支持 Android、iOS、HarmonyOS、Web、小程序和 macOS。其中 Web、小程序还标注为 beta,macOS 是 Alpha。系统要求里 Android 是 5.0+,iOS 是 12.0+,HarmonyOS Next 是 5.0.0(12)+。这些版本信息决定了它不是只面向新项目,老 Android 设备也在覆盖范围里。

README 里还有一个信息比较具体:AOT 模式下 Android SDK 增量大约 300 KB,iOS 大约 1.2 MB。这个数字不能直接等同于业务最终包体积,因为还要看具体页面、资源、依赖和平台接入方式,但至少说明它没有把一个很重的通用渲染引擎塞进宿主。

工程结构

Kuikly 的源码结构能看出它的分层。

core 是跨平台模块,里面有 commonMainandroidMainjvmMainappleMainohosArm64MainjsMain。这些目录对应 KMP 的不同 target,最终产物也不同:Android 是 .aar,iOS 和 macOS 是 .framework,鸿蒙是 .so,Web 和小程序是 JS 相关产物。

平台渲染层拆成了 core-render-androidcore-render-ioscore-render-ohoscore-render-web。业务页面用到的 @Page 注解在 core-annotations,注解处理在 core-ksp。这也解释了为什么写页面时要给页面加 @Page("xxx"),因为运行时需要通过页面名创建对应页面。

官方示例里的最小页面大概是这样:

bash 复制代码
@Page("HelloWorld")
internal class HelloWorldPage : Pager() {
    override fun body(): ViewBuilder {
        return {
            attr {
                allCenter()
            }

            Text {
                attr {
                    text("Hello Kuikly")
                    fontSize(14f)
                }
            }
        }
    }
}

这段代码里有三个点。Pager 是 Kuikly 页面的基础类,body() 返回页面内容,@Page("HelloWorld") 把这个页面注册成运行时可打开的页面名。Android 壳工程里输入或传入 HelloWorld,就能打开这个页面。

如果只看这段代码,它和 Compose 的声明式写法有一点像,但它不是直接使用 androidx.compose 包。Kuikly 仓库里有一个 compose 目录,里面基于 Jetpack Compose 1.7.3 做了适配,并把包名从 androidx.compose 改成了 com.tencent.kuikly.compose,用于支持 Kuikly 自己的渲染需求。这一点很关键,不能把它简单理解成"官方 Compose Multiplatform 原样接进来"。

创建项目

新项目的入口在 Android Studio 插件。文档里的路径是:

bash 复制代码
File -> New -> New Project -> Kuikly Project Template

创建后会生成 KMP 业务工程和各端壳工程。常见目录包括 sharedandroidAppiosAppohosApph5AppminiApp 等。业务页面通常先写在 shared/src/commonMain/kotlin/... 下,然后通过各端壳工程运行。

第一次体验可以先跑 Android。创建工程、完成 Gradle sync 后,直接选择 androidApp 运行。iOS 需要先进入 iosApp 执行:

bash 复制代码
pod install --repo-update

H5 的调试路径也比较具体。先启动 dev server,再构建 JS Bundle,最后在浏览器访问本地页面:

bash 复制代码
npm run serve
./gradlew :shared:packLocalJsBundleDebug
./gradlew :h5App:jsBrowserRun -t
./gradlew :h5App:copyAssetsToWebpackDevServer

如果 Kotlin 版本在 2.0 以上,H5 运行命令换成:

bash 复制代码
./gradlew :h5App:jsBrowserDevelopmentRun -t

小程序侧也是先把业务代码编译成 JS,然后用微信开发者工具打开生成目录。Debug 构建常见命令是:

bash 复制代码
./gradlew :shared:packLocalJsBundleDebug
./gradlew :miniApp:jsMiniAppDevelopmentWebpack

这些命令说明 Kuikly 的"多端"不是只在 IDE 里点一个按钮。Android、iOS、鸿蒙、H5、小程序都有自己的壳工程和工具链。KMP 侧页面可以共用,但平台侧的工程配置、签名、调试方式仍然要按平台处理。

接入 Android

如果不是新项目,而是要把 Kuikly 接到已有 Android 工程里,就要分成两件事:KMP 侧接入 KuiklyCore,Android 宿主侧接入 KuiklyRender

Android 宿主模块里需要加渲染器依赖。文档里的写法是:

bash 复制代码
repositories {
    maven("https://mirrors.tencent.com/repository/maven-tencent/")
}

dependencies {
    implementation("com.tencent.kuikly-open:core-render-android:KUIKLY_RENDER_VERSION")
    implementation("com.tencent.kuikly-open:core:KUIKLY_CORE_VERSION")
    implementation("androidx.core:core-ktx:1.7.0")
    implementation("androidx.appcompat:appcompat:1.6.1")
    implementation("com.google.android.material:material:1.8.0")
    implementation("androidx.constraintlayout:constraintlayout:2.1.3")
}

这里有一个容易踩的点:core-render-androidcore 的 Kuikly 版本要和 KMP 跨端工程保持一致。2.5.0 以后还要补腾讯 Maven 源。版本不一致时,问题可能不是编译期直接报错,而是运行时的页面创建、Bridge 或组件行为不一致。

宿主 App 还要提供承载容器。文档示例里是 KuiklyRenderActivity,里面创建 ContextCodeHandler,拿到 KuiklyRenderViewBaseDelegator,然后调用 openPage() 把 Kuikly 页面挂到一个 ViewGroup 里。生命周期也要转发给 delegator:onResume()onPause()onDetach()

精简后可以理解成下面这个结构:

bash 复制代码
class KuiklyRenderActivity : AppCompatActivity() {
    private lateinit var delegator: KuiklyRenderViewBaseDelegator
    private lateinit var contextCodeHandler: ContextCodeHandler
    private lateinit var container: ViewGroup

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        contextCodeHandler = ContextCodeHandler(pageName)
        delegator = contextCodeHandler.initContextHandler()
        container = findViewById(R.id.hr_container)

        contextCodeHandler.openPage(
            this,
            container,
            pageName,
            createPageData()
        )
    }

    override fun onResume() {
        super.onResume()
        delegator.onResume()
    }

    override fun onPause() {
        super.onPause()
        delegator.onPause()
    }

    override fun onDestroy() {
        super.onDestroy()
        delegator.onDetach()
    }
}

然后还要实现平台适配器。必需项包括图片适配器、日志适配器、异常适配器、页面路由适配器和线程适配器。图片加载用宿主已有图片库,日志和异常接入现有监控,路由接入 App 内跳转体系,线程适配接入 Android 主线程和后台线程。

这也是 Kuikly 和纯 UI 库的区别。它不是加一个 View 就结束,真正接进业务工程时,页面生命周期、路由、图片、异常、线程都要和宿主对齐。

和其他跨平台

Android 开发常见的跨平台方案大概有几类:Flutter、React Native、Compose Multiplatform、KMP 逻辑复用,以及 Kuikly。

Flutter 用 Dart 和自己的渲染体系,UI 一致性强,生态成熟,但原生团队要接受独立技术栈和 Flutter Engine。React Native 用 JS/TS 写业务,通过 Bridge 或新架构接平台能力,前端团队接起来更自然,但 Android 原生团队要处理 JS 运行时、包管理和平台交互边界。

Compose Multiplatform 用 Kotlin 和 Compose 写 UI,Android 开发者上手成本低,Desktop 和 iOS 也在持续推进。它更接近 JetBrains/Compose 生态本身,适合想沿着 Compose 技术线做多端 UI 的团队。

Kuikly 的特殊点在几个地方。第一,它基于 KMP,但目标不止逻辑复用,而是 UI + 逻辑一起跨端。第二,它有自研 DSL,也有基于 Compose 适配的 Compose DSL。第三,它把 Core 和 Render 分开,平台侧仍然要接入对应渲染器和适配器。第四,它强调动态化产物,这对大 App 的页面灰度、模块分发会更有吸引力。

所以判断 Kuikly 不能只看"是不是 Kotlin 写 UI"。如果团队已经是 Kotlin/Android 背景,希望一部分页面跑到 iOS、鸿蒙、H5、小程序,同时又不想把业务完全迁到 Dart 或 JS,Kuikly 的路线更贴近这种诉求。如果团队本来就是 Flutter 或 RN 技术栈,或者只需要 Android/iOS 两端统一 UI,它未必是最低成本选择。

最后

Kuikly 可以理解成一套基于 Kotlin Multiplatform 的跨端 UI 框架:KMP 侧写页面和逻辑,平台侧用 Render 承载和渲染,再通过适配器接入宿主能力。

它和 Flutter、RN、Compose Multiplatform 的差异,主要在技术栈、渲染方式、平台覆盖和动态化能力。Android 开发者要试它,先从一个 @Page 页面、一个 Android 容器、几项必需适配器开始,别一上来就改主框架。

#Android #KotlinMultiplatform #Kuikly #跨平台

相关推荐
HokKeung1 小时前
Git Hooks 和 Husky 怎么选
前端·前端工程化
李明卫杭州1 小时前
Vue 3 响应式三剑客:ref、shallowRef、reactive,你真的用对了吗?
前端
触底反弹1 小时前
🔥 JavaScript this 指向全解析:面试必考的 5 大绑定规则
前端·javascript·面试
Csvn2 小时前
TypeScript 类型守卫的 4 个实战级别:从 `filter(Boolean)` 到自定义类型谓词
前端
帅次2 小时前
Android 高级工程师面试:Kotlin 语法基础 近1年高频追问 22 题
android·面试·kotlin·扩展函数·空安全
骑士雄师2 小时前
langchain:推荐切片策略
服务器·前端·langchain
云浪2 小时前
拆开 WAV,看见声音——JavaScript 二进制数组实战全解析
前端·javascript·ecmascript 6
_瑞3 小时前
深入理解灵动岛
前端·ios·app
想你依然心痛3 小时前
嵌入式日志系统:分级日志、环形缓冲区与远程输出——运行时调试、非侵入
android·前端·javascript