【问题标题】:Emit Exception in Kotlin Flow Android在 Kotlin Flow Android 中发出异常
【发布时间】:2023-01-31 22:31:05
【问题描述】:

我在流程内部发出异常并低于异常。

IllegalStateException: Flow exception transparency is violated:
    Previous 'emit' call has thrown exception java.lang.NullPointerException, but then emission attempt of value 'planetbeyond.domain.api.Resource$Error@85b4d28' has been detected.
    Emissions from 'catch' blocks are prohibited in order to avoid unspecified behaviour, 'Flow.catch' operator can be used instead.
    For a more detailed explanation, please refer to Flow documentation.
       at kotlinx.coroutines.flow.internal.SafeCollector.exceptionTransparencyViolated(SafeCollector.kt:140)
       at kotlinx.coroutines.flow.internal.SafeCollector.checkContext(SafeCollector.kt:104)
       at kotlinx.coroutines.flow.internal.SafeCollector.emit(SafeCollector.kt:83)
       at kotlinx.coroutines.flow.internal.SafeCollector.emit(SafeCollector.kt:66)
       at planetbeyond.domain.use_cases.OptionSelectedCountUsecase$invoke$1.invokeSuspend(OptionSelectedCountUsecase.kt:20)

OptionSelectedCountUsecase.kt

class OptionSelectedCountUsecase @Inject constructor(
private val repository: Repository
) {
    operator fun invoke(questionId: Int): Flow<Resource<List<OptionSelectedCountModel>>> = flow {
        emit(Resource.Loading())
        try {
            val data = repository.getOptionSelectedCount(questionId)
            emit(Resource.Success(data))
        } catch (e: Exception) {
            emit(Resource.Error(e.toString()))// crashed at this line when api don't response anything or some sort of server error
        }
    }
}

资料库.kt

interface Repository{
  suspend fun getOptionSelectedCount(questionId: Int):List<OptionSelectedCountModel>
}

RepositoryImpl.kt

class RepositoryImpl @Inject constructor(
    private val apiService: ApiService
) : Repository {
   override suspend fun getOptionSelectedCount(questionId: Int): List<OptionSelectedCountModel> {
        return apiService.getOptionSelectedCount(questionId).data.map {
            it.toModel()
        }
    }
}

API服务.kt

interface ApiService {
    @GET("get_option_selected_count")
    suspend fun getOptionSelectedCount(
        @Query("question_id") question_id: Int
    ): WebResponse<List<OptionSelectedCountDto>>
}

LiveShowQuestionViewModel.kt

@HiltViewModel
class LiveShowQuestionsViewModel @Inject constructor(
    private val optionSelectedCountUsecase: OptionSelectedCountUsecase
) : ViewModel() { 
   fun getOptionSelectedCount(questionId: Int) {
        optionSelectedCountUsecase(questionId).onEach {
            when (it) {
                is Resource.Loading -> {
                    _optionSelectedCountState.value = OptionSelectedCountState(isLoading = true)
                }
                is Resource.Error -> {
                    _optionSelectedCountState.value = OptionSelectedCountState(error = it.message)
                }
                is Resource.Success -> {
                    _optionSelectedCountState.value = OptionSelectedCountState(data = it.data)
                }
            }
        }///.catch {  } // Why must I have to handle it here 
            .launchIn(viewModelScope)
    }
}

是否有必要像上面评论的那样处理流程外的异常。什么是最佳实践。

【问题讨论】:

  • 你不能像 catch 块那样发出,因为它可能会导致事物收集方面的意外行为......错误告诉你改为 use the catch 'operator'。如果你需要一个例子为什么这是不允许的,here's a small snippet 展示了“意外”行为。

标签: android rest kotlin kotlin-flow


【解决方案1】:

您不应手动发出异常和错误。否则,如果不检查发出的值是否为错误,流程的用户将不知道是否确实发生了异常。

您希望提供异常透明性,因此最好在收集流时处理它们。

其中一种方法是使用 catch 运算符。为了简化流收集,我们将捕获行为包装在一个函数中。

fun <T> Flow<T>.handleErrors(): Flow<T> = 
    catch { e -> showErrorMessage(e) }

然后,在收集流量时:

optionSelectedCountUsecase(questionId)
    .onEach { ... }
    .handleErrors()
    .launchIn(viewModelScope)

请注意,如果您只想处理来自用例调用的错误,您可以更改运算符的顺序。之前的命令也允许您处理来自 onEach 块的错误。下面的示例将仅处理来自用例调用的错误。

optionSelectedCountUsecase(questionId)
    .handleErrors()
    .onEach { ... }
    .launchIn(viewModelScope)

Read more about exception handling in flows

【讨论】:

    猜你喜欢
    • 2021-02-13
    • 2020-03-05
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2022-09-27
    • 1970-01-01
    • 2022-01-02
    • 1970-01-01
    相关资源
    最近更新 更多