协程范围内的处理列表导致异常
Processing list in Coroutines scope causes exception
我有一个功能可以在 Google 地图上显示标记(我称之为任务)。为了使其更清晰,我选择了一种遍历已显示任务和新任务的方法。如果任务已存在于地图中但不在新列表中,我将删除其标记。如果任务在两个列表中但其版本已更改,我修改其标记。如果任务只在新列表中,我会添加它的标记。
每次用户滚动浏览地图时,我都会根据滚动的位置搜索任务。用户可以多次滚动,这应该会停止该函数的上一次调用。
问题是有时我会得到这个异常
Fatal Exception: java.util.ConcurrentModificationException
at java.util.ArrayList$Itr.next(ArrayList.java:860)
at com.weryou.android.ui.missions.search.map.SearchMissionMapFragment$displayMissions.invokeSuspend(SearchMissionMapFragment.kt:435)
at kotlin.coroutines.jvm.internal.BaseContinuationImpl.resumeWith(ContinuationImpl.kt:33)
at kotlinx.coroutines.DispatchedTask.run(DispatchedTask.kt:106)
at kotlinx.coroutines.scheduling.CoroutineScheduler.runSafely(CoroutineScheduler.kt:571)
at kotlinx.coroutines.scheduling.CoroutineScheduler$Worker.executeTask(CoroutineScheduler.kt:750)
at kotlinx.coroutines.scheduling.CoroutineScheduler$Worker.runWorker(CoroutineScheduler.kt:678)
at kotlinx.coroutines.scheduling.CoroutineScheduler$Worker.run(CoroutineScheduler.kt:665)
还有这个例外
Fatal Exception: java.lang.IndexOutOfBoundsException: Index: 132, Size: 132
at java.util.ArrayList.get(ArrayList.java:437)
at kotlin.collections.CollectionsKt__MutableCollectionsKt.filterInPlace$CollectionsKt__MutableCollectionsKt(CollectionsKt__MutableCollectionsKt.java:284)
at kotlin.collections.CollectionsKt__MutableCollectionsKt.removeAll(CollectionsKt__MutableCollectionsKt.java:269)
at com.weryou.android.ui.missions.search.map.SearchMissionMapFragment$displayMissions.invokeSuspend(SearchMissionMapFragment.kt:220)
at kotlin.coroutines.jvm.internal.BaseContinuationImpl.resumeWith(ContinuationImpl.kt:33)
at kotlinx.coroutines.DispatchedTask.run(DispatchedTask.kt:106)
at kotlinx.coroutines.scheduling.CoroutineScheduler.runSafely(CoroutineScheduler.kt:571)
at kotlinx.coroutines.scheduling.CoroutineScheduler$Worker.executeTask(CoroutineScheduler.kt:750)
at kotlinx.coroutines.scheduling.CoroutineScheduler$Worker.runWorker(CoroutineScheduler.kt:678)
at kotlinx.coroutines.scheduling.CoroutineScheduler$Worker.run(CoroutineScheduler.kt:665)
这是我的功能
private val alreadyDisplayedMissions = ArrayList<Pair<MissionHeaderEntity, Marker>>()
private fun displayMissions(newMissions : List<MissionHeaderEntity>)
{
lifecycleScope.launch(Dispatchers.Default) {
val missionsToRemove : List<Pair<MissionHeaderEntity, Marker>> = alreadyDisplayedMissions.filter { oldMission ->
val newVersionOfOldMission : MissionHeaderEntity? = newMissions.find { it.id == oldMission.first.id }
newVersionOfOldMission == null || newVersionOfOldMission != oldMission.first
}
alreadyDisplayedMissions.removeAll { mission -> missionsToRemove.any { mission.first.id == it.first.id } }
// Loop in the new missions to display in map all new missions with a determined location,
// plus the tuto mission of the user if exists.
val missionsToShow : List<Pair<MissionHeaderEntity, MarkerOptions>> = newMissions.filter { newMission ->
(newMission.firstMission == true && newMission.owned == true)
|| alreadyDisplayedMissions.none { newMission.id == it.first.id }
|| (newMission.place?.address?.location?.latitude != null && newMission.place?.address?.location?.longitude != null)
}.map { newMission ->
// The icon resource of the marker to display
val markerIcon : Drawable = when(newMission.state)
{
MissionState.BOOKED -> markerMissionBooked
MissionState.AVAILABLE -> markerMissionAvailable
MissionState.PRE_RELEASED -> markerMissionPreReleased
else -> markerMissionOwned
}
// The icon of the marker to display .
val markerBitmap : Bitmap = mapUtils.createMissionMarkerIcon(markerIcon, newMission.costing?.price.toCurrency(requireContext()))
val markerLatitude : Double = newMission.place?.address?.location?.latitude!!
val markerLongitude : Double = newMission.place?.address?.location?.longitude!!
// The location of the marker to display.
val markerLatLng = LatLng(markerLatitude, markerLongitude)
val markerOptions : MarkerOptions = MarkerOptions()
.position(markerLatLng)
.icon(BitmapDescriptorFactory.fromBitmap(markerBitmap))
Pair(newMission, markerOptions)
}
lifecycleScope.launch(Dispatchers.Main) {
missionsToRemove.forEach { it.second.remove() }
missionsToShow.forEach {
val marker : Marker? = googleMap?.addMarker(it.second)
marker?.tag = it.first
alreadyDisplayedMissions.add(Pair(it.first, marker!!))
}
}
}
}
我的函数有什么问题以及如何解决这些异常?
我假设 java.util.ConcurrentModificationException
异常发生在 alreadyDisplayedMissions
列表中。您可以尝试使用 Job
实例,由 launch
函数返回。您可以在下一个作业开始时取消当前作业。在这种情况下,您应该使用 isActive
属性 或 ensureActive
函数检查当前作业是否处于活动状态:
private var job:工作? =空
private fun displayMissions(newMissions : List<MissionHeaderEntity>) {
job?.cancel()
lifecycleScope.launch(Dispatchers.Default) {
// use ensureActive() everywhere before using alreadyDisplayedMissions list
ensureActive()
...
}
}
ensureActive()
如果作业不再处于活动状态,则抛出 CancellationException
,因此在该函数之后将不会继续执行。
我有一个功能可以在 Google 地图上显示标记(我称之为任务)。为了使其更清晰,我选择了一种遍历已显示任务和新任务的方法。如果任务已存在于地图中但不在新列表中,我将删除其标记。如果任务在两个列表中但其版本已更改,我修改其标记。如果任务只在新列表中,我会添加它的标记。 每次用户滚动浏览地图时,我都会根据滚动的位置搜索任务。用户可以多次滚动,这应该会停止该函数的上一次调用。
问题是有时我会得到这个异常
Fatal Exception: java.util.ConcurrentModificationException
at java.util.ArrayList$Itr.next(ArrayList.java:860)
at com.weryou.android.ui.missions.search.map.SearchMissionMapFragment$displayMissions.invokeSuspend(SearchMissionMapFragment.kt:435)
at kotlin.coroutines.jvm.internal.BaseContinuationImpl.resumeWith(ContinuationImpl.kt:33)
at kotlinx.coroutines.DispatchedTask.run(DispatchedTask.kt:106)
at kotlinx.coroutines.scheduling.CoroutineScheduler.runSafely(CoroutineScheduler.kt:571)
at kotlinx.coroutines.scheduling.CoroutineScheduler$Worker.executeTask(CoroutineScheduler.kt:750)
at kotlinx.coroutines.scheduling.CoroutineScheduler$Worker.runWorker(CoroutineScheduler.kt:678)
at kotlinx.coroutines.scheduling.CoroutineScheduler$Worker.run(CoroutineScheduler.kt:665)
还有这个例外
Fatal Exception: java.lang.IndexOutOfBoundsException: Index: 132, Size: 132
at java.util.ArrayList.get(ArrayList.java:437)
at kotlin.collections.CollectionsKt__MutableCollectionsKt.filterInPlace$CollectionsKt__MutableCollectionsKt(CollectionsKt__MutableCollectionsKt.java:284)
at kotlin.collections.CollectionsKt__MutableCollectionsKt.removeAll(CollectionsKt__MutableCollectionsKt.java:269)
at com.weryou.android.ui.missions.search.map.SearchMissionMapFragment$displayMissions.invokeSuspend(SearchMissionMapFragment.kt:220)
at kotlin.coroutines.jvm.internal.BaseContinuationImpl.resumeWith(ContinuationImpl.kt:33)
at kotlinx.coroutines.DispatchedTask.run(DispatchedTask.kt:106)
at kotlinx.coroutines.scheduling.CoroutineScheduler.runSafely(CoroutineScheduler.kt:571)
at kotlinx.coroutines.scheduling.CoroutineScheduler$Worker.executeTask(CoroutineScheduler.kt:750)
at kotlinx.coroutines.scheduling.CoroutineScheduler$Worker.runWorker(CoroutineScheduler.kt:678)
at kotlinx.coroutines.scheduling.CoroutineScheduler$Worker.run(CoroutineScheduler.kt:665)
这是我的功能
private val alreadyDisplayedMissions = ArrayList<Pair<MissionHeaderEntity, Marker>>()
private fun displayMissions(newMissions : List<MissionHeaderEntity>)
{
lifecycleScope.launch(Dispatchers.Default) {
val missionsToRemove : List<Pair<MissionHeaderEntity, Marker>> = alreadyDisplayedMissions.filter { oldMission ->
val newVersionOfOldMission : MissionHeaderEntity? = newMissions.find { it.id == oldMission.first.id }
newVersionOfOldMission == null || newVersionOfOldMission != oldMission.first
}
alreadyDisplayedMissions.removeAll { mission -> missionsToRemove.any { mission.first.id == it.first.id } }
// Loop in the new missions to display in map all new missions with a determined location,
// plus the tuto mission of the user if exists.
val missionsToShow : List<Pair<MissionHeaderEntity, MarkerOptions>> = newMissions.filter { newMission ->
(newMission.firstMission == true && newMission.owned == true)
|| alreadyDisplayedMissions.none { newMission.id == it.first.id }
|| (newMission.place?.address?.location?.latitude != null && newMission.place?.address?.location?.longitude != null)
}.map { newMission ->
// The icon resource of the marker to display
val markerIcon : Drawable = when(newMission.state)
{
MissionState.BOOKED -> markerMissionBooked
MissionState.AVAILABLE -> markerMissionAvailable
MissionState.PRE_RELEASED -> markerMissionPreReleased
else -> markerMissionOwned
}
// The icon of the marker to display .
val markerBitmap : Bitmap = mapUtils.createMissionMarkerIcon(markerIcon, newMission.costing?.price.toCurrency(requireContext()))
val markerLatitude : Double = newMission.place?.address?.location?.latitude!!
val markerLongitude : Double = newMission.place?.address?.location?.longitude!!
// The location of the marker to display.
val markerLatLng = LatLng(markerLatitude, markerLongitude)
val markerOptions : MarkerOptions = MarkerOptions()
.position(markerLatLng)
.icon(BitmapDescriptorFactory.fromBitmap(markerBitmap))
Pair(newMission, markerOptions)
}
lifecycleScope.launch(Dispatchers.Main) {
missionsToRemove.forEach { it.second.remove() }
missionsToShow.forEach {
val marker : Marker? = googleMap?.addMarker(it.second)
marker?.tag = it.first
alreadyDisplayedMissions.add(Pair(it.first, marker!!))
}
}
}
}
我的函数有什么问题以及如何解决这些异常?
我假设 java.util.ConcurrentModificationException
异常发生在 alreadyDisplayedMissions
列表中。您可以尝试使用 Job
实例,由 launch
函数返回。您可以在下一个作业开始时取消当前作业。在这种情况下,您应该使用 isActive
属性 或 ensureActive
函数检查当前作业是否处于活动状态:
private var job:工作? =空
private fun displayMissions(newMissions : List<MissionHeaderEntity>) {
job?.cancel()
lifecycleScope.launch(Dispatchers.Default) {
// use ensureActive() everywhere before using alreadyDisplayedMissions list
ensureActive()
...
}
}
ensureActive()
如果作业不再处于活动状态,则抛出 CancellationException
,因此在该函数之后将不会继续执行。