赞
踩
Jetpack DataStore
是一种数据存储解决方案,允许您使用协议缓冲区存储键值对或类型化对象。DataStore 使用 Kotlin 协程和流程以异步、一致的事务方式存储数据。
如果您当前在使用 SharedPreferences 存储数据,请考虑迁移到 DataStore。
注意:如果您需要支持大型或复杂数据集、部分更新或参照完整性,请考虑使用 Room,而不是 DataStore。DataStore非常适合简单的小型数据集,不支持部分更新或参照完整性。
Preferences DataStore
和 Proto DataStore
DataStore 提供两种不同的实现:Preferences DataStore 和 Proto DataStore。
Preferences DataStore
使用键存储和访问数据。此实现不需要预定义的架构,也不确保类型安全。
Proto DataStore
将数据作为自定义数据类型的实例进行存储。此实现要求您使用协议缓冲区来定义架构,但可以确保类型安全。
如需在您的应用中使用 Jetpack DataStore,请根据您要使用的实现向 Gradle 文件添加以下内容。
如果是使用Protobuf DataStore
// Typed DataStore (Typed API surface, such as Proto)
dependencies {
implementation "androidx.datastore:datastore:1.0.0-alpha06"
// optional - RxJava2 support
implementation "androidx.datastore:datastore-rxjava2:1.0.0-alpha06"
// optional - RxJava3 support
implementation "androidx.datastore:datastore-rxjava3:1.0.0-alpha06"
}
// Alternatively - use the following artifact without an Android dependency.
dependencies {
implementation "androidx.datastore:datastore-core:1.0.0-alpha06"
}
如果是使用Preferences DataStore
// Preferences DataStore (SharedPreferences like APIs)
dependencies {
implementation "androidx.datastore:datastore-preferences:1.0.0-alpha06"
// optional - RxJava2 support
implementation "androidx.datastore:datastore-preferences-rxjava2:1.0.0-alpha06"
// optional - RxJava3 support
implementation "androidx.datastore:datastore-preferences-rxjava3:1.0.0-alpha06"
}
// Alternatively - use the following artifact without an Android dependency.
dependencies {
implementation "androidx.datastore:datastore-preferences-core:1.0.0-alpha06"
}
Preferences DataStore 实现使用 DataStore 和 Preferences 类将简单的键值对保留在磁盘上。
使用 Context.createDataStore()
扩展函数创建 DataStore<Preferences>
的实例。此外,如果您使用的是 RxJava
,请使用 RxPreferenceDataStoreBuilder
。必需的 name 参数是 Preferences DataStore 的名称。
kotlin代码
val dataStore: DataStore<Preferences> = context.createDataStore(
name = "settings"
)
或者
Java代码
DataStore<Preferences> dataStore =
new RxPreferenceDataStoreBuilder(context, /*name=*/ "settings").build();
由于 Preferences DataStore 不使用预定义的架构,因此您必须使用相应的键类型函数为需要存储在 DataStore<Preferences>
实例中的每个值定义一个键。例如,如需为 int 值定义一个键,请使用 intPreferencesKey()
。然后,使用 DataStore.data 属性,通过 Flow 提供适当的存储值。
kotlin代码
val EXAMPLE_COUNTER = intPreferencesKey("example_counter")
val exampleCounterFlow: Flow<Int> = dataStore.data
.map { preferences ->
// No type safety.
preferences[EXAMPLE_COUNTER] ?: 0
}
或者
Java代码
Preferences.Key<Integer> EXAMPLE_COUNTER = PreferencesKeys.int("example_counter");
Flowable<Integer> exampleCounterFlow =
RxDataStore.data(dataStore).map(prefs -> prefs.get(EXAMPLE_COUNTER));
Preferences DataStore 提供了一个 edit()
函数,用于以事务方式更新 DataStore 中的数据。该函数的 transform 参数接受代码块,您可以在其中根据需要更新值。转换块中的所有代码均被视为单个事务。
kotlin代码
suspend fun incrementCounter() {
dataStore.edit { settings ->
val currentCounterValue = settings[EXAMPLE_COUNTER] ?: 0
settings[EXAMPLE_COUNTER] = currentCounterValue + 1
}
}
或者
Java代码
Single<Preferences> updateResult = RxDataStore.updateDataAsync(dataStore, prefsIn -> {
MutablePreferences mutablePreferences = prefsIn.toMutablePreferences();
Integer currentInt = prefsIn.get(INTEGER_KEY);
mutablePreferences.set(INTEGER_KEY, currentInt != null ? currentInt + 1 : 1);
return Single.just(mutablePreferences);
});
// The update is completed once updateResult is completed.
Google
推出 DataStore 的目的是为了取代 SharedPreference
,对于老项目,就需要从 SharedPreference 中进行数据的迁移,从 SharedPreference 迁移到 DataStore
。
在 createDataStore
方法的参数中有 migrations
参数:
migrations: List<DataMigration<Preferences>> = listOf()
只需要在 createDataStore 方法中按照如下格式就可以自动完成数据的迁移:
mDataStorePre = this.createDataStore(
name = DATASTORE_PREFERENCE_NAME,
migrations = listOf(SharedPreferencesMigration(this, SP_PREFERENCE_NAME))
)
Proto DataStore
实现使用 DataStore 和协议缓冲区将类型化的对象保留在磁盘上。
关于Protobuf的使用,请参考protobuf官方文档
在创建 Proto DataStore 的时候,在 AndroidStudio 中,必须先做如下配置:
在 project 的 build.gradle 中添加依赖:
classpath 'com.google.protobuf:protobuf-gradle-plugin:0.8.15'
在 app 的 build.gradle 中:
plugins { id 'com.android.application' } apply plugin: 'kotlin-android' apply plugin: 'com.google.protobuf' //https://github.com/google/protobuf-gradle-plugin android { compileSdkVersion 29 buildToolsVersion "30.0.2" defaultConfig { applicationId "com.ryd.datastore" minSdkVersion 16 targetSdkVersion 29 versionCode 1 versionName "1.0" // 开起分包 multiDexEnabled true } buildTypes { release { minifyEnabled false proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' } } compileOptions { sourceCompatibility JavaVersion.VERSION_1_8 targetCompatibility JavaVersion.VERSION_1_8 } sourceSets { main { proto { //The plugin adds a new sources block named proto alongside java to every sourceSet. By default, it includes all *.proto files under src/$sourceSetName/proto. You can customize it in the same way as you would customize the java sources. srcDir 'src/main/proto' include '**/*.proto' } } } } dependencies { implementation 'androidx.appcompat:appcompat:1.2.0' implementation 'com.google.android.material:material:1.3.0' implementation 'androidx.constraintlayout:constraintlayout:2.0.4' //====================== // Typed DataStore (Typed API surface, such as Proto) implementation "androidx.datastore:datastore:1.0.0-alpha06" // optional - RxJava2 support implementation "androidx.datastore:datastore-rxjava2:1.0.0-alpha06" // optional - RxJava3 support //implementation "androidx.datastore:datastore-rxjava3:1.0.0-alpha06" //======================= // Preferences DataStore (SharedPreferences like APIs) implementation "androidx.datastore:datastore-preferences:1.0.0-alpha06" // optional - RxJava2 support implementation "androidx.datastore:datastore-preferences-rxjava2:1.0.0-alpha06" // optional - RxJava3 support //implementation "androidx.datastore:datastore-preferences-rxjava3:1.0.0-alpha06" //================== // kotlin implementation "androidx.core:core-ktx:1.3.2" implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version" // multidex implementation 'com.android.support:multidex:1.0.3' // protobuf implementation 'com.google.protobuf:protobuf-javalite:3.10.0' } protobuf { // Configure the protoc executable protoc { // Download from repositories artifact = "com.google.protobuf:protoc:3.10.0" } // Generates the java Protobuf-lite code for the Protobufs in this project. See // https://github.com/google/protobuf-gradle-plugin#customizing-protobuf-compilation // for more information. generateProtoTasks { // all() returns the collection of all protoc tasks all().each { task -> //配置要生成的内容 //代码生成是由protoc内置和插件完成的。每个内置/插件都会生成特定类型的代码。要在任务上添加或配置内置/插件,请列出其名称,后跟大括号块。如果需要的话,在括号里加上选项。例如: task.builtins { java { option 'lite' } } } } } repositories { mavenCentral() }
Proto DataStore 要求在 app/src/main/proto/ 目录的 proto 文件中保存预定义的架构。此架构用于定义您在 Proto DataStore 中保存的对象的类型。如需详细了解如何定义 proto 架构,请参阅 protobuf官方文档。
syntax = "proto3";
option java_package = "com.example.application";
option java_multiple_files = true;
message Settings {
int32 example_counter = 1;
}
注意:您的存储对象的类在编译时由 proto 文件中定义的 message 生成。请务必重新构建您的项目。
记得要执行 rebuild project
创建 Proto DataStore
来存储类型化对象涉及两个步骤:
Context.createDataStore()
扩展函数创建 DataStore 的实例,其中 T 是在 protokotlin代码 object SettingsSerializer : Serializer<Settings> { override val defaultValue: Settings = Settings.getDefaultInstance() 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) } val settingsDataStore: DataStore<Settings> = context.createDataStore( fileName = "settings.pb", serializer = SettingsSerializer )
或者
Java代码 private static class SettingsSerializer implements Serializer<Settings> { @Override public Settings getDefaultValue() { Settings.getDefaultInstance(); } @Override public Settings readFrom(@NotNull InputStream input) { try { return Settings.parseFrom(input); } catch (exception: InvalidProtocolBufferException) { throw CorruptionException(“Cannot read proto.”, exception); } } @Override public void writeTo(Settings t, @NotNull OutputStream output) { t.writeTo(output); } } DataStore<Settings> dataStore = new RxDataStoreBuilder<Settings>(context, /* fileName= */ "settings.pb", new SettingsSerializer()).build();
使用 DataStore.data 显示所存储对象中相应属性的 Flow。
kotlin代码
val exampleCounterFlow: Flow<Int> = settingsDataStore.data
.map { settings ->
// The exampleCounter property is generated from the proto schema.
settings.exampleCounter
}
或者
Java代码
Flowable<Integer> exampleCounterFlow =
RxDataStore.data(dataStore).map(settings -> settings.getExampleCounter());
Proto DataStore 提供了一个 updateData()
函数,用于以事务方式更新存储的对象。updateData() 为您提供数据的当前状态,作为数据类型的一个实例,并在原子读-写-修改操作中以事务方式更新数据。
kotlin代码
suspend fun incrementCounter() {
settingsDataStore.updateData { currentSettings ->
currentSettings.toBuilder()
.setExampleCounter(currentSettings.exampleCounter + 1)
.build()
}
}
或者
Java代码
Single<Settings> updateResult =
RxDataStore.updateDataAsync(dataStore, currentSettings ->
Single.just(
currentSettings.toBuilder()
.setExampleCounter(currentSettings.getExampleCounter() + 1)
.build()));
注意:请尽可能避免在 DataStore 数据读取时阻塞线程。阻塞界面线程可能会导致 ANR 或界面卡顿,而阻塞其他线程可能会导致死锁。
DataStore 的主要优势之一是异步 API,但可能不一定始终能将周围的代码更改为异步代码。如果您使用了采用同步磁盘 I/O 的现有代码库,或者您的依赖项不提供异步 API,就可能出现这种情况。
Kotlin
协程提供 runBlocking()
协程构建器,以帮助消除同步与异步代码之间的差异。您可以使用 runBlocking() 从 DataStore 同步读取数据。RxJava 在 Flowable 上提供阻塞方法。以下代码会阻塞发起调用的线程,直到 DataStore 返回数据:
kotlin代码
val exampleData = runBlocking { dataStore.data.first() }
或者
Java代码
Settings settings = RxDataStore.data(dataStore).blockingFirst();
对界面线程执行同步 I/O 操作可能会导致 ANR 或界面卡顿。您可以通过从 DataStore 异步预加载数据来减少这些问题:
kotlin代码
override fun onCreate(savedInstanceState: Bundle?) {
lifecycleScope.launch {
dataStore.data.first()
// You should also handle IOExceptions here.
}
}
或者
Java代码
RxDataStore.data(dataStore).first().subscribe();
这样,DataStore 可以异步读取数据并将其缓存在内存中。以后使用 runBlocking() 进行同步读取的速度可能会更快,或者如果初始读取已经完成,可能也可以完全避免磁盘 I/O 操作。
参考文档
Jetpack DataStore官方文档
Android Jetpack 之 DataStore博客
google/protobuf-gradle-plugin
protobuf官方文档
赞
踩
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。