Android Kotlin Flow 中发出异常

Tai*_*han 2 rest android kotlin kotlin-flow

我在流程内发出异常并得到以下异常。

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)
Run Code Online (Sandbox Code Playgroud)

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
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

存储库.kt

interface Repository{
  suspend fun getOptionSelectedCount(questionId: Int):List<OptionSelectedCountModel>
}
Run Code Online (Sandbox Code Playgroud)

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()
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

ApiService.kt

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

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)
    }
}
Run Code Online (Sandbox Code Playgroud)

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

Ten*_*r04 6

问题是您包装了一个emit调用try并尝试在匹配的块中发出catch。这意味着如果emit调用本身抛出(这可能是由流程的某些下游问题引起的),它将指示再次发出。这是非常暧昧和脆弱的行为。

相反,您可以将发出调用移到 try/catch 之外:

class OptionSelectedCountUsecase @Inject constructor(
private val repository: Repository
) {
    operator fun invoke(questionId: Int): Flow<Resource<List<OptionSelectedCountModel>>> = flow {
        emit(Resource.Loading())
        val result = try {
            val data = repository.getOptionSelectedCount(questionId)
            Resource.Success(data)
        } catch (e: Exception) {
            Resource.Error(e.toString())
        }
        emit(result)
    }
}
Run Code Online (Sandbox Code Playgroud)

不知何故,您在收集器中导致了 NullPointerException。这是一个需要解决的单独问题。