[Kotlin协程深度]suspend与Flow在Repository层的选型艺术
2019年,我接手第一个AndroidKotlin项目时,Repository层的设计让我头疼不已。那时候对suspend和Flow的边界感模糊不清,写出来的代码要么过度设计,要么埋下隐患。三年后的今天,我终于构建起一套清晰的决策框架。
初期的混乱与代价
项目起步阶段,我沿用了Java思维,凡事都追求“observable”。List需求用Flow,Single需求也用Flow,理由很简单:Flow足够灵活,能覆盖所有场景。这种设计在短期内似乎没有问题,但随着业务复杂度提升,问题逐渐暴露。
团队新成员阅读代码时满脸困惑——明明是一个简单的一次性查询,为什么要写成Flow?为什么要额外的取消逻辑?这些不必要的复杂度,直接拖累了团队效率。
关键节点的认知升级
转机出现在2020年的一次重构。当时需要为一个聊天模块添加评论功能,初始设计采用Flow实现实时评论更新。但产品突然调整:评论不再需要实时推送,改为手动刷新。改动波及了三个文件,核心原因就是Flow的订阅生命周期管理被嵌入了业务逻辑。
这次教训让我重新审视suspend和Flow的本质差异:suspend是请求-响应模式,结果返回即结束;Flow是发布-订阅模式,数据流持续推送直到取消。两者的语义不同,适用场景自然不同。
朋友圈场景的精准分类
以朋友圈业务为例,我梳理出明确的分类标准。发送朋友圈是典型的单次操作:用户点击发送按钮,等待服务器响应,流程结束。这是不折不扣的请求-响应场景,suspend是最佳选择。
朋友圈列表则截然不同。用户打开页面后,期望看到最新动态;好友发布新内容时,列表应自动更新;有人点赞评论,数据应实时变化。这是典型的持续观察场景,Flow才能优雅应对。
方法提炼:动词与名词法则
经过大量实践,我总结出一个简单有效的判断标准:操作语义像动词用suspend,像名词用Flow。发布、删除、点赞、评论都是动词,对应一次性动作;列表、动态、通知都是名词,对应持续存在的数据。
这个法则帮助团队新人在5秒内做出正确选择,代码review的争论大幅减少。
ViewModel层的协同策略
ViewModel通常需要可观察状态,StateFlow是标准方案。但这不意味着Repository层必须全部返回Flow。最佳实践是保持数据层的简洁性——suspend用于一次性查询,Flow用于持续观察——然后在ViewModel层通过简单转换将结果适配为StateFlow。
这样做的好处是职责清晰:数据层专注于数据获取语义,ViewModel专注于状态管理语义。过度使用Flow会让数据层背负不必要的生命周期管理负担。
实战决策清单
遇到新接口时,我会依次问三个问题:结果是一次性返回还是持续变化?调用方需要订阅还是只需当前值?生命周期是否需要严格管理?三个问题答案一致时,选择自然清晰。
复杂场景下,不妨先写suspend版本。如果后续发现需要Flow支持,重构成本远低于将Flow简化为suspend。简单优于灵活,语义清晰优于设计过度。
