欢送应用 Jetpack DataStore,这是一个通过改良的全新数据存储解决方案,旨在代替原有的 SharedPreferences。Jetpack DataStore 基于 Kotlin 协程和 Flow 开发,并提供两种不同的实现: Proto DataStore 和 Preferences DataStore。其中 Proto DataStore,能够存储带有类型的对象 (应用 protocol buffers 实现);Preferences DataStore,能够存储 键值对。在 DataStore 中,数据以异步的、统一的、事务性的形式进行存储,克服了 SharedPreferences 的大部分毛病。
-
protocol buffers
https://developers.google.cn/…
SharedPreferences 和 DataStore 比照
- SharedPreferences 有一个看上去能够在 UI 线程平安调用的同步 API,然而该 API 实际上执行了磁盘 I/O 操作。此外,apply() 办法会在 fsync() 阻塞 UI 线程。在您利用的任何中央,每当 Service 或 Activity 启动或进行时,就会触发期待 fsync() 的调用。由 apply() 安顿的 fsync() 调用过程会阻塞 UI 线程,这也经常成为造成 ANR 的源头。** SharedPreferences 在剖析出错时会抛出运行时异样。
- ANRhttps://developer.android.goo…
在两种实现中,除非另外特指,否则 DataStore 会将首选项存储在文件中,并且所有的数据操作都会在 Dispatchers.IO 上执行。
尽管 Preferences DataStore 与 Proto DataStore 都能够存储数据,但它们的实现办法不尽相同:
- Preference DataStore,就像 SharedPreferences 一样,不能定义 schema 或保障以正确的类型拜访键值。
- Proto DataStore 让您能够应用 Protocol buffers 定义 schema。应用 Protobufs 能够保留 强类型数据。它们绝对于 XML 或其余类似的数据格式要更快、更小、歧义更少。尽管 Proto DataStore 要求您学习一种新的序列化机制,但思考到 Proto DataStore 所带来的强类型 schema 的劣势,咱们认为这样的代价是值得的。
- Protocol buffershttps://developers.google.cn/…
Room 和 DataStore 比照
如果您有部分更新数据、参照完整性或反对大型、简单数据集的需要,则该当思考应用 Room 而不是 DataStore。DataStore 是小型、简略数据集的现实抉择,它并不反对部分更新与参照完整性。
应用 DataStore
首先增加 DataStore 依赖项。如果您应用的是 Proto DataStore,请确保您也增加了 proto 依赖项:
-
proto 依赖项
https://github.com/google/pro…
def dataStoreVersion = "1.0.0-alpha05"
// 在 Android 开发者网站上确认最新的版本号
// https://developer.android.google.cn/jetpack/androidx/releases/datastore
// Preferences DataStore
implementation "androidx.datastore:datastore-preferences:$dataStoreVersion"
// Proto DataStore
implementation "androidx.datastore:datastore-core:$dataStoreVersion"
当您应用 Proto DataStore 时,您须要在 app/src/main/proto/ 目录下应用 proto 文件定义您本人的 schema。无关定义 proto schema 的更多信息,请参阅 protobuf 语言指南。
- protobuf 语言指南
https://developers.google.cn/…
syntax = "proto3";
option java_package = "<your package name here>";
option java_multiple_files = true;
message Settings {int my_counter = 1;}
创立 DataStore
您能够应用 Context.createDataStore() 扩大办法创立 DataStore:
// 创立 Preferences DataStore
val dataStore: DataStore<Preferences> = context.createDataStore(name = "settings")
如果您应用的是 Proto DataStore,您还须要实现 Serializer 接口来通知 DataStore 如何读取和写入您的数据类型。
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)
}
// 创立 Proto DataStore
val settingsDataStore: DataStore<Settings> = context.createDataStore(
fileName = "settings.pb",
serializer = SettingsSerializer
)
从 DataStore 读取数据
无论是 Preferences 对象还是您在 proto schema 中定义的对象,DataStore 都会以 Flow 的模式裸露已存储的数据。DataStore 能够确保在 Dispatchers.IO 上检索数据,因而不会阻塞您的 UI 线程。
-
Dispatchers.IO
https://kotlin.github.io/kotl…
应用 Preferences DataStore:
val MY_COUNTER = preferencesKey<Int>("my_counter")
val myCounterFlow: Flow<Int> = dataStore.data
.map { currentPreferences ->
// 不同于 Proto DataStore,这里不保障类型平安。currentPreferences[MY_COUNTER] ?: 0
}
应用 Proto DataStore:
val myCounterFlow: Flow<Int> = settingsDataStore.data
.map { settings ->
// myCounter 属性由您的 proto schema 生成!settings.myCounter
}
向 DataStore 写入数据
为了写入数据,DataStore 提供了一个 DataStore.updateData() 挂起函数,它会将以后存储数据的状态作为参数提供给您,对于 Preferences 对象或是您在 proto schema 中定义的对象实例皆为如此。updateData() 函数应用原子的读、写、批改操作并以事务的形式更新数据。当数据在磁盘上实现存储时,此协程就会实现。
Preferences DataStore 还提供了一个 DataStore.edit() 函数来不便数据的更新。在此函数中,您会收到一个用于编辑的 MutablePreferences 对象,而不是 Preferences 对象。该函数与 updateData() 一样,会在转换代码块实现之后将批改利用到磁盘,并且当数据在磁盘上实现存储时,此协程就会实现。
应用 Preferences DataStore:
suspend fun incrementCounter() {
dataStore.edit { settings ->
// 能够平安地减少咱们的计数器,而不会因为资源竞争而失落数据。val currentCounterValue = settings[MY_COUNTER] ?: 0
settings[MY_COUNTER] = currentCounterValue + 1
}
}
应用 Proto DataStore:
suspend fun incrementCounter() {
settingsDataStore.updateData { currentSettings ->
// 能够平安地减少咱们的计数器,而不会因为资源竞争而失落数据。currentSettings.toBuilder()
.setMyCounter(currentSettings.myCounter + 1)
.build()}
}
从 SharedPreferences 迁徙至 DataStore
要从 SharedPreferences 迁徙至 DataStore,您须要将 SharedPreferencesMigration 对象传递给 DataStore 结构器,DataStore 能够主动实现从 SharedPreferences 迁徙至 DataStore 的工作。迁徙会在 DataStore 中产生任何数据拜访之前运行,这意味着在 DataStore.data 返回任何值以及 DataStore.updateData() 能够更新数据之前,您的迁徙必须曾经胜利。
如果您要迁徙至 Preferences DataStore,您能够应用 SharedPreferencesMigration 的默认实现。只须要传入 SharedPreferences 结构时所应用的名字就能够了。
应用 Preferences DataStore:
val dataStore: DataStore<Preferences> = context.createDataStore(
name = "settings",
migrations = listOf(SharedPreferencesMigration(context, "settings_preferences"))
)
当须要迁徙至 Proto DataStore 时,您必须实现一个映射函数,用来定义如何将 SharedPreferences 所应用的键值对迁徙到您所定义的 DataStore schema。
应用 Proto DataStore:
val settingsDataStore: DataStore<Settings> = context.createDataStore(produceFile = { File(context.filesDir, "settings.preferences_pb") },
serializer = SettingsSerializer,
migrations = listOf(
SharedPreferencesMigration(
context,
"settings_preferences"
) { sharedPrefs: SharedPreferencesView, currentData: UserPreferences ->
// 在这里将 sharedPrefs 映射至您的类型。}
)
)
总结
SharedPreferences 有着许多缺点: 看起来能够在 UI 线程平安调用的同步 API 其实并不平安、没有提醒谬误的机制、短少事务 API 等等。DataStore 是 SharedPreferences 的代替计划,它解决了 Shared Preferences 的绝大部分问题。DataStore 蕴含应用 Kotlin 协程和 Flow 实现的齐全异步 API,能够解决数据迁徙、保证数据一致性,并且能够解决数据损坏。
Android 高级开发零碎进阶笔记、最新面试温习笔记 PDF,我的 GitHub
文末
您的点赞珍藏就是对我最大的激励!
欢送关注我,分享 Android 干货,交换 Android 技术。
对文章有何见解,或者有何技术问题,欢送在评论区一起留言探讨!