我基于 Kotlin Multiplatform 实现了一套跨平台的弱网离线同步引擎

大家好。日常写客户端业务需求,大家一定对下面这种场景深恶痛绝:

每当我们要做一个表单提交、点赞、取消订单之类的操作,为了处理网络请求,ViewModel 里通常会堆满这样的样板代码:

kotlin 复制代码
fun submitData() {
    _uiState.update { it.copy(isLoading = true) }
    viewModelScope.launch {
        try {
            val response = apiService.submit(...)
            _uiState.update { it.copy(isLoading = false, isSuccess = true) }
        } catch (e: Exception) {
            // 弱网、断网、超时全在这里...
            _uiState.update { it.copy(isLoading = false) }
            showToast("网络连接失败,请稍后重试!")
        }
    }
}

这种"前台阻塞等待网络响应"的范式有几个致命痛点:

  1. 体验极差:用户在弱网下只能看着 Loading 圈发呆,最后等来一个报错。
  2. 状态爆炸 :你需要维护繁琐的 LoadingSuccessError 状态机。
  3. 容易丢数据:如果还没请求成功,App 就被系统杀了,用户的操作就彻底丢失了。

微信发消息、或者一些大厂的高频交互,为什么在地铁里没网的时候也感觉那么流畅?因为它们在底层使用了类似后端的 发件箱模式 (Outbox Pattern)

核心思路就是:不要让 UI 等网络!数据直接在本地数据库落盘,UI 瞬间返回成功。剩下的事,交给后台守护进程去重试。

为了在团队的 KMP 和 Android 项目里优雅地实现这个模式,并且不污染现有的网络代码,我手搓了一个极其轻量级的离线同步引擎开源库 ------ Syncox

什么是 Syncox?

Syncox 是一个专为 Kotlin Multiplatform (KMP)Android 打造的客户端离线同步引擎。

它的核心设计理念是 "零侵入""发后即忘 (Fire-and-Forget)" 。你不需要重构你现有的 Ktor 或 Retrofit 接口,只需要加上一个注解,就能让你的 App 拥有大厂级别的断网容灾能力。

看看接入有多简单

不用学什么复杂的架构概念,只需要三步,你就能体会到干掉 try-catch 的爽快感:

第一步:把数据包一层 (Action)

定义一个数据类实现 SyncoxAction 接口,这一步是为了告诉引擎:"我要存什么数据"以及"这个数据该找哪个接口发"。

kotlin 复制代码
@Serializable
data class CreatePostRequest(val title: String, val body: String, val userId: Int)

class CreatePostAction(request: CreatePostRequest) : SyncoxAction {
    override val actionType: String = "CREATE_POST" // 路由寻址 Key
    override val payloadJson: String = Json.encodeToString(request) // 自动转 JSON 落盘
}

第二步:给原有的网络接口加个注解

这就是 Syncox 最爽的地方。得益于 KSP 编译期代码生成技术 ,你只需要在真实的请求函数上贴个 @OfflineSync业务代码里再也不用手动调用这个函数了,KSP 会自动生成一张零反射的安全路由表,让底层的后台进程自己去找它!

kotlin 复制代码
// 这个方法交由底层引擎自动调度,报错了也会自动重试
@OfflineSync(action = "CREATE_POST")
suspend fun executeCreatePost(payloadJson: String): NetworkResult {
    return try {
        val request = Json.decodeFromString<CreatePostRequest>(payloadJson)
        val response = httpClient.post("posts", request)
        
        if (response.status.isSuccess()) {
            NetworkResult.Success // 成功:引擎会自动从 SQLite 擦除记录
        } else {
            NetworkResult.Failure(isFatal = false) // 服务端临时错:引擎自动触发退避重试
        }
    } catch (e: Exception) {
        NetworkResult.Failure(isFatal = false, error = e) // 断网:引擎进入静默等待
    }
}

第三步:在 ViewModel 里一键发送

现在,你的 ViewModel 干净得只剩下一行代码。没有 Loading,没有 try-catch。

kotlin 复制代码
// 瞬间落盘本地数据库,耗时 < 1ms。UI 线程完全不会卡顿!
Syncox.enqueue(CreatePostAction(request))

// UI 层如果想展示"正在后台同步中",直接拿这个 Flow 绑到 Compose 上就行
val pendingCountFlow: Flow<Int> = Syncox.observePendingCount()

总结与开源地址

如果你也厌倦了在 ViewModel 里处理无穷无尽的网络异常,或者你想让你的 App 在弱网环境下表现得更"高级"、更丝滑,非常建议你试试这套发件箱模式。

目前项目已经完全开源,支持 KMP 的跨端项目(Android、iOS、Desktop)和纯 Android 项目:

👉 GitHub 地址 : github.com/SamiuZhong/...

项目刚刚起步,还在积极迭代中。欢迎各位大佬前来审阅、提 Issue 或者 PR,如果觉得思路对你有启发,求个 Star ⭐ 支持一下!

相关推荐
IvorySQL4 小时前
PG 技术日报|2026-07-04
数据库·人工智能·postgresql·开源
安卓修改大师4 小时前
安卓修改大师反编译引擎Apktool深度解析:版本选择、参数说明与实战技巧
android
error:(4 小时前
【系统与实战双精通】VS Code 调试 ROS2 Python 节点与 Launch 系统指南
android·java·python
IvorySQL5 小时前
IvorySQL HTAP 实时湖仓接入引擎
数据库·postgresql·开源·htap·ivorysql
饼干哥哥5 小时前
我开发了一个「吃掉」别人Skill来升级的饕餮.Skill,现在免费开源
人工智能·设计模式·开源
我是唐青枫5 小时前
Kotlin 运算符重载详解:为什么 a += b 有时改对象,有时换对象?
开发语言·kotlin
黄林晴6 小时前
Kuikly 是什么?和KMP有什么关系?
android·前端
帅次7 小时前
Android 高级工程师面试:Kotlin 语法基础 近1年高频追问 22 题
android·面试·kotlin·扩展函数·空安全
想你依然心痛8 小时前
嵌入式日志系统:分级日志、环形缓冲区与远程输出——运行时调试、非侵入
android·前端·javascript