京东自营 618 + 国补 iPhone 历史最低价

腾讯开源跨平台开发框架 Kuikly:腾讯内部已广泛使用

背景

跨平台开发始终是大前端领域持续探索的重要方向,尤其是随着 Harmony Next的推出,业务对跨平台开发诉求更加强烈。在新的需求场景下,业务希望在更多的场景进行更全面的跨平台开发,这对跨平台框架能力也提出了更高的要求。

在跨端框架中,跨端能力、性能表现和动态化能力,作为开发框架的三个重要维度,一直比较难达到理想的平衡效果。

上图是从动态化和性能两个视角来看当前流行跨端框架在这三者的平衡情况。可以看到,动态语言跨端方案,如类 RN 框架、Web 容器类方案,能做动态化但性能较低。基于 AOT 编译的自绘跨端方案,如 Flutter

、JetBrains Compose(以下简称JB Compose),能达到接近原生的性能但又不能动态化。这些局限,导致很难完美匹配业务诉求。

此外,类RN框架需要跨越 JS <-> 虚拟机 <-> C++ <-> Native 多重技术栈,技术复杂,语言之间也需要多重转换,也影响着性能。Flutter / JB Compose 基于自绘,包体积增量较大,同时自绘引擎初始化也影响其加载性能。

在此技术背景下,Kuikly试图突破现有框架的局限,在跨端、性能、动态化三者之间寻求更优的平衡,目标打造一套去中间层的纯原生跨平台动态化方案,具备原生级性能体验和原生技术栈开发体验。

能力和特性优势

Kuikly 已在腾讯内部大规模应用,目前覆盖 15+ 款 APP 、落地 500+ 业务页面,日均 PV 达亿级。部分业务在鸿蒙端完全采用 Kuikly 进行开发,进而复用到Android 和 iOS ,显著提升了跨端开发效率。

在深入解析 Kuikly 的技术原理之前,让我们先简单了解其在真实业务场景中的特性优势。

一码五端,支持鸿蒙平台

已支持 Android、iOS、鸿蒙、Web、小程序五个平台,其中已开源 Android、iOS 平台,计划 5 月份开源鸿蒙支持,Q2 开源 Web 和小程序支持。

原生级性能体验

得益于 KMP跨平台能力,Kuikly 将 Kotlin 代码编译成各个平台原生产物( .aar/framework等),从而获得接近原生平台的执行性能。

与原生性能对比(高中低端机)

●Kuikly 开发的页面首屏耗时,与原生基本一致;

●在内存方面,由于 Kuikly 基于 KMP 编译及原生渲染,无额外引擎引入,内存增量与原生相差不大。

Kuikly测试数据基于复杂 Feeds 流 Demo 测试,Demo代码已包含在开源代码中

与其他框架性能对比

从整体看,Kuikly 在启动和内存上都具有更好的表现。

实际效果一览

通过与原生录屏对比,可以看到 Kuikly 和原生在体验上几乎没有差别。

Android:

从左至右分别是:原生、Kuikly-内置和Kuikly-动态化。

动图封面

iOS:

从左至右分别是:原生、Kuikly-内置和Kuikly-动态化。

动图封面

Kotlin 语言驱动,纯原生开发工具链

Kuikly 使用 Kotlin 作为开发语言,使用原生 IDE( Android Studio/ VS Code) 和原生性能分析工具,从业务代码到框架代码层,使用统一技术栈完成开发,调试和性能分析,从而实现框架开发技术栈自闭环。

各框架开发模式对比如下:

声明+响应式 DSL

自研声明式 + 响应式 DSL,提升 UI 开发效率。同时,ComposeDSL正在支持中,计划 Q2 正式开源。

支持页面级动态化

Kuikly 支持内置和动态化按需切换,具有页面维度更新、无 hook 稳定性高等优势。动态化模式性能表现上,Android 动态化采用平台产物,性能几乎没有损耗,即便在中低端机仍有接近原生表现。

轻量稳定易维护

框架整体设计精巧、无复杂外部依赖,框架稳定性、可控性和维护性较高。各流行框架安装包增量大小对比:

实现原理和架构

一直以来,团队不断进行各种跨端语言的探索,随着 JetBrains 推出 KMM(Kotlin Multiplatform Mobile,现更名为 KMP)及不断完善 ,我们看到了更优的解决方案:

●Kotlin 是 Android 官方开发语言,天然兼容 Android 工具链和生态,终端开发者上手成本低。

●KMP 的跨平台编译能力将 Kotlin 代码编译为各平台原生产物(如 Android 的 JVM/ART 字节码、iOS 的 Native 二进制),可以支持多端高性能运行。

●另外,KMP 也可以将 Kotlin 编译成 JS/Wasm 等产物,可以实现代码动态更新能力。

这些技术特性与我们需求较为契合,最终成为了 Kuikly 的核心技术基石。

架构图概览

跨端 Core层(核心逻辑):

●DSL 驱动:包含自研的声明+响应式 Kuikly DSL 与标准的 Compose DSL(建设中);

●BuildTree:高性能的 DSL 组件树映射生成 Native UI 树的核心逻辑,包括2棵树映射方案、精细化的 O(1) Diff 更新算法、渲染指令生成等逻辑;

●UI 统一控件:核心是保证多平台一致性,在跨端层重新实现了大量的复杂高阶组件(如ListView、ViewPager、Waterfall等),少量 Native 原子组件则通过统一接口层直接映射到原生;

●布局引擎:通过成熟的 yoga 布局引擎来支持流行的 FlexBox 布局

Native 渲染层(特点是轻量化):

●统一的平台接口层:保证 Native 各平台能力的接口统一;

●原生UI映射:最小化的原子渲染组件,仅 Text、Image、Input、ScrollView 等少量组件放到 Native 层;

●API 实现模块:供各平台扩展统一 API 提供给跨平台层使用;

Core 与 Render 层之间的通信,规避了 KMP 的 actual/expect 直接依赖调用方式,采用callKotlin/callNative 的指令通信方案,实现了 Kotlin 跨端层与 Native 渲染层之间的编译隔离,从而支持跨平台层(Core与业务代码)动态化更新能力。

KuiklyBase基础设施:鸿蒙 Kotlin/Native 适配以支持鸿蒙高性能运行,以及调试、质量监控、发布、组件生态等配套基础设施。

关键技术点解析

两棵树渲染原理

典型的渲染流程包括4个步骤:创建 ViewTree、测量、布局、绘制。为了保留纯原生渲染的体验,绘制这一步是放到了 Native 层,前三步都放在平台无关的 Kotlin 层。

与 Flutter、RN 等基于虚拟 Dom 的三棵树方案相比,Kuikly 采用跨平台 DSL 树直接映射生成 Native 渲染树的方案,实现了更轻量的渲染机制,进一步提升性能表现。

Kuikly的两棵树渲染方案

有了两棵树直调的渲染方案,接下来就是设计渲染指令,实现跨端和原生两棵树打通。如下图所示,不同平台 UI 均可以抽象增删改相关指令,通过调用各平台实现的这些抽象渲染接口,即可构建各端一致 UI 渲染。

高一致性原生渲染方案

跨端框架的渲染主要有两种实现方式:原生(Native 控件)渲染,自绘渲染。前者的代表有 RN 框架,后者的代表有 Flutter 和 Compose。一般来说,原生渲染有更好的开发工具链支持、更贴近原生的交互体验以及更小的安装包占用;而自绘有着更高的跨端一致性,但交互体验无法完全对齐原生。综合考虑,Kuikly 选择了原生渲染。

在过去,原生渲染的原生控件多端行为不一致问题,是跨端开发的一大痛点。Kuikly 在设计之初,就考虑了这个问题,通过轻原生层的设计,原生 UI 仅提供最少量的原子组件,大量高阶组件则通过拼积木的方式,在 Kotlin 跨平台层实现,从而实现组件逻辑的高一致性。

声明式、响应式 DSL

业界的Compose/Swift 等现代声明式、响应式 DSL,它们在开发效率、代码可维护性、运行时性能等方面有着显著优势,Kuikly 借鉴了这套优秀理念,利用 Kotlin 语言的独特优势实现现代化开发体验:

●通过高阶函数和类型安全构建器,实现优雅的声明式语法

●基于属性委托机制,构建自动化的响应式更新系统

这种设计使得数据( Model )变更能够自动触发视图( View )更新,同时保持代码的简洁性和可维护性。以下是一个典型Kuikly页面示例:

Kuikly 起步较早,Jetpack Compose(以下简称 Compose)的普及度不足、依赖要求较高,因此选择自研 DSL 。但随着 Compose 日趋广泛的使用,以及面向 AI 编程、适配业界生态的需要,Kuikly 当前也完成对 Compose DSL 的支持,近期也会逐步开源。

Kuikly 支持 Compose DSL 的整体思路是复用官方 Compose 的组件、布局、动画、事件等高阶能力,仅把渲染层从 Skia 自绘切换到 Kuikly 的跨平台渲染层(原生 View 绘制)。这样既保留了绝大部分原生 Compose 的 API 与能力,也能利用 Kuikly 的跨端渲染层,快速适配到鸿蒙、WebRender、小程序等其他平台;同时也能天然复用 Kuikly 的动态化能力,实现 Compose DSL 语法的动态化更新。

极致渲染性能优化

除了前面讲述的渲染流程,Kuikly 在渲染指令量级、渲染节点量级、渲染指令直出等方面也做了较多细致的优化,以达到更加极致的渲染效果,以下是两个典型优化策略:

精准 Diff

在 UI 框架中,Diff(差异对比)是指通过算法比较新旧组件树的差异,找出需要更新的最小变更集,从而避免全量渲染,提升性能。Kuikly 通过重写Kotlin逻辑语句封装 Patch 指令(条件语句:if/else if/else,循环语句:for, switch语句:when),实现精准 Diff 能力,满足业务极致性能场景需求。

UI 扁平化设计

UI 层级过深会导致测量/布局耗时增加、渲染性能下降,甚至引发卡顿。Kuikly吸收 RN 思路,结合自身现有2棵树设计,实现仅可视节点上屏:

●第一棵树(原型树)引入可渲染节点接口,由子类重写实现是否需要渲染

●对于仅布局的 View 节点重写接口返回为非渲染节点,避免被渲染映射原生控件

●原型树同步渲染过程中,剔除非渲染节点,仅保留可渲染节点,维护 RenderTree 再进行控件1:1映射

展开阅读全文

本文系作者在时代Java发表,未经许可,不得转载。

如有侵权,请联系nowjava@qq.com删除。

编辑于

关注时代Java

关注时代Java