Android 本地存储方案深度解析:SharedPreferences、DataStore、MMKV 全面对比
前言:
在 Android 开发中,键值对存储 是最常见的持久化需求之一。
无论是保存登录 Token、主题模式、还是缓存用户偏好,我们常见的方案有三种:
👉
SharedPreferences
👉DataStore
(官方推荐替代)
👉MMKV
(腾讯出品高性能方案)
那么,三者到底有什么区别?该如何选择?本篇将从原理、性能、使用方式、优缺点到最佳实践,带你一文搞懂。
🧩 一、整体对比总览
特性 | SharedPreferences | DataStore | MMKV |
---|---|---|---|
存储方式 | XML 文件 | Preferences / Proto(二进制) | mmap(内存映射 + protobuf) |
线程安全 | ❌ 需自行控制 | ✅ 完全线程安全 | ✅ 完全线程安全 |
异步支持 | 部分异步 (apply ) | ✅ 完全异步(协程) | ✅ 几乎同步(极快) |
性能 | 中等(磁盘 I/O 频繁) | 稍慢(安全但略重) | ⚡️最快(mmap 内存映射) |
跨进程 | ❌ 不支持 | ❌ 不支持(默认) | ✅ 支持 |
数据类型 | 基础类型 | 基础类型 / Proto 对象 | 基础类型 + ByteArray |
文件位置 | /shared_prefs/*.xml | /datastore/*.preferences_pb | /files/mmkv/*.mmkv |
官方推荐度 | ✅ 老项目使用 | ✅ 官方推荐方案 | ✅ 企业级性能首选 |
🧠 二、原理与机制分析
1️⃣ SharedPreferences:老牌 XML 存储
SharedPreferences 是 Android 最早的轻量存储方案,底层基于 XML 文件 实现。
-
写入:
commit()
同步写入磁盘;apply()
异步提交。 -
读取:同步加载 XML 文件解析为内存 Map。
-
问题:在主线程频繁读写会导致卡顿或 ANR。
val sp = context.getSharedPreferences("user_prefs", Context.MODE_PRIVATE)
sp.edit().putString("token", "abc123").apply()val token = sp.getString("token", null)
✅ 优点:
系统自带,API 简单稳定。
无需额外依赖。
❌ 缺点:
XML I/O 成本高。
线程安全性差。
并发写入容易丢数据。
2️⃣ DataStore:Google 官方现代替代方案
DataStore 是 Jetpack 组件之一,用于替代 SharedPreferences。
分为两种模式:
🧾 Preferences DataStore
无需定义结构,直接使用键值对:
val Context.dataStore by preferencesDataStore(name = "settings")
val KEY_TOKEN = stringPreferencesKey("token")suspend fun saveToken(context: Context, token: String) {context.dataStore.edit { prefs ->prefs[KEY_TOKEN] = token}
}val tokenFlow: Flow<String?> = context.dataStore.data.map { prefs -> prefs[KEY_TOKEN] }
🧬 Proto DataStore
基于 Protobuf 定义 Schema,可存储复杂对象:
syntax = "proto3";
message UserProfile {string name = 1;int32 age = 2;
}
然后使用自动生成的 UserProfileSerializer
存储类型化数据。
✅ 优点:
异步、线程安全、无数据丢失。
原生支持 Flow 响应式更新。
官方推荐未来方向。
❌ 缺点:
首次初始化耗时略高。
不支持跨进程。
Proto 模式使用门槛稍高。
3️⃣ MMKV:腾讯出品的极致性能方案
MMKV = Memory Mapped Key-Value
核心原理:mmap 内存映射文件 + protobuf 序列化
数据在内存中映射到磁盘,写入无需频繁磁盘 I/O,性能极高。
MMKV.initialize(context)
val mmkv = MMKV.defaultMMKV()mmkv.encode("token", "abc123")
mmkv.encode("isLogin", true)val token = mmkv.decodeString("token")
✅ 优点:
⚡️极快(毫秒级甚至微秒级)。
✅ 完全线程安全。
✅ 支持多进程、加密存储。
✅ API 简洁易用。
❌ 缺点:
需额外依赖库。
少数场景文件损坏风险(官方有修复机制)。
不是官方组件。
⚡️ 三、性能实测对比(单位:毫秒)
操作 | SharedPreferences | DataStore | MMKV |
---|---|---|---|
写入 1 次 | ~1.5 ms | ~3–5 ms | ~0.1 ms |
读取 1 次 | ~1.2 ms | ~2 ms | ~0.05 ms |
并发写入 | ❌ 阻塞/丢失 | ✅ 协程安全 | ✅ 无锁高效 |
初始化时间 | 快 | 稍慢 | 快 |
👉 结论:
在频繁读写场景(如聊天缓存、游戏状态)中,MMKV 的性能优势极其明显。
🧭 四、选型建议
使用场景 | 推荐方案 |
---|---|
简单用户配置 | ✅ DataStore(Preferences) |
高频缓存 / IM / 游戏 | ✅ MMKV |
响应式 UI(Flow 监听) | ✅ DataStore |
旧项目快速兼容 | ✅ SharedPreferences |
多进程共享数据 | ✅ MMKV |
存储复杂对象 | ✅ Proto DataStore 或 MMKV |
🧰 五、实际项目中的组合使用建议
在成熟项目中,推荐按用途分层存储:
AppSettingStore —— DataStore(语言、主题、隐私设置) AppCache —— MMKV(Token、临时缓存) LegacyConfig —— SharedPreferences(旧数据迁移)
SharedPreferences → DataStore 迁移示例:
context.dataStore.updateData { prefs ->val oldSp = context.getSharedPreferences("old_prefs", Context.MODE_PRIVATE)prefs.toMutablePreferences().apply {this[stringPreferencesKey("token")] = oldSp.getString("token", "") ?: ""}
}
🧪 附录:三种存储方案 Demo 对比与性能测试
📁 一、SharedPreferences 示例
class SpDemo(private val context: Context) {private val sp = context.getSharedPreferences("sp_demo", Context.MODE_PRIVATE)fun saveData(key: String, value: String) {val start = System.currentTimeMillis()sp.edit().putString(key, value).apply()Log.d("SP_TEST", "写入耗时: ${System.currentTimeMillis() - start} ms")}fun readData(key: String): String? {val start = System.currentTimeMillis()val value = sp.getString(key, null)Log.d("SP_TEST", "读取耗时: ${System.currentTimeMillis() - start} ms")return value}
}
📦 二、DataStore(Preferences)示例
依赖:
implementation "androidx.datastore:datastore-preferences:1.1.1"
使用:
val Context.dataStore by preferencesDataStore(name = "datastore_demo")object DataKeys {val KEY_NAME = stringPreferencesKey("name")
}class DataStoreDemo(private val context: Context) {suspend fun saveData(value: String) {val start = System.currentTimeMillis()context.dataStore.edit { prefs ->prefs[DataKeys.KEY_NAME] = value}Log.d("DATASTORE_TEST", "写入耗时: ${System.currentTimeMillis() - start} ms")}fun readData(): Flow<String?> = context.dataStore.data.map { prefs ->val start = System.currentTimeMillis()val value = prefs[DataKeys.KEY_NAME]Log.d("DATASTORE_TEST", "读取耗时: ${System.currentTimeMillis() - start} ms")value}
}
监听更新(响应式 UI):
lifecycleScope.launch {dataStoreDemo.readData().collect { value ->Log.d("DATASTORE_FLOW", "DataStore 监听到新数据:$value")}
}
⚡️ 三、MMKV 示例
依赖:
implementation 'com.tencent:mmkv-static:1.3.4'
使用:
class MMKVDemo(context: Context) {private val mmkv = MMKV.defaultMMKV()fun saveData(key: String, value: String) {val start = System.currentTimeMillis()mmkv.encode(key, value)Log.d("MMKV_TEST", "写入耗时: ${System.currentTimeMillis() - start} ms")}fun readData(key: String): String? {val start = System.currentTimeMillis()val value = mmkv.decodeString(key)Log.d("MMKV_TEST", "读取耗时: ${System.currentTimeMillis() - start} ms")return value}
}
⚙️ 四、统一测试入口(对比三种方案性能)
class StorageCompareActivity : AppCompatActivity() {private lateinit var spDemo: SpDemoprivate lateinit var dsDemo: DataStoreDemoprivate lateinit var mmkvDemo: MMKVDemooverride fun onCreate(savedInstanceState: Bundle?) {super.onCreate(savedInstanceState)spDemo = SpDemo(this)dsDemo = DataStoreDemo(this)mmkvDemo = MMKVDemo(this)lifecycleScope.launch {testPerformance()}}private suspend fun testPerformance() {val testKey = "test_key"val testValue = "Hello Android!"// 1. SharedPreferencesspDemo.saveData(testKey, testValue)spDemo.readData(testKey)// 2. DataStoredsDemo.saveData(testValue)dsDemo.readData().firstOrNull()// 3. MMKVmmkvDemo.saveData(testKey, testValue)mmkvDemo.readData(testKey)}
}
运行后可在 Logcat 中对比输出:
SP_TEST: 写入耗时: 2 ms SP_TEST: 读取耗时: 1 ms DATASTORE_TEST: 写入耗时: 4 ms DATASTORE_TEST: 读取耗时: 3 ms MMKV_TEST: 写入耗时: 0 ms MMKV_TEST: 读取耗时: 0 ms
🧩 五、实测结果总结
操作 | SharedPreferences | DataStore | MMKV |
---|---|---|---|
单次写入 | ~1–2 ms | ~3–5 ms | <0.1 ms |
单次读取 | ~1 ms | ~2 ms | <0.1 ms |
并发安全 | ⚠️ 较差 | ✅ 安全 | ✅ 安全 |
是否异步 | 部分 | ✅ 完全异步 | ✅ 快速同步 |
跨进程 | ❌ 不支持 | ❌ 不支持 | ✅ 支持 |
✅ 六、推荐实践(项目实战结构)
com.example.app.storage │ ├── MMKVDemo.kt → 高频缓存 / 登录状态 ├── DataStoreDemo.kt → 用户设置、隐私选项 └── SpDemo.kt → 旧版本迁移支持
混合方案示例:
object AppStorage {val userPrefs by lazy { DataStoreDemo(App.context) }val cache by lazy { MMKVDemo(App.context) }val legacy by lazy { SpDemo(App.context) }
}
💬 七、总结
方案 | 优点 | 缺点 | 一句话总结 | 适用场景 | 关键特性 |
---|---|---|---|---|---|
SharedPreferences | ✅ 简单、系统原生 | ❌ 性能差、线程不安全、I/O 开销大 | 老牌方案,适合小量配置,性能一般。 | 老项目、低频读写 | 简单、兼容性强 |
DataStore | ✅ 异步、线程安全、支持 Flow 响应式 | ❌ 初始化慢、不支持跨进程 | 官方推荐替代方案,安全、响应式。 | 响应式偏好存储 | Flow + 协程安全 |
MMKV | ⚡️ 极高性能、线程安全、跨进程、支持加密 | ⚠️ 库体积略大、需额外依赖 | 企业级高性能存储方案,快得离谱。 | 高频读写、高性能场景 | mmap + Protobuf,极快 |
如果你的 App 是中小型应用,推荐使用 DataStore;
如果是 IM、社交、游戏类高性能需求,推荐使用 MMKV;
如果是老项目,可以逐步从 SharedPreferences 迁移。
💡 参考资料:
-
Google 官方 DataStore 文档
-
MMKV 官方 GitHub
-
Android Developers Blog