【Android Jetpack】使用DataStore替换SharedPreferences实现数据持久化

在这里插入图片描述

DataStore


DataStore是Jetpack近期新推出的组件,可以以下处理两种类型的数据持久化:

DataStore TypeDescription
Preferences DataStore像SharedPreferences一样,以键值对的形式进行基本类型的数据存储。DataStore 基于 Flow 实现异步存储,避免因为阻塞主线程带来的ANR问题
Proto DataStore基于Protobuf实现任意自定义类型的数据存储,需要定义Protobuf的IDL,但是可以保证类型安全的访问

DataStore相对于SharedPreferences优点更多,可以完全替代SP的使用:

  • 基于Coroutine Flow 实现
  • 保证数据访问一致性
  • 异常处理机制
  • 异步访问,避免同步阻塞
  • 基于Protobuf,实现非基本型数据的存储

附上一张Google官方的对比表格:


Preferences DataStore


首先在build.gradle中添加依赖,注意保证使用当前最新版本

// Preferences DataStore
implementation "androidx.datastore:datastore-preferences:1.0.0-alpha01"

如同SharedPreferences一样,创建一个DataStore对象

val dataStore: DataStore<Preferences> =
    context.createDataStore(name = "example-data-store-prefs")

为方便使用,我们分别创建读写的扩展方法:

fun <T> DataStore<Preferences>.getValueFlow(
    key: Preferences.Key<T>,
    defaultValue: T
): Flow<T> {
    return this.data
        .catch { exception ->
            if (exception is IOException) {
                emit(emptyPreferences())
            } else {
                throw exception
            }
        }.map { preferences ->
            preferences[key] ?: defaultValue
        }
}

suspend fun <T> DataStore<Preferences>.setValue(key: Preferences.Key<T>, value: T) {
    this.edit { preferences ->
        preferences[key] = value
    }
}

写数据:

companion object {
    private val USERNAME = preferencesKey<String>("username")
}

viewModelScope.launch {
    dataStore.setValue(USERNAME, "Harry Potter")
}

读数据:

viewModelScope.launch {
    dataStore.getValueFlow(USERNAME, "")
        .collect { value ->
            // use the value
        }
}

异常处理:

viewModelScope.launch {
    dataStore.getValueFlow(USERNAME, "")
        .catch {
            // handle error
        }
        .collect { value ->
            // use the value
        }
}

Proto DataStore


build.gradle添加依赖

  // Proto DataStore
  implementation "androidx.datastore:datastore-core:1.0.0-alpha01"

当然,因为需要使用protobuf,所以别忘了添加protobuf的插件依赖,用来将生成代码

root的build.gradle

buildscript {
    ...
    dependencies {
        ...
        classpath 'com.google.protobuf:protobuf-gradle-plugin:0.8.13'
        ...
    }
    ...
}

然后应用此插件:

apply plugin: 'com.google.protobuf'

接下来,基于Protobuf定义一个Settings类型

syntax = "proto3";

option java_package = "com.example.application";
option java_multiple_files = true;

message Settings {
  int32 example_counter = 1;
}

基于Serializer<T>实现Settings的序列化

object SettingsSerializer : Serializer<Settings> {
  override fun readFrom(input: InputStream): Settings {
    try {
      return Settings.parseFrom(input)
    } catch (exception: InvalidProtocolBufferException) {
      throw CorruptionException("Cannot read proto.", exception)
    }
  }

  override fun writeTo(
    t: Settings,
    output: OutputStream) = t.writeTo(output)
}

创建DataStore实例时,需要指定pb文件以及相关Serializer

val settingsDataStore: DataStore<Settings> = context.createDataStore(
  fileName = "settings.pb",
  serializer = SettingsSerializer
)

写数据:

suspend fun incrementCounter() {
  settingsDataStore.updateData { currentSettings ->
    currentSettings.toBuilder()
      .setExampleCounter(currentSettings.exampleCounter + 1)
      .build()
    }
}

读数据:

val exampleCounterFlow: Flow<Int> = settingsDataStore.data
  .map { settings ->
    // The exampleCounter property is generated from the proto schema.
    settings.exampleCounter
  }

SharedPreferences迁移


当我们需要将数据从SharedPreferences迁移到DataStore时,可以在创建DataStore实例时通过SharedPreferencesMigration指定SP的名字:

val dataStore: DataStore<Preferences> =
    context.createDataStore(
        name = "example-data-store-prefs",
        migrations = listOf(SharedPreferencesMigration(context, "example-prefs"))
    )

通过SharedPreferencesMigration的签名可以清晰得知其他的参数功能:

fun SharedPreferencesMigration(
    context: Context,
    sharedPreferencesName: String,
    keysToMigrate: Set<String>? = MIGRATE_ALL_KEYS,
    deleteEmptyPreferences: Boolean = true
)

不足


最后说几点DataStore的不足:

  • 目前Jetpack Security没有支持DataStore,所以不能像SharedPreference一样支持加密
  • 不能安全的进行IPC,这点相对于SharedPreferences没有提升,有较强IPC需求的话首选MMKV
  • 使用PB进行序列化时需要额外定义IDL,这会产生一定工作量

官方文档:
https://developer.android.com/topic/libraries/architecture/datastore

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

fundroid

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值