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。

Kotlin 复制代码
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

无需定义结构,直接使用键值对:

Kotlin 复制代码
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,可存储复杂对象:

Kotlin 复制代码
syntax = "proto3";
message UserProfile {
  string name = 1;
  int32 age = 2;
}

然后使用自动生成的 UserProfileSerializer 存储类型化数据。

✅ 优点:

  • 异步、线程安全、无数据丢失。

  • 原生支持 Flow 响应式更新

  • 官方推荐未来方向。

❌ 缺点:

  • 首次初始化耗时略高。

  • 不支持跨进程。

  • Proto 模式使用门槛稍高。


3️⃣ MMKV:腾讯出品的极致性能方案

MMKV = Memory Mapped Key-Value

核心原理:mmap 内存映射文件 + protobuf 序列化

数据在内存中映射到磁盘,写入无需频繁磁盘 I/O,性能极高。

Kotlin 复制代码
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 迁移示例:

Kotlin 复制代码
context.dataStore.updateData { prefs ->
    val oldSp = context.getSharedPreferences("old_prefs", Context.MODE_PRIVATE)
    prefs.toMutablePreferences().apply {
        this[stringPreferencesKey("token")] = oldSp.getString("token", "") ?: ""
    }
}

🧪 附录:三种存储方案 Demo 对比与性能测试

📁 一、SharedPreferences 示例

Kotlin 复制代码
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)示例

依赖:

Groovy 复制代码
implementation "androidx.datastore:datastore-preferences:1.1.1"

使用:

Kotlin 复制代码
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):

Kotlin 复制代码
lifecycleScope.launch {
    dataStoreDemo.readData().collect { value ->
        Log.d("DATASTORE_FLOW", "DataStore 监听到新数据:$value")
    }
}

⚡️ 三、MMKV 示例

依赖:

Groovy 复制代码
implementation 'com.tencent:mmkv-static:1.3.4'

使用:

Kotlin 复制代码
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
    }
}

⚙️ 四、统一测试入口(对比三种方案性能)

Kotlin 复制代码
class StorageCompareActivity : AppCompatActivity() {

    private lateinit var spDemo: SpDemo
    private lateinit var dsDemo: DataStoreDemo
    private lateinit var mmkvDemo: MMKVDemo

    override 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. SharedPreferences
        spDemo.saveData(testKey, testValue)
        spDemo.readData(testKey)

        // 2. DataStore
        dsDemo.saveData(testValue)
        dsDemo.readData().firstOrNull()

        // 3. MMKV
        mmkvDemo.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            → 旧版本迁移支持

混合方案示例:

Kotlin 复制代码
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 迁移。

💡 参考资料:

相关推荐
qq_252924193 小时前
PHP 8.0+ 现代Web开发实战指南 引
android·前端·php
Mintopia3 小时前
🎨 AIGC 内容过滤技术:当创作的洪流遇上理性的堤坝
前端·javascript·aigc
Mintopia3 小时前
⚙️ Next.js 缓存与队列:当数据与请求跳起“低延迟之舞”
前端·全栈·next.js
Shi_haoliu3 小时前
Vue2 + Office Add-in关于用vue项目于加载项控制excel单元格内容(Demo版)
前端·javascript·vue.js·node.js·html·excel·office
IT_陈寒4 小时前
Redis 性能翻倍的 5 个隐藏技巧,99% 的开发者都不知道第3点!
前端·人工智能·后端
街尾杂货店&4 小时前
css word属性
前端·css
宝杰X75 小时前
Compose Multiplatform+Kotlin Multiplatfrom 第七弹跨平台 AI开源
人工智能·开源·kotlin
fruge6 小时前
2025前端工程化与性能优化实战指南:从构建到监控的全链路方案
前端·性能优化
2501_915918419 小时前
掌握 iOS 26 App 运行状况,多工具协作下的监控策略
android·ios·小程序·https·uni-app·iphone·webview