背景
随着鸿蒙系统的推出,客户端跨平台需求被提升到新高度。单纯的UI跨端已无法满足业务诉求,构建Android、iOS和鸿蒙平台的全跨端APP能够最大程度降低开发成本并提升人效。行业研发模式逐步改进,单周发版成为常态,动态化诉求相对较弱,开发者更希望在保持原生性能的同时使用通用UI开发语言以降低学习成本。
Kotlin与Compose是Google官方推荐的Android开发语言与UI框架,也是深受喜爱的开发方案。相比其他跨端方案,Kotlin Multiplatform(KMP)具备高性能和更灵活的原生交互优点。腾讯视频选择Compose Multiplatform作为全跨端APP基础,尽管该方案最初存在不支持鸿蒙、iOS平台混排能力受限、GC性能一般等问题,但经过持续优化,这些问题已得到解决。现在,团队希望开源这些解决方案,与行业共同推动Compose跨端生态成熟。
特性优势
ovCompose已在腾讯视频鸿蒙平台全面落地,成为鸿蒙首个全跨端APP。同时,KuiklyBase基础能力已在腾讯视频、QQ浏览器、腾讯体育等10多款APP中广泛应用。Android、iOS、鸿蒙三端一码的开发方式大幅提升了业务开发效率。随着鸿蒙系统发展,ovCompose和KuiklyBase未来将进一步扩展到TV和PC端。
鸿蒙高性能
Kotlin鸿蒙适配有JS与Native两种技术方案,KuiklyBase选择了Native方案,因为Kotlin Native(KN)相比JS有更快的执行速度和更好的三端一致性。性能测试数据显示,经过优化,在Compose的“小球碰撞”Demo中,以30 FPS为最低极限,小球数量从600提升到1500(Android平台为1600球),绘制性能提升150%。后续将开放更多优化策略。
鸿蒙三明治架构支持混排
鸿蒙平台采用Skia渲染方案,能够100%支持Compose语法和渲染能力。Skia渲染使用XComponent组件作为画布,通过三明治镂空结构解决了与原生组件的混排问题,原生UI可以展示在Compose上层或下层,满足绝大部分业务需求。同时支持粘贴按钮等安全组件的混排,使得Compose无需申请权限也能使用系统能力。
三端高一致性
在逻辑运行方面,鸿蒙平台采用Kotlin Native方案,解决了Kotlin JS使用TaskPool时跨线程访问约束问题,保持了高度三端一致性。在UI绘制方面,iOS和鸿蒙平台均采用Skia渲染,Android底层使用Skia渲染,应用层暴露Paragraph/Canvas绘制接口。基于Skia封装后的Skiko可以完美还原Android绘制效果,实现三端一致。三个平台均能100%使用Compose控件与绘制能力。
iOS多模态渲染解放混排能力
iOS端大量存量业务模块依赖Compose与原生UI的混合编排能力,灵活混排的技术实现及与原生UI性能标准的对齐是业务Compose化改造成功的关键。Compose Multiplatform官方在iOS端使用Skia加CAMetaLayer实现渲染,优点是与其他端表现一致,但缺点是混排能力较弱且内存占用较高,不适合多个Compose实例并存。
因此,团队考虑了两种方案:指令映射(使用UIKit实现Compose Canvas)和组件映射(将Compose组件映射为Native组件)。组件映射在组件层实现,是常见跨端UI框架设计方案,实现难度较低,但存在维护成本高和多端不一致问题。指令映射在画布层实现,逻辑层级更低更抽象,开发难度较高,但能充分利用UIKit渲染能力对Compose绘制效果实现高还原度。
最终采用自研的指令映射方案解决Compose在iOS上的难题,该方案已在腾讯视频iOS端核心业务场景落地。业务团队可以根据实际场景在基于UIKit的自研指令映射方案或官方Skia渲染方案之间自由切换,并可在Runtime期共存。对于文本渲染,采用Skia将文本渲染成图片,利用CALayer展示,保持了高度一致性。
Kotlin Native内存优化
GC优化
通过GC抑制和分段优化提升性能。在APP处于滑动等高帧率场景时,短暂抑制GC以换取更好流畅度。在不影响帧率情况下,进行更高频次GC以降低PSS水位。分析CMS垃圾回收算法发现其存在两次Stop-The-World暂停,第一次暂停时间较短,第二次Sweep期间暂停较长。利用GC挂起能力,在Vsync时进行挂起,在idle时恢复,从而优化性能。
Sweep优化方面,Kotlin Native GC在Sweep阶段有大量munmap系统调用,导致STW时间过长。通过将munmap移出STW阶段,在STW阶段仅做Page收集,在Resume后集中进行munmap,将第二次STW时间降低到1毫秒以内。
KN堆Dump优化
Kotlin Native支持生成堆内存转储文件用于内存泄漏排查,但Dump过程需暂停所有KN线程,导致界面冻结。针对不同平台采用优化方案:在鸿蒙系统基于Linux内核的fork()系统调用特性,采用“父进程无感知-子进程异步转储”方案实现零延迟内存快照;在iOS系统重新设计堆内存分析流程,在堆冻结阶段将堆内存数据保存到缓存文件,线程恢复后异步从缓存文件读取对象内容并写入Dump文件。优化后,450MB堆内存转储耗时从2.8秒降低到410毫秒,达到线上可用水平。
KuiklyBase组件生态
KuiklyBase提供一系列跨平台组件以支持开发:
- Kotlin Native堆栈还原组件:提供Kotlin异常堆栈还原,方便定位代码行号和方法名。
- Kotlin Native/ArkTS互调用组件:支持ArkTS与Kotlin Native跨语言访问,包括基础类型、闭包、ArrayBuffer等类型互转,统一生命周期管理,支持跨线程同步调用和服务发现。
- 资源管理组件:基于Kotlin Multiplatform构建跨平台原生资源管理解决方案,支持Android、iOS和HarmonyOS,通过构建时生成类型安全的资源访问类实现多平台资源统一管理。
- 原子操作组件:基于Kotlin官方原子操作库,提供AtomicInt、AtomicReference等原子类型,支持原子读写和CAS操作,实现线程安全并发编程。
- 协程组件:基于Kotlin官方协程库,简化异步和并发编程,支持协程构建器、调度器、挂起函数、结构化并发等能力。
- 序列化组件:基于Kotlin官方多平台序列化库,支持高效、类型安全的对象序列化与反序列化,兼容复杂类型。
- 日期时间处理库:基于Kotlin官方日期时间处理库,简化跨平台日期和时间操作,支持时区处理和时间运算。
- IO库:基于Square提供的高效I/O库,简化输入输出操作,提供缓冲区管理和流抽象。
- 不可变集合库:基于Kotlin官方不可变集合库,提供线程安全、高效的数据结构。
- 并发集合库:基于StatelyConcurrency提供的并发集合库,简化跨平台状态管理和并发控制。
- Lottie动画库:基于Airbnb开源的跨平台动画渲染库,支持将AE动画转换为JSON文件并在多平台渲染。
- PAG动画流解决方案:腾讯开源的跨平台动画工作流解决方案,支持AE动画导出和运行时编辑。
- 数据库:基于SQLite封装的轻量级关系型数据库,支持标准SQL和事务。
- 工具库:屏蔽平台差异,提供常用工具API,包括App信息管理、设备信息管理、屏幕信息管理、传感器管理等。
- 网络库:提供基于HTTP协议的POST、GET请求能力及关键信息采集。
实现原理
KN鸿蒙平台适配
Kotlin 1.9使用LLVM 11,Kotlin 2.1升级到LLVM 16,但鸿蒙平台支持版本在LLVM 12到15之间。KuiklyBase基于Kotlin 2.0.21进行鸿蒙适配。常规适配思路是分别使用鸿蒙和苹果的LLVM编译,但会导致鸿蒙和iOS平台需依赖不同Kotlin版本。KuiklyBase方案在Kotlin IR转LLVM IR时采用苹果的LLVM 11,在LLVM IR生成可执行文件时使用鸿蒙的LLVM 12,既满足诉求又无需Kotlin架构调整。
KN性能优化
适配后发现卡顿严重,性能评估显示鸿蒙耗时是iOS相同性能机器的2.48倍。通过内联优化和ThreadLocal优化提升性能。内联优化中,发现LLVM IR在内联上更充分,关键函数如EnterFrame在鸿蒙平台优化较少。添加always inline后性能得到显著提升,但仍有差距。分析发现Kotlin和C++代码生成LLVM函数时携带的cpu feature不一致,导致无法内联,配置正确属性后修复。
ThreadLocal优化方面,Kotlin Native在内存分配时依赖ThreadLocal访问线程独立Page,访问频率极高。鸿蒙平台默认采用软件模拟的thread_local,通过编译参数强制使用硬件thread_local后,整体性能提升30%。
协程性能优化
Compose Multiplatform框架的协程调度机制依赖异常处理模型实现任务恢复与取消控制,KN运行时将异常处理桥接至C++异常体系,产生性能损耗。异常触发时需沿调用栈回溯定位捕获点,时间复杂度与调用栈深度正相关,且伴随大量C++异常对象动态构建与析构,加剧时延。鸿蒙系统libhilog.so捕获异常进行处理造成延迟,经沟通优化后解决。最终通过缓存或放弃部分关键位置异常降低处理耗时,使长列表滑动场景稳定在120Hz。
调试性能优化
使用JetBrains的Kotlin Native调试脚本时,调试断点及打印变量耗时远超Native。分析发现KDS与LLDB交互原始简单。通过流程合并、复用、缓存、预加载潜在下一跳和局部调试可容错优化等手段提高通信和处理效率,整体性能提升数倍至几十倍,近似Native。
鸿蒙绘制不同步问题解决
Compose列表混排ArkUI元素滚动时,由于两种组件属于独立绘制层,在鸿蒙系统中存在不同步问题,导致UI衔接处出现空白区域。核心问题是鸿蒙采用集中渲染架构,XComponent的独立绘制模式与ArkUI绘制发生在不同进程,无法保证同步。采用XComponent的Texture模式,将内容绘制到FBO中,由FBO参与原有ArkUI绘制节奏,保证完全同步。
iOS多模态渲染优化
在基于UIKit渲染的基础上,发现CALayer重叠、未正确放置、无法复用等问题。Android采用独立绘制架构,画布为整块,内容通过Skia的PictureRecorder命令录制快速回放。iOS集中渲染架构需工具进行差量处理绘制命令,因此设计了基于iOS的PictureRecorder局部更新架构。
PictureRecorder对绘制命令进行差量,只更新变化部分以提升效率。通过hash判断绘制指令是否变化,但当页面复杂时hash计算成为负担。进一步优化采用增量hash减少计算量,每个draw函数执行时对当前hash和指令id合并计算最终hash,记录完整使用。增量hash减少diff操作,有效降低两次指令相同比较。压力测试中发现OC对象创建和释放耗时被放大,尤其在腾讯视频复杂页面回迁过程中明显,因此将OC对象指令改为简单C结构体,并去掉OC闭包。
优化后,以腾讯视频播放页面为例,首次渲染耗时降低13%,再次渲染耗时降低56%。
与KuiklyUI的差异
跨端框架自渲染与原生渲染在性能和多端适配层面各具优势。为满足业务差异化需求,腾讯大前端Oteam同时探索两个方案:
- 原生渲染方案KuiklyUI:侧重静态化加动态化双运行模式,采用轻量原生渲染保持原生UI体验和高度一致性,基于原生组件映射方式支持Compose API,并计划支持H5和小程序。
- 自渲染方案ovCompose:专注于全面对齐Compose Multiplatform标准API,采用自渲染方式实现鸿蒙平台适配,确保三端高度一致性。针对iOS存量业务,提出多模态渲染方案解决低端iPhone内存紧张、混排原生视图和手势等问题。
开源说明
此次开源包含5个仓库,涵盖ovCompose和KuiklyBase,仓库Group地址为https://github.com/Tencent-TDS。
计划
KMM生态近年来快速发展,Kotlin Native执行性能在许多方面已超越Kotlin JVM,但Compose Multiplatform跨平台技术尚未完全成熟,特别是在GC方面。ovCompose和KuiklyBase将持续优化,重点方向包括GC在业务场景的表现、Kotlin Native组件化、开发体验优化以及UIKit渲染模式进一步对齐Skia渲染,为开发者带来更好体验和更强性能。