He leído el artículo Una forma más segura de recopilar flujos de las IU de Android .
Conozco el siguiente contenido.
No es seguro recopilar un flujo en frío respaldado por un canal o que utiliza operadores con búferes como búfer, conflate, flowOn o shareIn con algunas de las API existentes, como CoroutineScope.launch, Flow.launchIn o LifecycleCoroutineScope.launchWhenX, a menos que cancele manualmente el trabajo que inició la rutina cuando la actividad pasa a segundo plano. Estas API mantendrán activo el productor de flujo subyacente mientras emiten elementos al búfer en segundo plano y, por lo tanto, desperdician recursos.
El Código A es del proyecto de muestra oficial.
El viewModel.suggestedDestinations es un MutableStateFlo w, es un flujo activo.
No sé si la operación collectAsState() de Hot Flow es segura en @Composable UI.
1: ¿Necesito usar el Código como el Código B o el Código C para reemplazar el Código A por un flujo caliente ?
2: ¿Es segura la operación collectAsState() de Cold Flow en @Composable UI?
Código 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>> }Código B
class LocationActivity : AppCompatActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) lifecycleScope.launch { repeatOnLifecycle(Lifecycle.State.STARTED) { ... } } } }Código 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 (Código A) es seguro para cualquier tipo de Flujo (frío/caliente, no importa). Si observa cómo se implementa collectAsState , verá que utiliza un LaunchedEffect en el fondo ( 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 } } que crea un ámbito de rutina y lanza la task lambda una vez que ingresa a la composición y la cancela automáticamente una vez que abandona la composición.
En el Código A, viewModel.suggestedDestinations.collectAsState() (junto con su LaunchedEffect y su ámbito de rutina) estará activo siempre que otro código llame a CraneHomeContent . Tan pronto como se deja de llamar a CraneHomeContent , se cancela LaunchedEffect dentro de collectAsState() (y también el alcance de la rutina).
Si se llama desde varios lugares, habrá varios LaunchedEffect y, por lo tanto, varios ámbitos de rutina.