Skip to content

hoc081098/DemoCoroutinesChannelResult

Repository files navigation

DemoCoroutinesChannelResult

Use Kotlinx Coroutines Channel to send and receive events between Fragments.

Hits

Overview

1. Create MainSingleEvent class.

sealed interface MainSingleEvent<out T : MainSingleEvent<T>> {
  interface Key<out T : MainSingleEvent<T>>

  val key: Key<T>

  data class HomeFragmentResult(val text: String) : MainSingleEvent<HomeFragmentResult> {
    override val key = HomeFragmentResult

    companion object : Key<HomeFragmentResult>
  }

  data class DashboardFragmentResult(val text: String) : MainSingleEvent<DashboardFragmentResult> {
    override val key = DashboardFragmentResult

    companion object : Key<DashboardFragmentResult>
  }

  data class HomeDetailsResult(val text: String) : MainSingleEvent<HomeDetailsResult> {
    override val key = HomeDetailsResult

    companion object : Key<HomeDetailsResult>
  }

  companion object {
    val KEYS: Set<Key<SomeMainSingleEvent>> = setOf(
      HomeFragmentResult,
      DashboardFragmentResult,
      HomeDetailsResult,
    )
  }
}

2. Create MainVM class with buffered channels as event bus.

class MainVM : ViewModel() {
  private val eventChannels: Map<MainSingleEvent.Key<SomeMainSingleEvent>, Channel<SomeMainSingleEvent>> =
    MainSingleEvent.KEYS.associateBy(
      keySelector = { it },
      valueTransform = { Channel<MainSingleEvent<SomeMainSingleEvent>>(Channel.UNLIMITED) }
    )

  fun <T : MainSingleEvent<T>> sendEvent(event: T) {
    checkNotNull(eventChannels[event.key]) { "Must register ${event.key} in MainSingleEvent.Companion.KEYS before using!" }
      .trySend(event)
      .getOrThrow()
      .also { Log.d("@@@", "Sent $event") }
  }

  @Suppress("UNCHECKED_CAST")
  fun <T : MainSingleEvent<T>, K : MainSingleEvent.Key<T>> receiveEventFlow(key: K): Flow<T> =
    checkNotNull(eventChannels[key]) { "Must register $key in MainSingleEvent.Companion.KEYS before using!" }
      .receiveAsFlow()
      .map { it as T }
}

3. Send and receive events in Fragments.

We will share MainVM instance between Fragments using Activity as owner.

private val mainVM by viewModels<MainVM>(
  ownerProducer = { requireActivity() }
)

// send in HomeFragment
mainVM.sendEvent(MainSingleEvent.HomeFragmentResult("Hello from HomeFragment"))

// receive in others
mainVM.receiveEventFlow(MainSingleEvent.HomeFragmentResult)
  .onEach { Log.d("###", "Received $it") }
  .launchIn(lifecycleScope)