第1章 离线优先架构概览
1.1 什么是离线优先
离线优先,说白了就是让 App 在没有网络时也能正常使用。我刚开始做 Android 开发时,大部分项目都是“在线优先”——网络请求失败就弹 Toast,数据加载转圈圈转半天。后来有一次用户反馈:“你们的 App 进地铁就废了”,我才意识到这问题有多严重。
离线优先的核心思想很简单:本地数据是权威来源。用户的操作先写本地,再尝试同步到服务器。网络只是“加速器”,不是“通行证”。我个人习惯把这种模式叫“乐观 UI”——界面先响应用户,后台再去跟服务器对账。
记住一个原则:用户的每一次点击,都不应该因为网络而卡住。哪怕没有网,也要让用户感到“App 是活着的”。
1.2 离线优先 vs 在线优先
我遇到过不少团队纠结“到底用哪种”。其实没有的好坏,要看场景。下面这张对比表,是我自己总结的:
| 对比维度 | 在线优先 | 离线优先 |
|---|---|---|
| 数据来源 | 服务器 > 本地 | 本地 > 服务器 |
| 首次加载 | 必须联网,否则白屏 | 本地缓存优先,即开即用 |
| 写操作 | 等服务器返回后才更新 UI | 本地立即写入,后台异步同步 |
| 离线可用 | 基本不可用 | 全功能可用 |
| 数据一致性 | 强一致(但用户体验差) | 最终一致(需要冲突处理) |
| 实现复杂度 | 低 | 中等~高 |
你想想看,淘宝、微信、网易云音乐,哪个不是离线优先?用户在地铁追剧、刷朋友圈,网络断了照样能看缓存内容。这就是离线优先的魅力。
1.3 适用场景
不是所有 App 都适合离线优先。我个人判断的标准就一条:用户会不会在没有网络的情况下使用你 App 的核心功能?
- 适合做离线优先:
- 笔记类(Notion、有道云笔记)——用户写笔记不依赖网速
- 待办事项(Todoist、TickTick)——快速记录,后台同步
- 阅读类(微信读书、Pocket)——缓存文章离线看
- 工具类(计算器、记账本)——数据私密且频繁修改
- 不太适合做离线优先:
- 股票行情——数据实时性要求极高,本地缓存没意义
- 视频直播——核心就是实时流,离线了看什么?
- 电商支付——必须等服务器确认才能完成交易
我的建议: 先想清楚 App 的“离线必需功能”是什么。哪怕只有浏览历史记录、用户设置这些轻量数据,也值得用 Room 缓存起来。用户会感谢你的。
1.4 离线优先的核心挑战
搞离线优先,说白了就是在跟“数据一致性”打架。我曾经在一个记账 App 里踩过大坑:用户离线时修改了收入,上线后又同步了一次,导致金额翻倍。嗯,这里要注意——写操作必须幂等。
核心挑战集中在三个方面:
- 冲突处理: 本地和服务器都改了同一条数据,听谁的?常见策略有“最后写入胜利”、“服务器优先”、“合并差异”等。
- 同步时序: 本地操作顺序和服务器操作顺序不一致怎么办?需要用版本号 + 时间戳来协调。
- 本地存储: 怎么存才够快、够稳定?Room 做的很好,但要注意避免在主线程读写数据库。
我曾经踩过的坑: 一开始用 SharedPreferences 存离线数据,后来数据量一大就 ANR。换了 Room + 协程之后,读写速度直接提升 10 倍。别舍不得那点学习成本。
这张图展示了离线优先的完整数据流。你会发现一条关键路径:所有用户操作都先走本地。同步引擎在后台悄悄把本地数据推给服务器,如果服务器返回冲突,再回滚或合并。这个模式我称之为“本地优先,网络兜底”。
嗯,讲到这里,你已经对离线优先有了全局认识。接下来就是动手搭建 Room 数据库,然后配合 Compose 和 ViewModel 把整套流程跑通。准备好了吗?