다른 코루틴에서 방출이 감지되었습니다.
FlowCollector는 스레드로부터 안전하지 않으며 동시 배출이 금지됩니다.
이 제한을 완화하려면 'flow' 대신 'channelFlow' 빌더를 사용하십시오.
Coroutines Flow를 사용하다가 위와 같은 에러가 발생했다.
수정 전 문제가 있었던 코드
A-Fragment
onCreateView {
lifecycleScope.launch(Dispatchers.Main) {
viewModel.test(a).collect {data ->
goToBActivity(data)
}
}
}
viewModel
fun test() = flow {
viewModelScope.launch {
val getSomething = async {requestApi(A)}
val addSomething = async {equestApi(B)}
getSomething.await().let { data -> doSomething()}
val BData = addSomething.await()?.walletAccount
if (BData != null) {
emit(BData)
}
}
}.flowOn(Dispatchers.IO).catch { handleThrowable(it) }
뷰모델의 test()에서 두 API를 동시 처리하기 위해 CoroutineScope내에서 async, await를사용한뒤 그 결과값을 A-Fragment에서 사용해야했기에 flow로 값을 받아 사용하는 형태의 코드였다.
A-Fragment에서 CorountinesScope가 시작되었는데 viewModel에서 또 실행, emit을 해서 생긴 에러다.
발생 한 에러 로그에서 flow' 대신 'channelFlow' 빌더를 사용하라고 되어있어 channelFlow를 사용하하고 나와있어서 수정해보았다.
'channelFlow' 빌더로 수정한 코드
A-Fragment 는 변경사항 없음
viewModel
fun test() = callbackFlow {
viewModelScope.launch {
val getSomething = async {requestApi(A)}
val addSomething = async {equestApi(B)}
getSomething.await().let { data -> doSomething()}
val BData = addSomething.await()?.walletAccount
if (BData != null) {
trySend(BData)
}
}
}.flowOn(Dispatchers.IO).catch { handleThrowable(it) }
데이터를 flow밖으로 보내야해서 callbackFlow{}
를 사용해 trySend()
로 데이터를보냈다.
이렇게..
수정했는데...
안됐다..
에러는 안나는데 A-Fragment에서 실행되야할 goToBActivity(data) 부분이 실행이 안되는것이다.
검색결과
해당 함수는 close()가 호출될 때 까지 채널을 열어둔 상태로 기다린다고 한다.
공식문서 추가설명
Suspends until either 'onCompleted'/'onApiError' from the callback is invoked or flow collector is cancelled (e.g. by 'take(1)' or because a collector's coroutine was cancelled). In both cases, callback will be properly unregistered.
최종 수정된 viewModel
fun test() = callbackFlow {
viewModelScope.launch {
val getSomething = async {requestApi(A)}
val addSomething = async {equestApi(B)}
getSomething.await().let { data -> doSomething()}
val BData = addSomething.await()?.walletAccount
if (BData != null) {
trySend(BData)
}
}
awaitClose()
}.flowOn(Dispatchers.IO).catch { handleThrowable(it) }
awaitClose()를 추가하니 원하는대로 동작이 되었다.
와 정말 유익해요 저도 도움이 되었어요
그리고 정말 많이 공부하신 거 같아요 ^^b