Android 数据持久化:Store 库 vs PreferencesDataStore以及mmkv的简单对比

570 阅读6分钟

作为一名从前端开发转战 Android 的工程师,您可能对 localStorage 这样的本地存储方案并不陌生。在 Android 中,preferencesDataStore 提供了类似的功能,但其异步操作和稍显繁琐的语法,可能会让初学者感到些许不适应。

本文将向您介绍一个第三方库:Store,它基于 preferencesDataStore 构建,提供了更简洁、类型安全、易于使用的 API,让 Android 本地数据管理变得更加轻松。

preferencesDataStore 的不足

preferencesDataStore 作为 Android Jetpack 组件的一部分,提供了键值对的异步存储能力。然而,直接使用它,您可能会发现需要编写不少的样板代码:

  1. 定义 Preferences.Key 需要为每个键定义一个 Preferences.Key 对象,并指定类型。
  2. 使用 edit 函数修改数据: 每次修改数据都需要使用 edit 函数,并传入一个 lambda 表达式。
  3. 使用 map 函数从 Flow 中提取数据:DataStore 返回的 Flow 中提取数据时,需要使用 map 函数,并处理默认值。
  4. 处理默认值: 在读取数据时,需要手动处理默认值,避免空指针异常。

这些操作在需要频繁读写多个键值对时,会显得比较繁琐,降低开发效率。

Store 库的优势

Store 库旨在解决 preferencesDataStore 的这些痛点,它提供了以下优势:

  1. 简洁的 API: 使用 store.key 定义键,使用 store.setstore.get 进行读写操作,代码更加简洁易懂。
  2. 类型安全: store.key 会根据传入的默认值自动推断类型,确保类型安全,避免运行时错误。
  3. 默认值支持: 可以直接在 store.key 中设置默认值,无需手动处理 ?: 运算符。
  4. 基于 Flow 依然基于 Flow,可以方便地观察数据变化,实现响应式编程。
  5. 可配置的 DataStore 实例: 可以自定义 DataStore 的创建方式,满足不同的需求。

代码示例对比

为了更直观地展示 Store 库的优势,我们通过一个简单的例子进行对比:

使用 preferencesDataStore 的原始代码:

import android.content.Context
import androidx.datastore.core.DataStore
import androidx.datastore.preferences.core.Preferences
import androidx.datastore.preferences.core.edit
import androidx.datastore.preferences.core.intPreferencesKey
import androidx.datastore.preferences.core.stringPreferencesKey
import androidx.datastore.preferences.preferencesDataStore
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.map

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

class SettingsManager(private val context: Context) {

    private val THEME_KEY = intPreferencesKey("theme_mode")
    private val USERNAME_KEY = stringPreferencesKey("username")

    suspend fun saveThemeMode(themeMode: Int) {
        context.dataStore.edit { settings ->
            settings[THEME_KEY] = themeMode
        }
    }

    val themeModeFlow: Flow<Int> = context.dataStore.data.map { settings ->
        settings[THEME_KEY] ?: 0
    }

    suspend fun saveUserName(userName: String) {
        context.dataStore.edit { settings ->
            settings[USERNAME_KEY] = userName
        }
    }

    val userNameFlow: Flow<String> = context.dataStore.data.map { settings ->
        settings[USERNAME_KEY] ?: ""
    }
}

使用 Store 库的代码:

import android.content.Context
import com.github.zsoltmolnar.store.Store
import com.github.zsoltmolnar.store.preferences.PreferencesStore
import kotlinx.coroutines.flow.Flow

class SettingsManager(context: Context) {
    private val store: Store<PreferencesStore> = PreferencesStore(context, "settings")

    private val themeModeKey = store.key("theme_mode", 0)
    private val userNameKey = store.key("username", "")

    suspend fun saveThemeMode(themeMode: Int) {
        store.set(themeModeKey, themeMode)
    }

    val themeModeFlow: Flow<Int> = store.get(themeModeKey)

    suspend fun saveUserName(userName: String) {
        store.set(userNameKey, userName)
    }

    val userNameFlow: Flow<String> = store.get(userNameKey)
}

对比两段代码,我们可以明显看到 Store 库的优势:

  • 简洁的 API: 使用 store.key 定义键,并直接指定默认值,代码更简洁。
  • 类型安全: store.key 会根据传入的默认值自动推断类型,确保类型安全。
  • 默认值支持: 可以直接在 store.key 中设置默认值,无需手动处理 ?: 运算符。

如何使用 Store

  1. 添加依赖:build.gradle.kts 文件中添加依赖:

    dependencies {
        implementation("com.github.zsoltmolnar:store:1.4.0")
    }
    
  2. 创建 Store 实例: 使用 PreferencesStore(context, "settings") 创建 Store 实例。

  3. 定义键: 使用 store.key("key_name", defaultValue) 定义键,并指定默认值。

  4. 读写数据: 使用 store.set(key, value) 保存数据,使用 store.get(key) 读取数据。

mmkv

MMKV 是由腾讯微信团队开发的一个高效、稳定、易用的移动端 Key-Value 存储组件。它具有以下显著特点:

  1. 基于 mmap 技术: MMKV 的核心是基于 mmap (memory-mapped file) 技术实现的。mmap 将磁盘文件映射到内存中,使得对文件的读写操作就像操作内存一样快速,大大提高了读写性能。
  2. 高性能: 由于 mmap 的特性,MMKV 的读写性能非常高,远超 SharedPreferencespreferencesDataStore。它能够处理高并发、大数据量的读写操作。
  3. 跨进程支持: MMKV 支持跨进程数据共享,这意味着可以在不同的进程中访问和修改同一份数据。这在多进程架构的应用中非常有用。
  4. 简单易用: MMKV 提供了简洁易用的 API,与 SharedPreferences 类似,学习成本较低。
  5. 数据安全: MMKV 支持数据加密,可以保护敏感数据不被泄露。
  6. 支持多种数据类型: 除了基本数据类型(如 String, Int, Boolean 等),MMKV 还支持 ParcelableByteArray 等复杂数据类型。
  7. 内存占用少: MMKV 使用 mmap 技术,只有在需要时才会将数据加载到内存中,因此内存占用较少。
  8. 稳定性高: MMKV 经过微信团队的长期使用和验证,具有很高的稳定性。

MMKV 的核心优势总结:

  • 高性能: 读写性能远超 SharedPreferencespreferencesDataStore
  • 跨进程支持: 可以在不同的进程中共享数据。
  • 简单易用: API 简洁,学习成本低。
  • 数据安全: 支持数据加密。
  • 支持多种数据类型: 除了基本类型,还支持 ParcelableByteArray

适用场景:

  • 需要高性能读写操作的场景。
  • 需要在多个进程之间共享数据的场景。
  • 需要存储大量数据的场景。
  • 需要存储复杂数据类型的场景。

总结

三种方案的对比

特性preferencesDataStoreStore (基于 preferencesDataStore)MMKV
存储方式异步异步同步 (基于 mmap, 性能高)
数据类型类型安全 (Int, String, Boolean, Float, Long, etc.)类型安全 (Int, String, Boolean, Float, Long, etc.)简单类型 (String, Int, Boolean, etc.), 支持 Parcelable, ByteArray
API 易用性稍显繁琐,需要协程和 Flow简洁,易用,基于 key 的 API简洁,易用
线程阻塞不会阻塞主线程不会阻塞主线程可能阻塞主线程 (但 mmap 性能高,阻塞时间短)
数据观察使用 Flow使用 Flow支持监听器
性能较好,异步操作,性能较好较好,异步操作,性能较好优秀,使用 mmap 技术,读写性能极高
内存占用较低,按需加载较低,按需加载较低,使用 mmap 技术,内存占用较少
存储位置应用私有存储应用私有存储应用私有存储
数据安全性简单加密,不安全简单加密,不安全支持加密
跨进程支持不支持不支持支持
文件大小限制无明确限制,但过大可能导致性能问题无明确限制,但过大可能导致性能问题无明确限制,但过大可能导致性能问题
适用场景应用配置、用户偏好、简单数据缓存应用配置、用户偏好、简单数据缓存大量数据存储、跨进程数据共享、高性能读写
依赖Android Jetpack (androidx.datastore:datastore-preferences)Store 库 (基于 preferencesDataStore)Tencent/MMKV
是否支持复杂数据类型不支持不支持支持 Parcelable, ByteArray
主要优点类型安全,异步操作简洁易用,类型安全,默认值支持高性能,跨进程,支持复杂数据类型
主要缺点API 稍显繁琐依赖第三方库,仍然是基于 preferencesDataStore同步操作,可能阻塞主线程

关键说明:

  • Store (基于 preferencesDataStore): 将其明确定义为 preferencesDataStore 的封装库,并体现其 API 易用性上的优势。
  • 性能: preferencesDataStoreStore 的性能都属于较好,但 MMKV 的性能远高于它们。
  • 跨进程支持: 只有 MMKV 支持跨进程数据共享。
  • 主要优缺点: 添加了主要优缺点的总结,更方便对比。