我不明白为什么当我从 Fragment 拨打电话时它会抛出这个错误。如果我从 MainActivity 进行调用,代码就可以工作。
这是
ViewModel
中的代码:
fun initialSync(syncRequest: SyncRequest) {
_uiState.value = SyncUiState.Loading
viewModelScope.launch(coroutineDispatcherProvider.IO()) {
try {
val result = getSyncUseCase.execute(syncRequest)
_uiState.value = SyncUiState.Loaded(SyncItemUiState(result))
} catch (error: Exception) {
_uiState.value = SyncUiState.Error(error.message.toString())
}
}
}
在 UseCase 中,我将此 Repository 实现称为:
override suspend fun getSync(syncRequest: SyncRequest): SyncResponse {
var syncResponse = SyncResponse(SpannableStringBuilder(), emptyList())
if(syncRequest.type==1) {
val syncNetwork = syncFactory.create(Source.NETWORK).getSync(syncRequest)
if(syncNetwork.allToday.isNotEmpty()) {
syncResponse.allToday=syncNetwork.allToday
syncResponse.dataForView=syncNetwork.dataForView
}else{
val syncFirebase = syncFactory.create(Source.FIREBASE).getSync(syncRequest)
syncResponse.allToday=syncFirebase.allToday
syncResponse.dataForView=syncFirebase.dataForView
}
}
return syncResponse
}
这是管理 API 调用的代码:
override suspend fun getSync(syncRequest: SyncRequest): SyncResponse {
val todayAll=todayApi.getTodayAll("all")
val syncResponse= SyncResponse(SpannableStringBuilder(),todayAll)
return syncResponse
}
这是 API 调用的代码:
@GET("today/{thePath}")
suspend fun getTodayAll(@Path("thePath") thePath: String?): List<Today?>
ViewModel 正在捕获此错误:
kotlinx.coroutines.JobCancellationException:作业被取消; job=SupervisorJobImpl{取消}@c05b47
我不明白为什么当我从一个片段调用时会出现这个错误:
val syncRequest = SyncRequest(0, 1,false,false)
syncViewModel.initialSync(syncRequest)
同样的调用在 MainActivity 中工作正常。我正在模拟器中测试代码。在片段中,当用户单击按钮时调用代码。
您的错误的一个可能原因是 Fragment 的生命周期在 API 调用完成之前结束。当 Fragment 被销毁或从 Activity 中分离时,任何在该 Fragment 的 ViewModel 中启动的正在进行的协程都将被取消。当 Fragment 不再可见时,这是避免内存泄漏或不必要工作的正常行为。
为了解决这个问题,你可以使用
viewModelScope
,它与ViewModel本身的生命周期相关联。这样,在 ViewModel 范围内启动的协程将在 ViewModel 被清除或相关活动被销毁时自动取消。
要使用
viewModelScope
,请确保您的 Fragment 使用 by viewModels()
委托来检索 ViewModel。这是一个例子:
private val syncViewModel: SyncViewModel by viewModels()
private fun makeApiCall() {
val syncRequest = SyncRequest(0, 1, false, false)
syncViewModel.initialSync(syncRequest)
}
通过使用
by viewModels()
,ViewModel 将与Fragment 相关联并绑定到它的生命周期。然后,在您的 ViewModel 中,使用 viewModelScope
而不是 coroutineScope
来启动协程:
fun initialSync(syncRequest: SyncRequest) {
_uiState.value = SyncUiState.Loading
viewModelScope.launch(coroutineDispatcherProvider.IO()) {
// Your API call and response handling code
}
}
通过这些更改,当关联的 Fragment 被销毁或分离时,
viewModelScope
内启动的协程将自动取消,确保相应地完成或取消 API 调用。