Kotlin Channel 의 기본 개념에 대해 알아보려고 합니다~
Kotlin Channel은 코루틴 간 통신을 위한 동시성 프리미티브로, 데이터를 안전하게 전송하고 공유할 수 있는 메커니즘을 제공합니다.
즉 비동기 데이터 스트림이라고 생각할 수 있습니다.
한쪽에서 데이터를 보내고(send), 다른 쪽에서는 데이터를 받는(receive) 파이프라인 처럼 작동합니다.

위처럼 한 코루틴은 생산자로서 데이터를 생성하여 채널에 보내고, 다른 코루틴은 소비자로서 채널에서 데이터를 받습니다. 생성자 - 소비자 패턴을 쉽게 구현할 수 있습니다.
자세한 개념에 대해 알아보기 전 기본 사용 방법에 대해 알아보겠습니다.
fun main() = runBlocking<Unit> {
val channel = Channel<Int>()
launch {
for (x in 1..5) { // 1부터 5까지 반복문
println("send ${x * x}")
channel.send(x * x) // send 시 채널에 데이터 전송
}
}
// receive 시 채널로부터 데이터 받음
repeat(5) { println("Receive = ${channel.receive()}") }
channel.close()
println("Done!")
}
val channel = Channel<Int>()
launch {
for (x in 1..5) channel.send(x * x)
channel.close() // we're done sending
}
// here we print received values using `for` loop (until the channel is closed)
for (y in channel) println(y)
println("Done!")
위처럼 작성 시 채널이 닫힐 때까지 모든 값을 수신하는 코드가 됩니다.
public interface SendChannel<in E>
public interface ReceiveChannel<out E>
public interface Channel<E> : SendChannel<E>, ReceiveChannel<E>
채널은 SendChannel , ReceiveChannel 인터페이스가 존재하고 Channel 인터페이스가 이 두개의 인터페이스를 따르고 있습니다.
SendChannel과 ReceiveChannel에 대해선 나중에 알아보고 우선 채널에 대해 자세히 알아보겠습니다.
public interface Channel<E> : SendChannel<E>, ReceiveChannel<E> {
public companion object Factory {
public const val UNLIMITED: Int = Int.MAX_VALUE
public const val RENDEZVOUS: Int = 0
public const val CONFLATED: Int = -1
public const val BUFFERED: Int = -2
internal const val OPTIONAL_CHANNEL = -3
public const val DEFAULT_BUFFER_PROPERTY_NAME: String = "kotlinx.coroutines.channels.defaultBuffer"
internal val CHANNEL_DEFAULT_CAPACITY = systemProp(DEFAULT_BUFFER_PROPERTY_NAME,
64, 1, UNLIMITED - 1
)
}
}
public fun <E> Channel(
capacity: Int = RENDEZVOUS,
onBufferOverflow: BufferOverflow = BufferOverflow.SUSPEND,
onUndeliveredElement: ((E) -> Unit)? = null
): Channel<E>
우선 Channel 인스턴스를 생성할 때 전달할 수 있는 파라미터에 대해 알아보겠습니다.
채널의 버퍼 용량을 결정합니다.
capacity는 백프레셔 처리할 방법을 정합니다.
백프레셔란 생산자가 소비자보다 빠르게 데이터를 발행하는 상황에 있어 Channel은 여러 버퍼링 전략을 통해 해결할 수 있도록 하는 전략을 말합니다..
capacity로 여러 값들을 설정할 수 있는데 이는 위의 Channel 인터페이스에 정의된 Factory에서 확인할 수 있습니다. 각 채널 용량으로 사용할 수 있는 미리 정의된 상수들입니다.
send 호출은 상대편에서 receive 를 호출할 때 까지 일시 중단됩니다(그 반대도 마찬가지). 즉, sendScope에서 send 후 receive가 불려야 다음 코드블럭으로 넘어갈 수 있습니다.send 호출은 절대 일시 중단되지 않습니다.버퍼가 가득 찼을 때의 동작을 지정합니다.
이는 BufferOverFlow Enum Class로 이뤄져 있습니다.
public enum class BufferOverflow {
SUSPEND,
DROP_OLDEST,
DROP_LATEST
}
각 정책에 대해 알아보겠습니다~
send 작업이 일시 중단됩니다. 버퍼에 공간이 생길 때까지 송신자는 대기합니다.전달되지 않은 요소를 처리하는 콜백 함수
다음과 같은 상황에서 호출됩니다.
send 작업이 취소되었을 때기본적으로 null이기 때문에 전달되지 않은 요소에 대한 특별한 처리는 이뤄지지 않습니다.
이 콜백은 전달되지 않은 요소의 리소스를 정리하는 데 유용합니다.
채널이 I/O fㅣ소스나 네트워크 연결과 같은 자원을 포함한 객체를 전송하는 경우 이러한 리소스를 적절히 해제할 수 있습니다.
Channel 이 무슨 역활을 하는지, 파라미터에 어떤 것이 있고 동작이 어떻게 달라지는 지 가볍게 알아봤습니다.
그럼 이러한 특성을 고려했을 때 Channel은 언제, 어떤 상황에 사용했을 때 유용할까요?
등 이 있지만 안드로이드에서 이벤트 처리 에 매우 유용하다고 생각합니다.
이벤트 처리의 특성으로
이 있습니다. 자세한 내용으로는 (https://medium.com/prnd/viewmodel에서-더이상-eventflow를-사용하지-마세요-3974e8ddffed 의 블로그를 참조하시면 이벤트 처리에 대한 매우 좋은 내용을 보실 수 있습니다.
이 때 BUFFERED을 사용한 채널을 사용하면 네트워크 도중 홈버튼을 눌렀을 때 이벤트가 도착해서 이를 처리할 수 있고 처리되었다면 사라지기 때문에 Configuration Change가 일어나도 같은 이벤트가 발생되지 않습니다.