channel을 이용해서 코루틴에서 데이터를 주고받기 위한 flow
한 블럭안에서 여러개의 컨텍스트를 사용할 수 있음
channelFlow<Int> {
launch(Dispatchers.IO){
flow1.collectLatest {
print("flow1 $it")
send(it)
}
}
launch(Dispatchers.Main) {
flow2.collectLatest {
print("flow2 $it")
send(it)
}
}
awaitClose {
print(">>>> close")
}
}
send()
를 통해서 채널로 데이터를 보낼 수도 있지만 send()
함수는 suspend가 달려있다.
비동기가아닌 동기형식으로 데이터를 보내고 싶다면
trySendBlocking(it)
.onFailure { }
.onSuccess { }
.onClosed { }
or
trySend(it)
이런 식으로 trySendBlocking
, trySend
를 통해서도 가능하다
둘의 차이점으로는
trySend()
는 버퍼가 꽉 차있는 경우 현재 값을 무시하고 다음 값을 보내려고하며
trySendBlocking()
의 경우 버퍼가 꽉 차있는 경우, 버퍼의 자리가 빌때까지 기다린 후 다시 시도한다.
위 ChannelFlow가 기반이며 큰 차이는 없다 다만 데이터를 flow밖으로 보내야할 일이 있다면 해당 flow를 사용하면된다.
다만 사용시에 awaitClose
를 호출해야 런타임 에러없이 사용이 가능하다.
해당 함수는 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.
이렇게 설명되어있다.
fun doLogin(api : LoginAPI) = callbackFlow {
val callback = object : LoginCallback {
override fun onSuccess(token: String) {
trySendBlocking(token)
.onFailure {
}
.onSuccess {
}
}
override fun onFail(error: Exception) {
cancel()
}
override fun onComplete() {
channel.close()
}
}
api.login(callback)
awaitClose { api.free(callback) }
}
아직 실제로 사용해보지 않아 이해도가 높지 않다 실 프로젝트에서 한번 적용시켜봐야 더 정확히 문서를 작성할 수 있을듯 하다.
참고링크