machfour
Contexto
En Jetpack compose, tenemos la opción de usar rememberCoroutineScope()
así como el uso de la LaunchedEffect
componible para usar corrutinas / ejecutar funciones de suspensión (mostrar snackbars, etc.).
La convención que he adoptado hasta ahora es recordar un solo ámbito de rutina en la parte superior de mi árbol de composición y pasarlo a través de argumentos de función a los lugares donde se necesita. Esto parece vagamente una buena práctica, pero por otro lado está agregando ruido adicional a las firmas de mi función.
Preguntas
- ¿Hay alguna razón para preferir el uso de
LaunchedEffect
sobrerememberCoroutineScope()
dentro de funciones componibles? - ¿Vale la pena el esfuerzo de solo crear/recordar un ámbito de rutina una vez por árbol de composición, o debería simplemente llamar
rememberCoroutineScope()
en cada función donde realmente se lanza una rutina?
nglauber
Dejando mi entendimiento aquí:
Pregunta 1:
LaunchedEffect
debe usarse cuando desee que se deba tomar alguna acción cuando su componible se inicie/reinicie por primera vez (o cuando el parámetro clave haya cambiado). Por ejemplo, cuando desea solicitar algunos datos de su ViewModel o ejecutar algún tipo de animación…
rememberCoroutineScope
por otro lado, es específico para almacenar el alcance Coroutine permitiendo que el código lance algunos suspend
función… en mi humilde opinión, la única relación entre ellos es que también puede utilizar un LaunchedEffect
para lanzar una rutina…
Pregunta 2: Como se puede ver en los documentos, rememberCoroutineScope
mantendrá la referencia del alcance de la rutina en un punto específico de la composición. Por lo tanto, si un componible determinado se elimina de la recomposición, esa corrutina se cancelará automáticamente. Por ejemplo, tiene las siguientes llamadas componibles A -> B -> C
. Si recuerdas el alcance de la rutina en C
y se elimina de la composición, la rutina se cancela automáticamente. Pero si recuerdas de A
pase el alcance a través B
y C
use este alcance en C
y entonces C
se elimina, la corrutina seguirá ejecutándose (porque se recordó en A
)…
-
Anexo: estoy usando AS Arctic Fox Canary 12 y ahora veo advertencias explícitas contra el uso
coroutineScope.launch { ... }
dentro de una función componible: “Las llamadas al lanzamiento deben ocurrir dentro de un LaunchedEffect y no de una composición”. (El nombre de pelusa es “CoroutineCreationDuringComposition”). Entonces, por lo que vale, esto da una directiva ‘oficial’ de que mi forma anterior de usar un coroutineScope para todo no era la forma preferida.– machfour
27 de marzo de 2021 a las 11:20
-
RememberCoroutineScope es lanzar una corrutina fuera de un componible dentro de un ámbito consciente de la composición. Para componibles internos, las corrutinas deben lanzarse con LaunchedEffect
– Desarrollador disidente
28 de diciembre de 2021 a las 7:31
Utilice recordarCoroutineScope() cuando está usando rutinas y necesita cancelar y reiniciar la rutina después de un evento
Usar Efecto Lanzado() cuando está utilizando rutinas y necesita cancelar y reiniciar la rutina cada vez que cambia su parámetro y no se almacena en un estado mutable.
LaunchedEffect: ejecuta funciones de suspensión en el ámbito de un componible
Para llamar a las funciones de suspensión de forma segura desde dentro de un componible, usa el componible LaunchedEffect. Cuando LaunchedEffect ingresa a la Composición, inicia una rutina con el bloque de código pasado como parámetro. La rutina se cancelará si LaunchedEffect abandona la composición.
RememberCoroutineScope: obtén un alcance consciente de la composición para lanzar una corrutina fuera de un componible
Dado que LaunchedEffect es una función componible, solo se puede usar dentro de otras funciones componibles. Para lanzar una corrutina fuera de un componible, pero con alcance para que se cancele automáticamente una vez que abandone la composición, use RememberCoroutineScope
Mas de aquí
rememberCoroutineScope
es una función componible que devuelve un CoroutineScope vinculado al punto de la Composición donde se llama. El alcance se cancelará cuando la convocatoria abandone la Composición. Si crea su propio coroutineScope en lugar de recordar, es posible que obtenga MonotonicFrameClock is not available in this CoroutineContext
error como en cuestión aquí.
LaunchedEffect
es un recuerdo bajo el capó con coroutineScope que se ejecuta cuando ingresa a la composición y cuando cambia cualquiera de sus claves.
@Composable
@NonRestartableComposable
@OptIn(InternalComposeApi::class)
fun LaunchedEffect(
key1: Any?,
block: suspend CoroutineScope.() -> Unit
) {
val applyContext = currentComposer.applyCoroutineContext
remember(key1) { LaunchedEffectImpl(applyContext, block) }
}
LaunchedEffect es bueno para lanzar animaciones, snackbar, buscar cualquier función de suspensión predeterminada. Compose también tiene otros usos muy útiles: desencadenar algunos eventos sin la interacción del usuario.
por ejemplo, ejecutar una devolución de llamada solo cuando se alcanza un cierto estado sin interacciones del usuario como en esta pregunta
LaunchedEffect(statementsByYear != null) {
if (statementsByYear != null) {
onDataAcquired(statementsByYear!!)
}
}
LaunchedEffect se activa en la composición, pero como en cuestión, ya que el valor es nulo si no se cumple la condición de bloque, entonces cuando statementsByYear
está establecido por api statementsByYear != null
cambia de verdadero a falso y si se cumple la condición de bloque y ejecuta la instrucción.
O solo una vez dentro de Composable usando la ayuda de ViewModel guardando una bandera como en esta pregunta.
El bloque de LaunchedEffect(keys) se invoca en la composición y cuando cambia cualquier tecla. Si configura claves desde su ViewModel, se iniciará este LaunchedEffect y podrá crear un bloque condicional que verifique que el mismo indicador sea verdadero que está contenido en ViewModel.
LaunchedEffect(mViewModel.isLaunched) {
if(!mViewModel.isLaunched) {
mViewMode.iniBilling(context as Activity)
mViewMode.isLaunched = true
}
}
También los bloques condicionales ingresan a la composición cuando se cumplen las condiciones, por lo que al envolver LaunchedEffect con el bloque if puede definir cuándo ingresará y saldrá de la composición, lo que significa que cancelar el trabajo, su coroutineScope podría estar ejecutándose como en esta respuesta
if (count > 0 && count <5) {
// `LaunchedEffect` will cancel and re-launch if
// `scaffoldState.snackbarHostState` changes
LaunchedEffect(scaffoldState.snackbarHostState) {
// Show snackbar using a coroutine, when the coroutine is cancelled the
// snackbar will automatically dismiss. This coroutine will cancel whenever
// if statement is false, and only start when statement is true
// (due to the above if-check), or if `scaffoldState.snackbarHostState` changes.
scaffoldState.snackbarHostState.showSnackbar("count $count")
}
}
Este bloque entrará en composición cuando contar es mayor que 0 y permanece en la composición mientras que el recuento es inferior a 5, pero dado que es LaunchedEffect, se activará una vez, pero si el recuento llega a 5 más rápido que la duración de Snackbar, Snackbar se cancela porque el bloque abandona la composición.
Sé que el número 1 se discutió recientemente en Kotlinlang Slack, pero la búsqueda de Slack es patética, así que no puedo encontrarlo. Para el n. ° 2, el problema no es “vale la pena el esfuerzo”, sino cuál es la respuesta correcta para la rutina. Por ejemplo, escribes
AboutScreen()
y tener allí unText()
que muestra la cantidad de segundos desde que se creó la aplicación, y usa una rutina para actualizar el estado para esoText()
. El usuario visita la pantalla acerca de, luego sale y usa otras partes de su aplicación durante una hora.– CommonsWare
4 de marzo de 2021 a las 12:33
¿Debería tu rutina de cada segundo ejecutarse durante toda esa hora, aunque la pantalla Acerca de no esté en tu composición? Si la respuesta es “diablos, no”, entonces necesita un
CoroutineScope
que está en el ámbito del componible relevante, y eso seríarememberCoroutineScope()
en ese componible. Si por alguna razón tu hacer necesita esa corrutina para seguir funcionando, necesita un alcance que coincida con la vida útil deseada. Esto no es diferente a cualquier otra decisión sobre el alcance de la corrutina: no se trata de lo que es fácil, sino de cuál es la vida útil adecuada para que se ejecute la corrutina.– CommonsWare
4 de marzo de 2021 a las 12:35
Gracias, intentaré mirar Kotlinlang Slack. Para el n.° 2, todas las pantallas/fragmentos de mi aplicación tienen árboles de composición separados, por lo que la vida útil es, como máximo, el tiempo que un usuario pasa en una pantalla. Pero estaba pensando más en cosas de corta duración, como mostrar snacks y ejecutar animaciones. ¿Debería reutilizar el mismo ámbito de rutina para muchas cosas diferentes de corta duración o crear uno nuevo cada vez? Parece que lo que estás diciendo es que debería hacer que los ámbitos no sean más grandes de lo necesario, lo que se inclina hacia la creación/recordación de los ámbitos de rutina por separado en cada lugar en el que se usan.
– machfour
5 de marzo de 2021 a las 1:22
“Debería hacer que los visores no sean más grandes de lo necesario”, dentro de lo razonable, sí. Volviendo a su pregunta, “¿Vale la pena el esfuerzo de solo crear/recordar un ámbito de rutina una vez por árbol de composición”, la respuesta es “no”. Los visores de rutina son baratos, y recordar cosas es barato, por lo que no hay necesidad de restringir artificialmente su uso. En su lugar, puede centrarse en lo que la lógica empresarial dice que debería ser su vida útil.
– CommonsWare
5 de marzo de 2021 a las 12:29
proandroiddev.com/… – Publicación de blog sobre LaunchedEffect y RememberCoroutineScope
– Udit
9 sep 2021 a las 9:01