直播APP跨平台架构实践(二):KMP UI 与 Rust 下载引擎协作实践

705 阅读3分钟

一、背景:KMP + Rust 的协同挑战

在上篇中我们提到,项目采用了 KMP + Kuikly 来实现跨端 UI,而底层下载逻辑则由 Rust 实现。这样做的好处是,KMP 提供灵活的多端 UI 框架和 Kotlin 语言生态,Rust 则保证了性能与安全性。

但这种组合也带来了不少挑战:KMP 与 Rust 跨语言边界复杂(Kotlin/Native + FFI); 线程模型不一致,Rust 任务是异步的,而 KMP 要保证在主线程安全更新 UI; 跨端数据结构对齐问题(尤其是 iOS Framework 集成); 下载状态同步延迟与 UI 刷新频率不匹配

二、架构设计:双层跨平台协作模型

我们最终采用了“双层协作”架构:

未命名文件.png

在此架构中:UI 层(KMP) :负责渲染下载任务界面、任务状态、进度条、错误提示; 逻辑层(Rust) :负责真实的文件下载、断点续传、缓存与任务调度; 桥接层(JNI / C-API) :我们在此定义了一套统一的接口规范(包括状态同步、回调分发、错误码映射),保证跨平台一致性。

三、架构设计思路

统一状态流模型

Rust 下载引擎在底层会通过 tokio::mpsc::channel 推送任务状态,而在 KMP 侧,我们用 StateFlow 做统一封装:

// Kotlin ViewModel 层
val downloadState = MutableStateFlow(DownloadState.Idle)

fun observeDownload(taskId: String) {
    rustBridge.subscribe(taskId) { stateJson ->
        val state = json.decodeFromString<DownloadState>(stateJson)
        downloadState.value = state
    }
}

这种 “Rust → KMP → Compose/Kuikly” 的状态流式架构,让 UI 层完全响应式更新,不卡主线程,也便于灰度监控。


跨语言回调安全机制

Rust 调用 Kotlin 层时可能会发生线程切换或生命周期不一致问题。

为此我们在桥接层设计了 回调代理层(Callback Dispatcher)

// Rust 层调用 Kotlin 的桥接方法
extern "C" fn on_progress(task_id: *const c_char, progress: f32) {
    kotlin_notify_progress(task_id, progress);
}
// Kotlin 侧接收并调度到主线程
fun kotlin_notify_progress(taskId: String, progress: Float) {
    mainScope.launch {
        _downloadProgress[taskId]?.emit(progress)
    }
}

保证:所有 UI 更新都安全地在主线程执行;回调生命周期与 KMP 层 ViewModel 解耦; 避免内存泄漏或野指针。

桥接层接口规范化

为了避免 Android/iOS/Harmony 三端分别实现不同桥接逻辑,我们在 Kotlin 层定义了一套中间层协议接口:

interface IRustDownloader {
    fun init(config: DownloadConfig)
    fun startTask(url: String, dest: String)
    fun pauseTask(taskId: String)
    fun observeProgress(taskId: String, listener: (Float) -> Unit)
}

每个平台只需各自注册一套 native 接口即可。这种方式: 降低多端集成复杂度; 便于未来替换引擎(如改用 C++ 或其他方案); 强化工程的可维护性。

四、集成与性能表现

性能上单任务下载性能:比旧的 Java 下载模块提升约 30%–40%并发任务性能:Rust 异步任务模型几乎无锁竞争; 内存占用:峰值降低约 20%包体积增量:约 1.8MB(Android)2.1MB(iOS Framework)

灰度策略采用新老下载引擎共存;通过特征开关(Feature Flag)动态切换; A/B 实验验证 QoE 指标(下载成功率、任务耗时、CPU 占用)。

五、开发中遇到的实际问题

问题解决方案
iOS Framework 编译失败Rust 生成的 .a 静态库与 Swift 组件化工程冲突,改为动态 Framework 并使用 cbindgen 生成头文件。
Harmony 平台 NDK 缺失Kuikly 工具链支持 Harmony,需手动增加 Rust target 并适配 musl 编译链。
Kotlin/Native 回调崩溃增加回调代理层,所有回调都通过主线程调度执行。
下载状态丢失Rust 侧持久化任务信息(SQLite),KMP 层启动时重新同步。

六、总结

这套 KMP + Rust 协作模型 实现了三端 UI 一致、逻辑统一;下载引擎性能与稳定性显著提升;桥接层接口清晰、可扩展性强,为后续的跨平台开发铺设奠定了基础。