作为一名从前端开发转战 Android 的工程师,您可能对 localStorage 这样的本地存储方案并不陌生。在 Android 中,preferencesDataStore 提供了类似的功能,但其异步操作和稍显繁琐的语法,可能会让初学者感到些许不适应。
本文将向您介绍一个第三方库:Store,它基于 preferencesDataStore 构建,提供了更简洁、类型安全、易于使用的 API,让 Android 本地数据管理变得更加轻松。
preferencesDataStore 的不足
preferencesDataStore 作为 Android Jetpack 组件的一部分,提供了键值对的异步存储能力。然而,直接使用它,您可能会发现需要编写不少的样板代码:
- 定义
Preferences.Key: 需要为每个键定义一个Preferences.Key对象,并指定类型。 - 使用
edit函数修改数据: 每次修改数据都需要使用edit函数,并传入一个 lambda 表达式。 - 使用
map函数从Flow中提取数据: 从DataStore返回的Flow中提取数据时,需要使用map函数,并处理默认值。 - 处理默认值: 在读取数据时,需要手动处理默认值,避免空指针异常。
这些操作在需要频繁读写多个键值对时,会显得比较繁琐,降低开发效率。
Store 库的优势
Store 库旨在解决 preferencesDataStore 的这些痛点,它提供了以下优势:
- 简洁的 API: 使用
store.key定义键,使用store.set和store.get进行读写操作,代码更加简洁易懂。 - 类型安全:
store.key会根据传入的默认值自动推断类型,确保类型安全,避免运行时错误。 - 默认值支持: 可以直接在
store.key中设置默认值,无需手动处理?:运算符。 - 基于
Flow: 依然基于Flow,可以方便地观察数据变化,实现响应式编程。 - 可配置的
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 库
-
添加依赖: 在
build.gradle.kts文件中添加依赖:dependencies { implementation("com.github.zsoltmolnar:store:1.4.0") } -
创建
Store实例: 使用PreferencesStore(context, "settings")创建Store实例。 -
定义键: 使用
store.key("key_name", defaultValue)定义键,并指定默认值。 -
读写数据: 使用
store.set(key, value)保存数据,使用store.get(key)读取数据。
mmkv
MMKV 是由腾讯微信团队开发的一个高效、稳定、易用的移动端 Key-Value 存储组件。它具有以下显著特点:
- 基于 mmap 技术: MMKV 的核心是基于 mmap (memory-mapped file) 技术实现的。mmap 将磁盘文件映射到内存中,使得对文件的读写操作就像操作内存一样快速,大大提高了读写性能。
- 高性能: 由于 mmap 的特性,MMKV 的读写性能非常高,远超
SharedPreferences和preferencesDataStore。它能够处理高并发、大数据量的读写操作。 - 跨进程支持: MMKV 支持跨进程数据共享,这意味着可以在不同的进程中访问和修改同一份数据。这在多进程架构的应用中非常有用。
- 简单易用: MMKV 提供了简洁易用的 API,与
SharedPreferences类似,学习成本较低。 - 数据安全: MMKV 支持数据加密,可以保护敏感数据不被泄露。
- 支持多种数据类型: 除了基本数据类型(如 String, Int, Boolean 等),MMKV 还支持
Parcelable和ByteArray等复杂数据类型。 - 内存占用少: MMKV 使用 mmap 技术,只有在需要时才会将数据加载到内存中,因此内存占用较少。
- 稳定性高: MMKV 经过微信团队的长期使用和验证,具有很高的稳定性。
MMKV 的核心优势总结:
- 高性能: 读写性能远超
SharedPreferences和preferencesDataStore。 - 跨进程支持: 可以在不同的进程中共享数据。
- 简单易用: API 简洁,学习成本低。
- 数据安全: 支持数据加密。
- 支持多种数据类型: 除了基本类型,还支持
Parcelable和ByteArray。
适用场景:
- 需要高性能读写操作的场景。
- 需要在多个进程之间共享数据的场景。
- 需要存储大量数据的场景。
- 需要存储复杂数据类型的场景。
总结
三种方案的对比
| 特性 | preferencesDataStore | Store (基于 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 易用性上的优势。 - 性能:
preferencesDataStore和Store的性能都属于较好,但MMKV的性能远高于它们。 - 跨进程支持: 只有
MMKV支持跨进程数据共享。 - 主要优缺点: 添加了主要优缺点的总结,更方便对比。