我是否需要在@Composable 中使用 repeatOnLifecycle 扭曲热流的 collectAsState()?
Do I need to warp the collectAsState() of a hot Flow with repeatOnLifecycle in @Composable?
我已阅读文章A safer way to collect flows from Android UIs。
我知道以下内容。
由通道支持的冷流或使用带有缓冲区的运算符(例如 buffer、conflate、flowOn 或 shareIn)使用某些现有 API(例如 CoroutineScope.launch 收集是不安全的,Flow.launchIn,或者LifecycleCoroutineScope.launchWhenX,除非你在activity进入后台时手动取消启动协程的Job。这些 API 将保持底层流生产者处于活动状态,同时在后台将项目发送到缓冲区,从而浪费资源。
代码A来自官方样本project。
viewModel.suggestedDestinations
是一个MutableStateFlo
w,很火爆。
不知道热流的操作collectAsState()
在@Composable中是否安全UI.
1:我是否需要像代码 B 或代码 C 那样使用代码来替换代码 A 以获得 热流?
2: cold Flow 的操作collectAsState()
在@Composable UI.
中是否安全
代码A
@OptIn(ExperimentalMaterialApi::class)
@Composable
fun CraneHomeContent(
onExploreItemClicked: OnExploreItemClicked,
openDrawer: () -> Unit,
modifier: Modifier = Modifier,
viewModel: MainViewModel = viewModel(),
) {
val suggestedDestinations by viewModel.suggestedDestinations.collectAsState()
...
}
@HiltViewModel
class MainViewModel @Inject constructor(
...
) : ViewModel() {
...
private val _suggestedDestinations = MutableStateFlow<List<ExploreModel>>(emptyList())
val suggestedDestinations: StateFlow<List<ExploreModel>>
}
代码B
class LocationActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
lifecycleScope.launch {
repeatOnLifecycle(Lifecycle.State.STARTED) {
...
}
}
}
}
代码C
@Composable
fun LocationScreen(locationFlow: Flow<Flow>) {
val lifecycleOwner = LocalLifecycleOwner.current
val locationFlowLifecycleAware = remember(locationFlow, lifecycleOwner) {
locationFlow.flowWithLifecycle(lifecycleOwner.lifecycle, Lifecycle.State.STARTED)
}
val location by locationFlowLifecycleAware.collectAsState()
...
}
collectAsState
(代码 A)对任何类型的 Flow 都是安全的(cold/hot 没关系)。如果你看看 collectAsState
是如何实现的,那么你会发现它在深处使用了 LaunchedEffect
(collectAsState
-> produceState
-> LaunchedEffect
)
internal class LaunchedEffectImpl(
parentCoroutineContext: CoroutineContext,
private val task: suspend CoroutineScope.() -> Unit
) : RememberObserver {
private val scope = CoroutineScope(parentCoroutineContext)
private var job: Job? = null
override fun onRemembered() {
job?.cancel("Old job was still running!")
job = scope.launch(block = task)
}
override fun onForgotten() {
job?.cancel()
job = null
}
override fun onAbandoned() {
job?.cancel()
job = null
}
}
它创建一个协程范围并在进入组合后启动 task
lambda,并在它离开组合后自动取消它。
在代码 A 中,只要 CraneHomeContent
被其他代码调用,viewModel.suggestedDestinations.collectAsState()
(连同它的 LaunchedEffect 和协程作用域)就会处于活动状态。一旦 CraneHomeContent
停止被调用, collectAsState()
内部的 LaunchedEffect
就会被取消(协程范围也是如此)。
如果从多个地方调用它,那么会有多个 LaunchedEffect
,因此会有多个协程范围。
我已阅读文章A safer way to collect flows from Android UIs。
我知道以下内容。
由通道支持的冷流或使用带有缓冲区的运算符(例如 buffer、conflate、flowOn 或 shareIn)使用某些现有 API(例如 CoroutineScope.launch 收集是不安全的,Flow.launchIn,或者LifecycleCoroutineScope.launchWhenX,除非你在activity进入后台时手动取消启动协程的Job。这些 API 将保持底层流生产者处于活动状态,同时在后台将项目发送到缓冲区,从而浪费资源。
代码A来自官方样本project。
viewModel.suggestedDestinations
是一个MutableStateFlo
w,很火爆。
不知道热流的操作collectAsState()
在@Composable中是否安全UI.
1:我是否需要像代码 B 或代码 C 那样使用代码来替换代码 A 以获得 热流?
2: cold Flow 的操作collectAsState()
在@Composable UI.
代码A
@OptIn(ExperimentalMaterialApi::class)
@Composable
fun CraneHomeContent(
onExploreItemClicked: OnExploreItemClicked,
openDrawer: () -> Unit,
modifier: Modifier = Modifier,
viewModel: MainViewModel = viewModel(),
) {
val suggestedDestinations by viewModel.suggestedDestinations.collectAsState()
...
}
@HiltViewModel
class MainViewModel @Inject constructor(
...
) : ViewModel() {
...
private val _suggestedDestinations = MutableStateFlow<List<ExploreModel>>(emptyList())
val suggestedDestinations: StateFlow<List<ExploreModel>>
}
代码B
class LocationActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
lifecycleScope.launch {
repeatOnLifecycle(Lifecycle.State.STARTED) {
...
}
}
}
}
代码C
@Composable
fun LocationScreen(locationFlow: Flow<Flow>) {
val lifecycleOwner = LocalLifecycleOwner.current
val locationFlowLifecycleAware = remember(locationFlow, lifecycleOwner) {
locationFlow.flowWithLifecycle(lifecycleOwner.lifecycle, Lifecycle.State.STARTED)
}
val location by locationFlowLifecycleAware.collectAsState()
...
}
collectAsState
(代码 A)对任何类型的 Flow 都是安全的(cold/hot 没关系)。如果你看看 collectAsState
是如何实现的,那么你会发现它在深处使用了 LaunchedEffect
(collectAsState
-> produceState
-> LaunchedEffect
)
internal class LaunchedEffectImpl(
parentCoroutineContext: CoroutineContext,
private val task: suspend CoroutineScope.() -> Unit
) : RememberObserver {
private val scope = CoroutineScope(parentCoroutineContext)
private var job: Job? = null
override fun onRemembered() {
job?.cancel("Old job was still running!")
job = scope.launch(block = task)
}
override fun onForgotten() {
job?.cancel()
job = null
}
override fun onAbandoned() {
job?.cancel()
job = null
}
}
它创建一个协程范围并在进入组合后启动 task
lambda,并在它离开组合后自动取消它。
在代码 A 中,只要 CraneHomeContent
被其他代码调用,viewModel.suggestedDestinations.collectAsState()
(连同它的 LaunchedEffect 和协程作用域)就会处于活动状态。一旦 CraneHomeContent
停止被调用, collectAsState()
内部的 LaunchedEffect
就会被取消(协程范围也是如此)。
如果从多个地方调用它,那么会有多个 LaunchedEffect
,因此会有多个协程范围。