DataStore出现的原因
Jetpack DataStore is a data storage solution that allows you to store key-value pairs or typed objects with protocol buffers. DataStore uses Kotlin coroutines and Flow to store data asynchronously, consistently, and transactionally.
If you're currently usingSharedPreferences
to store data, consider migrating to DataStore instead.
官方文档给出的定义如上所示
- 首先它的作用就是以
key-value
的形式去存储数据 - 它用的是
kotlin
的Flow实现,以事务方式处理数据。 - 最关键的一句话就是如果你的项目当前使用的是Sp,那么建议做一次迁移。
DataStore的实现方式
DataStore provides two different implementations: Preferences DataStore and Proto DataStore.
- Preferences DataStore stores and accesses data using keys. This implementation does not require a predefined schema, and it does not provide type safety.
- Proto DataStore stores data as instances of a custom data type. This implementation requires you to define a schema using protocol buffers, but it provides type safety.
文档中给出DataStore的两种实现方式:
- Preferences DataStore 顾名思义,以键值对的方式存储在本地。
-
Proto DataStore 通过
protocol buffers
将对象序列化存储在本地。
DataStore的使用
- 在项目当中集成,添加所需依赖
// Preferences DataStore
implementation "androidx.datastore:datastore-preferences:1.0.0-alpha03"
// Proto DataStore
implementation "androidx.datastore:datastore-core:1.0.0-alpha03"
- 创建一个DataStore对象
private val DATASTORE_NAME = "hy_datastore"
var dateStore: DataStore<Preferences> = mContext.createDataStore(name = DATASTORE_NAME)
- 将对象存入DataStore中
通过mDataStorePre.edit()
将数据存入DataStore中
public suspend fun DataStore<Preferences>.edit(
transform: suspend (MutablePreferences) -> Unit
): Preferences {
return this.updateData {
// It's safe to return MutablePreferences since we freeze it in
// PreferencesDataStore.updateData()
it.toMutablePreferences().apply { transform(this) }
}
}
可以看到edit()
方法是一个suspend
函数,所以只能在协程体中进行调用。以挂起的方式进行运行,做到不阻碍主线程。
private suspend fun saveDate(data: String) {
mDataStorePre.edit { mutablePreferences ->
mutablePreferences[KEY_TEST_DATASTORE] = ""
}
}
- 从DataStore中读取对象
private suspend fun getDate(data: String): String {
var value = mDataStorePre.data.map { preferences ->
preferences[KEY_TEST_DATASTORE] ?: ""
}
return value.first()
}
可以看到DataStore的key
不同于sp
的key
,DataStore的key
是Preferences.Key<T>
类型,只支持Int,Long,Boolean,Float,String,Double
:
public inline fun <reified T : Any> preferencesKey(name: String): Preferences.Key<T> {
return when (T::class) {
Int::class -> {
Preferences.Key<T>(name)
}
String::class -> {
Preferences.Key<T>(name)
}
Boolean::class -> {
Preferences.Key<T>(name)
}
Float::class -> {
Preferences.Key<T>(name)
}
Long::class -> {
Preferences.Key<T>(name)
}
Double::class -> {
Preferences.Key<T>(name)
}
Set::class -> {
throw IllegalArgumentException("Use `preferencesSetKey` to create keys for Sets.")
}
else -> {
throw IllegalArgumentException("Type not supported: ${T::class.java}")
}
}
}
除此外的类型将会抛出异常。
- 通过mDataStorePre.data我们将获取到的是一个
Flow<T>
的对象,每当数据变化的时候都会重新发出。 - 异常的捕获我们通过catch来捕获,如下所示:
private suspend fun getDate(): String {
var value = mDataStorePre.data.catch {
if (it is IOException) {//io异常的话可以发送一个emptyPreferences()来重新使用
it.printStackTrace()
emit(emptyPreferences())
} else {
throw it
}
}
.map { preferences ->
preferences[KEY_TEST_DATASTORE] ?: ""
}
return value.first()
}
迁移Sp到DataStore
只需要在创建的时候传入需要迁移的SharedPreferencesMigration
如下:
var mDataStorePre: DataStore<Preferences> = mContext.createDataStore(
name = DATASTORE_NAME,
migrations = listOf(
SharedPreferencesMigration(
mContext,
SPUtil.NAME
)
)
)
- 迁移之后会自动删除Sp所使用的文件,需要注意的是只从sp迁移一次。