24、Room与DataStore对比:SharedPreferences问题、DataStore简介、Room vs DataStore、适用场景

做Android开发久了,你会发现一个很有意思的现象——很多老技术不是被更牛的技术淘汰的,而是被“用起来太别扭”给淘汰的。SharedPreferences就是个典型例子。

今天这篇,我想跟你聊聊数据存储的选型问题。Room和DataStore,到底该用哪个?别急,我们先从SharedPreferences的“罪状”说起。

SharedPreferences的三大痛点

说实话,我刚入行那会儿,项目里清一色全是SharedPreferences。存个用户token、存个开关状态,简单粗暴。但后来踩的坑多了,我才意识到它的问题有多严重。

⚠️ 我曾经踩过的坑: 有一次线上反馈说用户登录状态莫名其妙丢失,查了半天,发现是SharedPreferences在多进程环境下数据错乱了。从那以后,我对它彻底失去了信任。

SharedPreferences主要存在三个问题:

  • 同步写入阻塞UI——commit()是同步操作,直接在主线程写磁盘。你想想看,如果数据量大一点,界面卡顿是必然的。
  • 类型不安全——存的时候是String,取的时候忘了强转?运行时直接崩给你看。我见过太多因为getString()取Integer导致的崩溃。
  • 不支持多进程——官方文档明确说了不保证跨进程安全。如果你有多个进程读写同一个SP文件,数据错乱是家常便饭。

嗯,说白了,SharedPreferences就是个小作坊工具,小项目凑合用,一旦规模上来,各种问题就暴露了。

DataStore简介:轻量级数据存储的新选择

Google自己也意识到了SharedPreferences的问题,所以在Jetpack里推出了DataStore。它分两种:

  • Preferences DataStore——键值对存储,类似SharedPreferences,但基于Flow异步实现。
  • Proto DataStore——基于Protocol Buffers的强类型存储,需要定义schema。

我个人习惯用Preferences DataStore来替代SharedPreferences,因为迁移成本最低。来看个例子:

// 依赖
implementation "androidx.datastore:datastore-preferences:1.0.0"

// 创建DataStore
val Context.dataStore: DataStore<Preferences> by preferencesDataStore(name = "settings")

// 写入数据
suspend fun saveUserName(name: String) {
    dataStore.edit { preferences ->
        preferences[stringPreferencesKey("user_name")] = name
    }
}

// 读取数据
val userNameFlow: Flow<String?> = dataStore.data.map { preferences ->
    preferences[stringPreferencesKey("user_name")]
}

看到没?全程都是异步的,不会阻塞主线程。而且它内部用了事务机制,保证了数据一致性。

💡 小技巧: DataStore的edit()函数是原子操作。如果你同时修改多个key,要么全部成功,要么全部回滚。这在SharedPreferences里可做不到。

Room vs DataStore:到底有什么区别?

很多新手会问:Room和DataStore都是存储,选哪个?其实它们根本不是一个赛道上的东西。

我画了一张对比图,帮你快速理解:

Room vs DataStore 核心对比 Room ✅ 关系型数据库(SQLite封装) ✅ 支持复杂查询、关联表 ✅ 支持Flow + LiveData响应式 ✅ 适合结构化、大量数据 ✅ 支持迁移、版本管理 ❌ 配置相对复杂 ❌ 不适合简单键值对 DataStore ✅ 轻量级键值存储 ✅ 基于Flow,完全异步 ✅ 原子事务操作 ✅ 类型安全(Proto版本) ✅ 替代SharedPreferences ❌ 不支持复杂查询 ❌ 不适合大量结构化数据

说白了,Room是重型武器,适合管理复杂的数据关系。DataStore是轻骑兵,适合存配置、偏好、简单状态。

适用场景:什么时候用哪个?

我根据实际项目经验,整理了一个选型指南:

场景 推荐方案 原因
用户登录信息(token、userId) DataStore 简单键值对,不需要关联查询
应用设置(主题、语言、通知开关) DataStore 轻量、异步、原子操作
列表数据(商品、文章、联系人) Room 需要CRUD、排序、过滤、分页
缓存网络数据 Room 支持复杂查询和缓存策略
多进程共享数据 Room(或ContentProvider) DataStore不支持多进程
简单计数器、标记位 DataStore 杀鸡不用牛刀
📌 我的个人建议: 如果一个数据只需要“存”和“取”,没有复杂的查询需求,优先用DataStore。一旦涉及到“查某条数据”、“按条件排序”、“关联多张表”,直接上Room。别犹豫,犹豫就会败北。

实际项目中的选型经验

我记得去年重构一个电商项目时,遇到了一个典型场景:用户购物车数据。一开始用的是SharedPreferences存JSON字符串,结果用户反馈购物车经常丢数据、同步慢。后来我改成了Room,用实体类+DAO来管理,问题迎刃而解。

但反过来,如果只是存一个“是否首次启动”的标记,你用Room建一张表、写一个DAO、再写一个Repository……是不是有点小题大做了?

所以我的原则很简单:

  • 数据量小、结构简单、无关联 → DataStore
  • 数据量大、结构复杂、需要查询 → Room
  • 两者混用完全没问题——一个项目里同时用Room和DataStore很常见
⚠️ 注意: 千万不要把DataStore当成小型数据库来用。它内部是单文件存储,数据量大了以后读写性能会急剧下降。我见过有人把几百条商品数据塞进DataStore,结果每次edit()都要序列化整个文件,卡得不行。

好了,关于Room和DataStore的对比就聊到这里。选型没有绝对的对错,关键看场景。你想想看,用对了工具,代码写起来顺手,用户用起来也流畅,这才是我们追求的目标。


公众号:蓝海资料掘金营,微信deep3321