从意大利面代码到Lazarus协议
Source: Dev.to
我并不是因为想要开发软件而开始的。我是因为需要在互联网失效的情况下可靠的系统而开始的。
这是一篇关于在真实世界约束下构建生产级离线优先工程应用的技术事后分析:网络不可靠、低端设备、预算紧张以及法律责任。
为什么离线优先是不可谈判的
K‑First 起始于一个简单的观察:
工地没有可靠的互联网,但工程师仍然需要可靠的数据。
大多数应用假设:
- 网络优先
- 随后缓存
- 同步作为优化
当出现以下情况时,这种模型会崩溃:
- 表单填写过程中连接中断
- 后台进程被终止
- 设备意外重启
- 数据丢失会导致法律或财务后果
因此,从第一天起核心约束就很明确:
即使网络永远无法恢复,应用也必须正常运行。
离线优先不是一个功能,而是一种架构。
Source:
V1: “零燃烧” 单体(2025 年 12 月)
K‑First 的第一个版本只有一个严苛的约束:零燃烧。
- 没有服务器
- 没有付费基础设施
- 除非用户明确付费,否则不进行后台同步
初始架构(以及陷阱)
为了快速迭代,我们构建了一个巨大的单一控制器——本质上是一个 God Class——负责:
- 导航
- 状态管理
- 数据库访问
- UI 编排
所有项目数据在应用启动时就被急切地加载到内存中。
纸面上看似可行,实际却产生了三个严重问题。
1. 内存压力
大型项目意味着大量的内存状态。低端 Android 设备对此并不友好。
2. “被动构建者” 导航
表单通过 Navigator.pop(result) 返回数据。当出现以下情况时会失败:
- Android 杀死后台活动
- 用户通过深度链接返回应用
- 进程在流程中途重新启动
3. “幽灵数据” Bug
用户保存日志……但随后发现这些日志根本没有真正持久化。
这对于工程日志本来说是不可接受的。
到 12 月底,单体架构已经在真实使用中开始崩溃。
第 2 阶段:拉撒路重构(2026 年 1 月)
我们停止了功能开发,启动了内部称为 Operation Clean House 的行动。
目标不是优雅,而是生存能力。
打破上帝类
我们迁移到了严格的 MVVM 结构:
| 层 | 职责 |
|---|---|
| Repository | 持久化 |
| ViewModel | 状态与生命周期安全 |
| UI | 纯渲染 |
- 状态不再通过导航流动。
- 导航不再充当数据传输。
拉撒路协议:自愈本地存储
离线优先应用中最大的风险是静默的数据库损坏。
常见原因:
- 崩溃
- 电池拔除
- OEM 特定的怪癖
拉撒路协议
- 每一次关键的数据库写入都会进行检查点记录。
- 启动时,应用会验证 SQLite 文件。
- 若检测到损坏:
- 将数据库隔离。
- 恢复最近的良好备份。
- 通知用户,但永不让用户面对一个空白的应用。
原则: 部分正确的日志本总好过被抹掉的日志本。
Android 15 与 16 KB 页面大小墙
在 2026 年 1 月,Google Play 拒绝了我们的构建。原因与 Flutter 毫无关系。
- Android 15 引入了对原生库的强制 16 KB 页面大小 要求。
- 我们的加密栈(SQLCipher)不兼容。
解决方案
- 强制升级
sqflite_sqlcipher。 - 添加迁移安全网,以防止现有用户被锁定。
- 小心处理加密数据库头部。
这强化了一个痛苦的事实:移动平台并不是稳定的目标,它们在不断变化。如果你采用离线优先的方式,就必须承担这一责任。
三星 “Zombie Key” 事件(S23 / S24)
症状
三星用户更新应用后看到:
“0 Projects”
没有崩溃,也没有错误——只有一个空状态。
根本原因
三星的硬件托管密钥库(Knox)在冷启动时有时尚未准备好。
我们的应用:
- 请求加密密钥。
- 收到一个 新 密钥。
- 尝试打开已有的数据库。
- 静默失败。
我们把这称为 Zombie Key(僵尸密钥):
- 有效(它存在)
- 真实(它是正确的类型)
- 完全错误(它与已存储的数据库不匹配)
修复方案:Samsung Patience Protocol(三星耐心协议)
我们没有假设存储是瞬时可用的,而是实现了:
- 带指数退避的重试循环(最长约 7.5 秒)。
- 只有在耗尽所有重试后,才将应用视为全新安装。
经验教训: 永远不要假设硬件安全模块会及时唤醒。
Split‑Brain问题与统一核心决策
原始计划
sqflite用于免费用户(仅离线)- PowerSync 用于付费用户(开启同步)
看起来很聪明,但却变成了维护噩梦。
两个引擎意味着:
- 双倍迁移
- 双倍测试
- 双倍故障模式
转向
我们选择了 统一核心:
-
PowerSync 作为同步引擎,遍布所有场景。
-
SQLite 作为唯一的真相来源。
-
同步通过能力切换,而不是通过架构切换。
-
免费用户在离线模式下运行 PowerSync。
-
付费用户只需调用
connect()启用。
这消除了整整一类未来的迁移工作。
工程伦理:信任胜于功能
在合规审查期间,我们发现一些计算器的结果依赖于:
- 主观的土地价值
- 不一致的地方规则
我们将它们移除——不是因为无法实现,而是因为发布法律风险高的计算是不负责任的。
我们还添加了:
- 全球免责声明
- 同意门控分析(DPDP 法案)
- 在信心不足时的硬性关闭开关
工程责任不仅止于正确性,还包括其后果。
最终技术栈(2026 年初)
移动端
- Flutter (Dart)
- MVVM 架构
- SQLite + SQLCipher
- Argon2id 密钥派生
- Firebase (Crashlytics, Auth)
Web
- Astro (SSG,默认零 JS)
- Tailwind CSS
- React islands(仅限计算器)
- Motion One(机械动画)
- Vercel (CI/CD)
- 优先同意的分析加载
这段旅程教会我的事
- 离线优先改变了一切。
- 架构必须是首要特性,而非事后考虑。
- 平台的波动性要求防御性编程。
- 伦理工程是不可妥协的。
道路崎岖,但最终得到的是一个坚韧、可信赖的工具,工程师们即使在网络不可用时也能依赖它。
核心原则
- 存储不是一种优化——它本身就是产品
- 硬件是不可预测的
- 特别是当涉及安全模块时
- 架构债务的累积速度快于功能债务
- 删除功能可能是成熟的标志
- 信任是最昂贵的损失——也是最难重新获得的
结束
此架构现已为 K‑First 提供动力——这是一款离线优先的工程日志本,专为真实现场条件而构建,在可靠性比外观更重要的情况下使用。
如果你正在为物理世界构建工具:
首先假设会出现故障——并设计出让用户永远不必为此付费的系统。