Kotlin and Retrofit: HTTP FAILED: java.io.IOException: Canceled only in Fragment

问题描述 投票:0回答:1

我不明白为什么当我从 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 中工作正常。我正在模拟器中测试代码。在片段中,当用户单击按钮时调用代码。

android kotlin retrofit kotlin-coroutines
1个回答
0
投票

您的错误的一个可能原因是 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 调用。

© www.soinside.com 2019 - 2024. All rights reserved.