【发布时间】:2021-09-30 16:45:24
【问题描述】:
我经常问自己如何使用协程。每次单击按钮或出现其他事件时,我都会启动一个协程来从/向数据库或 rest api 保存或加载数据。然后我有像下面这样的小功能。
在极少数情况下,如果两个或多个协程同时写入/读取,我会得到 ConcurrentModificationExceptions。我从来没有遇到过 Java+RxJava 的这个问题。现在我只使用 Kotlin+Coroutines(没有 RxKotlin,没有 Flow,没有 LiveData)。作为数据库,我使用 Room。
有没有办法保存对 Coroutine-Container 之类的引用,我可以在其中添加 Jobs 以使它们一个接一个地完成?或者你们实际上是如何启动协程的?
fun loadAllDataForTheUserInterface() {
viewModelScope.launch {
val newData = dataBaseRepository.load(...)
fragment.draw(newData)
}
}
或
fun handleSaveClick(user: User) {
viewModelScope.launch {
val newUser = restApiRepository.uploadNewUser(user)
databaseRepository.save(newUser)
fragment.close()
}
}
我的堆栈跟踪
java.util.ConcurrentModificationException
at java.util.ArrayList$Itr.next(ArrayList.java:860)
at com.google.gson.internal.bind.CollectionTypeAdapterFactory$Adapter.write(CollectionTypeAdapterFactory.java:96)
at com.google.gson.internal.bind.CollectionTypeAdapterFactory$Adapter.write(CollectionTypeAdapterFactory.java:61)
at com.google.gson.Gson.toJson(Gson.java:704)
at com.google.gson.Gson.toJson(Gson.java:683)
at com.google.gson.Gson.toJson(Gson.java:638)
at com.google.gson.Gson.toJson(Gson.java:618)
at my.supercool.app.component.module.JsonModule.toJson(JsonModule.kt:12)
at my.supercool.app.data.database.MyRoomObjectConverter.fromObjectToJson(MyRoomObjectConverter.kt:29)
at my.supercool.app.data.database.MyRoomObjectConverter.fromSomeListToJson(MyRoomObjectConverter.kt:71)
at my.supercool.app.data.database.dao.SomeDao_Impl$1.bind(SomeDao_Impl.java:101)
at my.supercool.app.data.database.dao.SomeDao_Impl$1.bind(SomeDao_Impl.java:47)
at androidx.room.EntityInsertionAdapter.insertAndReturnId(EntityInsertionAdapter.java:113)
at my.supercool.app.data.database.dao.SomeDao_Impl$4.call(SomeDao_Impl.java:142)
at my.supercool.app.data.database.dao.SomeDao_Impl$4.call(SomeDao_Impl.java:137)
at androidx.room.CoroutinesRoom$Companion$execute$2.invokeSuspend(CoroutinesRoom.kt:61)
at kotlin.coroutines.jvm.internal.BaseContinuationImpl.resumeWith(ContinuationImpl.kt:33)
at kotlinx.coroutines.DispatchedTask.run(DispatchedTask.kt:106)
at androidx.room.TransactionExecutor$1.run(TransactionExecutor.java:47)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1167)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:641)
at java.lang.Thread.run(Thread.java:923)
【问题讨论】:
-
如果您已经知道每次单击按钮时肯定会运行两个作业,那么有一种方法可以根据您的需要同步或异步运行作业:)
-
协程有
launch和async块可以做到这一点,如果你还想解释一下,请告诉我:) -
如果您的 ViewModel 引用了 Fragment,这是一个主要的禁忌。至于您手头的问题,我认为我们需要查看
MyRoomObjectConverter的代码才能知道出了什么问题。您可能需要同步某些内容或小心使用调度程序。 -
您应该研究 Flows,因为这可能是您正在寻找的范例。您可以拥有一系列给定的 GUI 事件,并为每个事件执行所需的操作。直到前一个事件已经通过,下一个事件才会通过流,从而消除了并发问题。
-
看来我的主要问题是我认为启动的协程(SomeScope.launch)会在后台以某种方式进行管理,不会相互竞争或同意。正如我现在了解到的,启动的协程就像启动一个新线程一样。启动多个启动似乎不是线程安全的,我认为是这种情况。有了这些知识,ConcurrentModifactionException 就不足为奇了。
标签: android kotlin architecture kotlin-coroutines