[Kotlin] CoroutineのChannelでドハマリしました

##ハマったこと

以下のコードのなにが問題だったのでしょうか？
(ちなみに以下のコードでは、`firstChannel: $it`のみが出力され、`secondChannel: $it`は出力されません。)

- `MainActivity#onResume`で`MainViewModel#onResume`を呼び出している想定です。
- `addChannel`ではそれぞれのChannelへ要素を（パイプライン的に）追加しています。
- 追加された要素はそれぞれのprivateなメソッドで各々消費しています。

```Kotlin
class MainViewModel : ViewModel() {

    private val firstChannel = ConflatedBroadcastChannel<String?>()
    private val secondChannel = ConflatedBroadcastChannel<Int?>()
    var count = 0

    fun onResume() {
        addChannel()
        viewModelScope.launch {
            processFirstChannel(firstChannel.openSubscription())
            processSecondChannel(secondChannel.openSubscription())
        }
    }

    fun addChannel() {
        viewModelScope.launch {
            while (isActive) {
                firstChannel.send("sample $count")
                secondChannel.send(count)
                delay(1000)
                count++
            }
        }
    }

    private suspend fun processFirstChannel(channel: ReceiveChannel<String?>?) {
        channel?.consumeEach { string ->
            string?.let {
                println("firstChannel: $it")
            }
        }
    }

    private suspend fun processSecondChannel(channel: ReceiveChannel<Int?>?) {
        channel?.consumeEach { int ->
            int?.let {
                println("secondChannel: $it")
            }
        }
    }
}
```

##問題点
何がイケなかったのかというと

```Kotlin
viewModelScope.launch {
    processFirstChannel(firstChannel.openSubscription())
    processSecondChannel(secondChannel.openSubscription())
}
```
上記で同一のCoroutineScope内で`consumeEach`していたからです。
`consumeEach`は要素が消費し終わるまで、もしくはchannelがcancelされるまで、処理が走り続けるので、以降の処理が走りません。なので、`processSecondChannel`の処理が走らずに`secondChannel: $it`が出力されなかったのです。

##解決法

1. 別のCoroutineScope内で`processSecondChannel`を処理する。
2. firstChannelをcancelする

`secondChannel: $it`を出力させるための方法として上記のように2つ考えられます。

###1. 別のCoroutineScope内で`processSecondChannel`を処理する。

```Kotlin
fun onResume() {
    addChannel()
    viewModelScope.launch {
        processFirstChannel(firstChannel.openSubscription())
    }
    viewModelScope.launch {
        processSecondChannel(secondChannel.openSubscription())
    }
}
```

上記のようにするか、もしくは、下記のようにするかです。

```Kotlin
fun onResume() {
    addChannel()
    processFirstChannel(firstChannel.openSubscription())
    processSecondChannel(secondChannel.openSubscription())
}

private fun processFirstChannel(channel: ReceiveChannel<String?>?) {
    viewModelScope.launch {
        channel?.consumeEach { string ->
            string?.let {
                println("firstChannel: $it")
            }
        }
    }
}

private fun processSecondChannel(channel: ReceiveChannel<Int?>?) {
    viewModelScope.launch {
        channel?.consumeEach { int ->
            int?.let {
                println("secondChannel: $it")
            }
        }
    }
}
```
要は、呼び出し元 or 呼び出し先で新たなCoroutineを起動させるということです。

###2. firstChannelをcancelする

要素を追加する処理を個別で行っていれば、firstChannelをcancelすることで`secondChannel: $it`を出力させることができます（ただし、今回の例では要素の追加を同一Coroutine内で行っているため、出力はされない）。

##まとめ
- `consumeEach`は、要素が消費し終わるまで、もしくはchannelがcancelされるまで、処理が走り続ける。（それ以降の処理が走らない）
- パイプライン的に要素をchannelに追加する場合や、BroadcastReceiverなどで監視し続けてその変化をchannelへ追加し続ける場合などの処理が複数ある場合は、同一のCoroutine内でやらず、別Coroutineを起動させる。

##おわりに
基本的なchannelの扱いではありますが、ドハマリしました(^_^;)