对于 Retrofit、Room,我们只需调用
viewModelScope.launch { ... }
,它们会自动在后台线程中执行请求
但是当我尝试在 DataStore 中更新模型时,它不起作用,尽管没有运行时异常,这就是为什么我认为一切都应该没问题,但不,它仍然是默认模型
val Context.userDataStore: DataStore<User> by dataStore(
fileName = USER_FILE_NAME,
serializer = UserSerializer
)
private object UserSerializer : Serializer<User> {
override val defaultValue: User
get() = User()
...
然后我尝试使用特定上下文进行更新,并且成功了
viewModelScope.launch { withContext(Dispatchers.IO) { ... } }
所以这是 DataStore 的正确方法吗?
这是一个老问题,所以我很确定您不会再遇到这个问题。但我想不出为什么使用错误的调度程序会默默地放弃您想要保存的更改。您的代码的其他部分还会发生其他事情。
也就是说,我现在写答案的主要原因是,当使用搜索引擎查找数据存储区使用哪个调度程序时,这个问题会作为第一个点击出现。
我在“使用首选项数据存储”代码实验室中发现了这个巧妙的比较:
如您所见,明确指出数据存储会切换到 IO 调度程序本身,因此您不需要自己执行此操作,即使您这样做也没有什么区别。
由于约定是每个挂起函数(也适用于流)应确保它在执行不属于主线程的工作时在正确的调度程序上运行,因此数据存储的工作方式是预期的行为。您不应该自己使用 IO 调度程序来访问数据存储来“只是确保”。
警告:尽可能避免阻塞 DataStore 数据读取的线程。 阻塞 UI 线程可能会导致 ANR 或 UI 卡顿,并阻塞其他线程 线程可能会导致死锁。
在 DataStore 文档中提到,如果您正在使用使用同步磁盘 I/O 的现有代码库,或者您有不提供异步 API 的依赖项。
Kotlin 协程提供
runBlocking()
协程构建器来帮助弥合同步和异步代码之间的差距。
您可以使用
runBlocking()
从DataStore同步读取数据。
val exampleData = runBlocking { context.dataStore.data.first() }
在 UI 线程上执行同步 I/O 操作可能会导致 ANR 或 UI 卡顿。您可以通过从 DataStore 异步预加载数据来缓解这些问题:
override fun onCreate(savedInstanceState: Bundle?) {
lifecycleScope.launch {
context.dataStore.data.first()
// You should also handle IOExceptions here.
}
}
这样,DataStore 异步读取数据并将其缓存在内存中。如果初始读取已完成,稍后使用
runBlocking()
的同步读取可能会更快,或者可能完全避免磁盘 I/O 操作。