Skip to content

Commit 7bcfff3

Browse files
committed
better CollectWithLifecycleEffect
1 parent f523d2b commit 7bcfff3

File tree

1 file changed

+62
-15
lines changed

1 file changed

+62
-15
lines changed

sample/app/src/main/java/com/hoc081098/channeleventbus/sample/android/common/CollectWithLifecycleEffect.kt

Lines changed: 62 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,60 +1,96 @@
11
package com.hoc081098.channeleventbus.sample.android.common
22

33
import androidx.compose.runtime.Composable
4+
import androidx.compose.runtime.Immutable
45
import androidx.compose.runtime.LaunchedEffect
56
import androidx.compose.runtime.NonRestartableComposable
67
import androidx.compose.runtime.RememberObserver
7-
import androidx.compose.runtime.getValue
88
import androidx.compose.runtime.remember
99
import androidx.compose.runtime.rememberUpdatedState
1010
import androidx.compose.ui.platform.LocalLifecycleOwner
1111
import androidx.lifecycle.Lifecycle
1212
import androidx.lifecycle.LifecycleOwner
1313
import androidx.lifecycle.repeatOnLifecycle
14+
import kotlinx.coroutines.CoroutineDispatcher
1415
import kotlinx.coroutines.CoroutineScope
1516
import kotlinx.coroutines.Dispatchers
1617
import kotlinx.coroutines.Job
1718
import kotlinx.coroutines.cancel
1819
import kotlinx.coroutines.flow.Flow
1920
import kotlinx.coroutines.launch
2021

22+
@Immutable
23+
enum class CollectWithLifecycleEffectDispatcher {
24+
/**
25+
* Use [Dispatchers.Main][kotlinx.coroutines.MainCoroutineDispatcher].
26+
*/
27+
Main,
28+
29+
/**
30+
* Use [Dispatchers.Main.immediate][kotlinx.coroutines.MainCoroutineDispatcher.immediate].
31+
*/
32+
ImmediateMain,
33+
34+
/**
35+
* Use [androidx.compose.runtime.Composer.applyCoroutineContext].
36+
* Under the hood, it uses Compose [androidx.compose.ui.platform.AndroidUiDispatcher].
37+
*/
38+
Composer,
39+
}
40+
2141
/**
2242
* Collect the given [Flow] in an effect that runs when [LifecycleOwner.lifecycle] is at least at [minActiveState].
2343
*
24-
* If [inImmediateMain] is `true`, the effect will run in
25-
* [Dispatchers.Main.immediate][kotlinx.coroutines.MainCoroutineDispatcher.immediate],
26-
* otherwise it will run in [androidx.compose.runtime.Composer.applyCoroutineContext].
44+
* - If [dispatcher] is [CollectWithLifecycleEffectDispatcher.ImmediateMain], the effect will run in
45+
* [Dispatchers.Main.immediate][kotlinx.coroutines.MainCoroutineDispatcher.immediate].
46+
* - If [dispatcher] is [CollectWithLifecycleEffectDispatcher.Main], the effect will run in
47+
* [Dispatchers.Main][kotlinx.coroutines.MainCoroutineDispatcher].
48+
* - If [dispatcher] is [CollectWithLifecycleEffectDispatcher.Composer], the effect will run in
49+
* [androidx.compose.runtime.Composer.applyCoroutineContext].
50+
*
51+
* NOTE: When [dispatcher] or [collector] changes, the effect will **NOT** be restarted.
52+
* The latest [collector] will be used to receive values from the [Flow] ([rememberUpdatedState] is used).
53+
* If you want to restart the effect, you need to change [keys].
2754
*
2855
* @param keys Keys to be used to [remember] the effect.
2956
* @param lifecycleOwner The [LifecycleOwner] to be used to [repeatOnLifecycle].
3057
* @param minActiveState The minimum [Lifecycle.State] to be used to [repeatOnLifecycle].
31-
* @param inImmediateMain Whether the effect should run in
32-
* [Dispatchers.Main.immediate][kotlinx.coroutines.MainCoroutineDispatcher.immediate].
58+
* @param dispatcher The dispatcher to be used to launch the [Flow].
3359
* @param collector The collector to be used to collect the [Flow].
3460
*
3561
* @see [LaunchedEffect]
62+
* @see [CollectWithLifecycleEffectDispatcher]
3663
*/
3764
@Composable
3865
fun <T> Flow<T>.CollectWithLifecycleEffect(
3966
vararg keys: Any?,
4067
lifecycleOwner: LifecycleOwner = LocalLifecycleOwner.current,
4168
minActiveState: Lifecycle.State = Lifecycle.State.STARTED,
42-
inImmediateMain: Boolean = true,
69+
dispatcher: CollectWithLifecycleEffectDispatcher = CollectWithLifecycleEffectDispatcher.ImmediateMain,
4370
collector: (T) -> Unit,
4471
) {
4572
val flow = this
46-
val currentCollector by rememberUpdatedState(collector)
73+
val collectorState = rememberUpdatedState(collector)
4774

4875
val block: suspend CoroutineScope.() -> Unit = {
4976
lifecycleOwner.repeatOnLifecycle(minActiveState) {
50-
flow.collect(currentCollector)
77+
// NOTE: we don't use `flow.collect(collectState.value)` because it can use the old value
78+
flow.collect { collectorState.value(it) }
5179
}
5280
}
5381

54-
if (inImmediateMain) {
55-
LaunchedEffectInImmediateMain(flow, lifecycleOwner, minActiveState, *keys, block = block)
56-
} else {
57-
LaunchedEffect(flow, lifecycleOwner, minActiveState, *keys, block = block)
82+
when (dispatcher) {
83+
CollectWithLifecycleEffectDispatcher.ImmediateMain -> {
84+
LaunchedEffectInImmediateMain(flow, lifecycleOwner, minActiveState, *keys, block = block)
85+
}
86+
87+
CollectWithLifecycleEffectDispatcher.Main -> {
88+
LaunchedEffectInMain(flow, lifecycleOwner, minActiveState, *keys, block = block)
89+
}
90+
91+
CollectWithLifecycleEffectDispatcher.Composer -> {
92+
LaunchedEffect(flow, lifecycleOwner, minActiveState, *keys, block = block)
93+
}
5894
}
5995
}
6096

@@ -65,13 +101,24 @@ private fun LaunchedEffectInImmediateMain(
65101
vararg keys: Any?,
66102
block: suspend CoroutineScope.() -> Unit,
67103
) {
68-
remember(*keys) { LaunchedEffectImpl(block) }
104+
remember(*keys) { LaunchedEffectImpl(block, Dispatchers.Main.immediate) }
105+
}
106+
107+
@Composable
108+
@NonRestartableComposable
109+
@Suppress("ArrayReturn")
110+
private fun LaunchedEffectInMain(
111+
vararg keys: Any?,
112+
block: suspend CoroutineScope.() -> Unit,
113+
) {
114+
remember(*keys) { LaunchedEffectImpl(block, Dispatchers.Main) }
69115
}
70116

71117
private class LaunchedEffectImpl(
72118
private val task: suspend CoroutineScope.() -> Unit,
119+
dispatcher: CoroutineDispatcher,
73120
) : RememberObserver {
74-
private val scope = CoroutineScope(Dispatchers.Main.immediate)
121+
private val scope = CoroutineScope(dispatcher)
75122
private var job: Job? = null
76123

77124
override fun onRemembered() {

0 commit comments

Comments
 (0)