第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 里踩过大坑:用户离线时修改了收入,上线后又同步了一次,导致金额翻倍。嗯,这里要注意——写操作必须幂等。

核心挑战集中在三个方面:

  1. 冲突处理: 本地和服务器都改了同一条数据,听谁的?常见策略有“最后写入胜利”、“服务器优先”、“合并差异”等。
  2. 同步时序: 本地操作顺序和服务器操作顺序不一致怎么办?需要用版本号 + 时间戳来协调。
  3. 本地存储: 怎么存才够快、够稳定?Room 做的很好,但要注意避免在主线程读写数据库。
我曾经踩过的坑: 一开始用 SharedPreferences 存离线数据,后来数据量一大就 ANR。换了 Room + 协程之后,读写速度直接提升 10 倍。别舍不得那点学习成本。
离线优先架构核心逻辑 本地优先层 网络后备层 用户操作 (Compose UI) Repository 数据协调 Room 本地数据库 远程 API (Retrofit) 同步引擎 (WorkManager) 数据流:UI 操作 → Repository → 本地 Room(立即响应)→ 同步引擎 → 远程 API(后台同步)

这张图展示了离线优先的完整数据流。你会发现一条关键路径:所有用户操作都先走本地。同步引擎在后台悄悄把本地数据推给服务器,如果服务器返回冲突,再回滚或合并。这个模式我称之为“本地优先,网络兜底”。

嗯,讲到这里,你已经对离线优先有了全局认识。接下来就是动手搭建 Room 数据库,然后配合 Compose 和 ViewModel 把整套流程跑通。准备好了吗?