#android #kotlin-coroutines #android-jetpack #flow
Вопрос:
Я пытаюсь реализовать класс NetworkBoundResource в своем проекте, и это то, что я пытаюсь. Все работает правильно, получая ответ, кэшируя, но когда я передаю значение внутри FlowBuilder, он выходит из строя и показывает эту ошибку.
ошибка, которую я получаю:
Emission from another coroutine is detected.
Child of ProducerCoroutine{Active}@df26eb9, expected child of FlowCoroutine{Active}@a0bb2fe.
FlowCollector is not thread-safe and concurrent emissions are prohibited.
To mitigate this restriction please use 'channelFlow' builder instead of 'flow')' 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.
Класс сетевых ресурсов:
abstract class NetworkBoundResource<ResultType, RequestType> {
fun invoke(): Flow<Resource<ResultType>> = flow {
val rawData = loadFromDb()
if (shouldFetch(rawData)) {
fetchDataFromServer()
.onStart { emit(Resource.loading(rawData)) } // emit() causing issue
.catch { emit(Resource.error(it, null)) } // emit() causing issue
.collectLatest { }
}
}
// Save API response result into the database
protected abstract suspend fun cacheInDb(items: RequestType)
// Need to fetch data from server or not.
protected abstract fun shouldFetch(data: ResultType?): Boolean
// Show cached data from the database.
protected abstract suspend fun loadFromDb(): ResultType
// Fetch the data from server.
protected abstract suspend fun fetchDataFromServer(): Flow<ApiResponse<List<Category>>>
// when the fetch fails.
protected open fun onFetchFailed() {}
}
Класс репозитория:
fun getCategories(): Flow<Resource<List<Category>>> {
return object : NetworkBoundResource<List<Category>, List<Category>>() {
override suspend fun cacheInDb(items: List<Category>) {
withContext(Dispatchers.IO) { database.getCategories().insert(items) }
}
override fun shouldFetch(data: List<Category>?): Boolean {
return true
}
override suspend fun loadFromDb(): List<Category> {
return withContext(Dispatchers.IO) { database.getCategories().read() }
}
override suspend fun fetchDataFromServer(): Flow<ApiResponse<List<Category>>> {
return flow { emit(RetrofitModule.getCategories()) }
}
}.invoke()
}
Класс MyViewModel:
init {
viewModelScope.launch {
repository.getCategories().collectLatest {
if(it.data!=null){
_categories.value = it.data
Log.d("appDebug", " ViewModel : $it")
}
}
}
}
Ответ №1:
Как говорится в исключении, холодные потоки не позволяют emit()
одновременно. У вас есть два варианта:
- Замените
flow { }
наchannelFlow { }
и отправьте значения сsend()
помощью (возможно, в вашем случае это проще) - Убедитесь
emit()
, что одновременно вызывается no
Комментарии:
1. Большое спасибо за ваш ответ, это решит мою проблему.