当前位置: 首页 > news >正文

Android 本地存储方案深度解析:SharedPreferences、DataStore、MMKV 全面对比

前言:

在 Android 开发中,键值对存储 是最常见的持久化需求之一。
无论是保存登录 Token、主题模式、还是缓存用户偏好,我们常见的方案有三种:

👉 SharedPreferences
👉 DataStore(官方推荐替代)
👉 MMKV(腾讯出品高性能方案)

那么,三者到底有什么区别?该如何选择?本篇将从原理、性能、使用方式、优缺点到最佳实践,带你一文搞懂。


🧩 一、整体对比总览

特性SharedPreferencesDataStoreMMKV
存储方式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 简洁易用。

❌ 缺点:

  • 需额外依赖库。

  • 少数场景文件损坏风险(官方有修复机制)。

  • 不是官方组件。


⚡️ 三、性能实测对比(单位:毫秒)

操作SharedPreferencesDataStoreMMKV
写入 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

🧩 五、实测结果总结

操作SharedPreferencesDataStoreMMKV
单次写入~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

http://www.dtcms.com/a/499027.html

相关文章:

  • 网站开发前后端中山做外贸网站
  • ElastiCache Redis 内存告警深度分析与运维实战指南
  • Spring5.3.10源码编译和调试(IDEA+Gradle)的过程
  • JS | 知识点总结 - 原型链
  • 【Docker】Docker镜像仓库
  • EEException: Geometry.area: Unable to perform this geometry operation.
  • 逻辑和共情
  • linux安装输入法
  • git连接远程仓库并拉去推送以及克隆命令
  • steam新品节游戏推荐!手机怎么玩steam游戏!
  • OpenHarmony Stage模型深度解剖:从Ability Kit到沙箱隔离的全链路底层原理
  • 基于 GEE 的 MODIS 昼夜地表温度数据可视化与导出全流程解决方案
  • 【Docker】记录一次使用docker部署dify网段冲突的问题
  • 缓存三剑客问题
  • 构建AI智能体:六十七、超参数如何影响大模型?通俗讲解原理、作用与实战示例
  • timm教程翻译:(一)Overview
  • 手写Spring第5弹:告别项目混乱:用Maven构建标准Java项目目录结构
  • Vue3 表单输入绑定
  • 手机app制作网站网站后台尺寸一般做多大的
  • C程序中的选择语句
  • OpenCV进阶:图像变换、增强与特征检测实战
  • 自己做的网站怎样对接支付宝php餐饮美食店网站源码 生成html
  • 【大数据技术实战】Kafka 认证机制全解析
  • Android14源码移植到Android16的应用报错分析说明
  • 13万枚比特币被没收。。。
  • 企业网站规划案例网页微信版传输助手
  • 用Trae自动生成一个围棋小程序
  • 聊天室项目开发——etcd的安装和使用
  • 小林coding | MySQL图解
  • 大模型-智能体-【篇二:多智能体框架】