24、Room与DataStore对比:SharedPreferences问题、DataStore简介、Room vs DataStore、适用场景
做Android开发久了,你会发现一个很有意思的现象——很多老技术不是被更牛的技术淘汰的,而是被“用起来太别扭”给淘汰的。SharedPreferences就是个典型例子。
今天这篇,我想跟你聊聊数据存储的选型问题。Room和DataStore,到底该用哪个?别急,我们先从SharedPreferences的“罪状”说起。
SharedPreferences的三大痛点
说实话,我刚入行那会儿,项目里清一色全是SharedPreferences。存个用户token、存个开关状态,简单粗暴。但后来踩的坑多了,我才意识到它的问题有多严重。
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")]
}
看到没?全程都是异步的,不会阻塞主线程。而且它内部用了事务机制,保证了数据一致性。
Room vs DataStore:到底有什么区别?
很多新手会问:Room和DataStore都是存储,选哪个?其实它们根本不是一个赛道上的东西。
我画了一张对比图,帮你快速理解:
说白了,Room是重型武器,适合管理复杂的数据关系。DataStore是轻骑兵,适合存配置、偏好、简单状态。
适用场景:什么时候用哪个?
我根据实际项目经验,整理了一个选型指南:
| 场景 | 推荐方案 | 原因 |
|---|---|---|
| 用户登录信息(token、userId) | DataStore | 简单键值对,不需要关联查询 |
| 应用设置(主题、语言、通知开关) | DataStore | 轻量、异步、原子操作 |
| 列表数据(商品、文章、联系人) | Room | 需要CRUD、排序、过滤、分页 |
| 缓存网络数据 | Room | 支持复杂查询和缓存策略 |
| 多进程共享数据 | Room(或ContentProvider) | DataStore不支持多进程 |
| 简单计数器、标记位 | DataStore | 杀鸡不用牛刀 |
实际项目中的选型经验
我记得去年重构一个电商项目时,遇到了一个典型场景:用户购物车数据。一开始用的是SharedPreferences存JSON字符串,结果用户反馈购物车经常丢数据、同步慢。后来我改成了Room,用实体类+DAO来管理,问题迎刃而解。
但反过来,如果只是存一个“是否首次启动”的标记,你用Room建一张表、写一个DAO、再写一个Repository……是不是有点小题大做了?
所以我的原则很简单:
- 数据量小、结构简单、无关联 → DataStore
- 数据量大、结构复杂、需要查询 → Room
- 两者混用完全没问题——一个项目里同时用Room和DataStore很常见
好了,关于Room和DataStore的对比就聊到这里。选型没有绝对的对错,关键看场景。你想想看,用对了工具,代码写起来顺手,用户用起来也流畅,这才是我们追求的目标。
公众号:蓝海资料掘金营,微信deep3321