¿Qué recuerda realmente Jetpack Compose? ¿Cómo funciona bajo el capó?

7 minutos de lectura

Avatar de usuario de Thracian
tracio

Al consultar el tutorial básico de Codelab, hay un fragmento para aumentar el contador en el botón cuando se hace clic

@Composable
fun MyScreenContent(names: List<String> = listOf("Android", "there")) {
    val counterState = remember { mutableStateOf(0) }

    Column(modifier = Modifier.fillMaxHeight()) {
        Column(modifier = Modifier.weight(1f)) {
            for (name in names) {
                Greeting(name = name)
                Divider(color = Color.Black)
            }
        }
        Counter(
            count = counterState.value,
            updateCount = { newCount ->
                counterState.value = newCount
            }
        )
    }
}


@Composable
fun Counter(count: Int, updateCount: (Int) -> Unit) {
    Button(
        onClick = { updateCount(count + 1) },
        colors = ButtonConstants.defaultButtonColors(
            backgroundColor = if (count > 5) Color.Green else Color.White
        )
    ) {
        Text("I've been clicked $count times")
    }
}

Está claro que remember { mutableStateOf(0) } almacena el estado/valor. Mi pregunta es qué recuerda debajo del capó. Usando var count = remember { 0 } o mutableStateOf(0) sin recordar no aumenta el valor.

fun MyScreenContent(names: List<String> = listOf("Android", "there")) {
   
    var count = remember { 0 }

    Column(modifier = Modifier.fillMaxHeight()) {
        Column(modifier = Modifier.weight(1f)) {
            for (name in names) {
                Greeting(name = name)
                Divider(color = Color.Black)
            }
        }
        Counter(
            count = count,
            updateCount = { newCount ->
                count = newCount
            }
        )
    }
}

El fragmento anterior no actualiza el valor impreso en Textrecuerda solo trabajar con MutableState?

  • puedes leer mi artículo para saber qué hace y cómo lo hace medium.com/@thegoldycopythat/…

    – Abhinav Chauhan

    11 de junio de 2022 a las 12:27


  • @AbhinavChauhan lo leí. Es muy bueno. Puede agregar una respuesta basada en su artículo.

    – tracio

    13 de junio de 2022 a las 6:43

  • gracias, encontraré tiempo para agregar la respuesta, gracias por la sugerencia, también puede considerar seguir en el medio.

    – Abhinav Chauhan

    14 de junio de 2022 a las 9:47

  • Realmente útil, tanto la pregunta como la respuesta. Gracias.

    – Juana P.

    20 de septiembre de 2022 a las 15:19

avatar de usuario de madabrowski
madabrowski

remember – le permite recordar el estado de la invocación de recomposición anterior y solo esto. Entonces, si, por ejemplo, aleatoriza el color en la ejecución inicial. El color aleatorio se calculará solo una vez y luego se reutilizará cuando sea necesario volver a componer.

Y por lo tanto,

remember = almacena el valor en caso de que se llame a recompose.

Ahora, la segunda cosa importante es saber cuándo reCompose en realidad debería activarse y allí los estados mutables vienen a ayudar.

mutablestate = almacenar el valor y, en caso de que actualice el disparador de valor, recomponer para todos los elementos que usan estos datos.

  • De los materiales que leí sobre Compose, “recordar” fue muy confuso. Me dio la impresión de que para que un componible se recomponga, se debe recordar y usar una variable de estado en ese componible. Ahora entiendo que no es así. Siempre que el componible lea un estado mutable que declaro en algún lugar (dentro o fuera de cualquier código componible) y mientras el estado cambie, el componible se recompondrá. “recuerda es solo para algunas cosas simples que haces dentro de un componible como hacer clic en un botón e incrementar un número; de lo contrario, no es necesario en la mayoría de mis casos.

    – LXJ

    6 de abril de 2022 a las 7:17


Avatar de usuario de Thracian
tracio

Para aprender cómo funciona la composición y la recomposición, puede consultar Bajo el capó de Jetpack Compose artículo por Leland Richardson, que describe muy bien los trabajos internos, también video de youtube aquí. Y la mayor parte de esta respuesta usa el artículo como referencia y cita la mayor parte de él.

La implementación de Composer contiene una estructura de datos que está estrechamente relacionada con un Búfer de brecha. Esta estructura de datos se usa comúnmente en editores de texto.

Un búfer de brecha representa una colección con un índice o cursor actual. Se implementa en la memoria con una matriz plana. Esa matriz plana es más grande que la colección de datos que representa, y el espacio no utilizado se denomina brecha.

Básicamente, agregar espacio cerca de su tabla de ranuras de función Composable para poder actualizar la interfaz de usuario dinámicamente con altos costos ya que get, move, inserty delete — son operaciones de tiempo constante, excepto para mover el espacio. Mover la brecha es En) pero esto no sucede a menudo, por lo que debe cambiar toda la estructura de la interfaz de usuario; en promedio, las interfaces de usuario no cambian mucho la estructura.

@Composable
    fun Counter() {
     var count by remember { mutableStateOf(0) }
     Button(
       text="Count: $count",
       onPress={ count += 1 }
     )
    }

Cuando el compilador ve la anotación Composable, inserta parámetros adicionales y llama al cuerpo de la función. Primero, el compilador agrega una llamada al composer.start y le pasa un entero clave generado en tiempo de compilación.

fun Counter($composer: Composer) {
 $composer.start(123)
 var count by remember($composer) { mutableStateOf(0) }
 Button(
   $composer,
   text="Count: $count",
   onPress={ count += 1 },
 )
 $composer.end()
}

Cuando este compositor se ejecuta, hace lo siguiente:

  1. Composer.start recibe una llamada y almacena un objeto de grupo
  2. recuerda inserta un objeto de grupo
  3. el valor que devuelve mutableStateOf, la instancia de estado, se almacena.
  4. El botón almacena un grupo, seguido de cada uno de sus parámetros.
  5. Y finalmente llegamos a composer.end.

ingrese la descripción de la imagen aquí

La estructura de datos ahora contiene todos los objetos de la composición, el árbol completo en orden de ejecución, efectivamente un profundidad primer recorrido del árbol

Entonces remember necesario para almacenar un mutableState() obtener valor de la composición anterior y mutableState() es necesario para activar uno.

Y MutableState usos de la interfaz @Stable anotación

@Stable
interface MutableState<T> : State<T> {
    override var value: T
    operator fun component1(): T
    operator fun component2(): (T) -> Unit
}

Stable se usa para comunicar algunas garantías al compilador de composición sobre cómo se comportará un determinado tipo o función.

Cuando se aplica a una clase o una interfaz, Stable indica que lo siguiente debe ser cierto:

  1. El resultado de equals siempre devolverá el mismo resultado para las mismas dos instancias.
  2. Cuando una propiedad pública del tipo cambie, se notificará la composición.
  3. Todos los tipos de propiedad pública son estables. Cuando se aplica a una función o una propiedad, la anotación Stable]indica que la función devolverá el mismo resultado si se pasan los mismos parámetros. Esto solo tiene sentido si los parámetros y los resultados son estables, inmutables o primitivos.

Las invariantes que implica esta anotación se utilizan para optimizaciones por parte del compilador de composición y tienen un comportamiento indefinido si no se cumplen las suposiciones anteriores. Como resultado, uno no debe usar esta anotación a menos que esté seguro de que se cumplen estas condiciones.

Otro fuente con un video que describe cómo funciona Compose.

  • Bien explicado! ¡Esta debería haber sido la respuesta aceptada!

    – sud007

    11 de mayo a las 18:47

Avatar de usuario de Thracian
tracio

ejemplo de laboratorio de código menciona sobre recordar y mutableState como

Reaccionar a los cambios de estado es el corazón de Compose. Las aplicaciones Compose transforman los datos en UI llamando a las funciones Composable. Si sus datos cambian, recupera estas funciones con los nuevos datos, creando una interfaz de usuario actualizada. Compose ofrece herramientas para observar los cambios en los datos de su aplicación, que recuperarán automáticamente sus funciones; esto se llama recomposición. Compose también analiza qué datos necesita un componible individual para que solo necesite recomponer los componentes cuyos datos han cambiado y puede omitir la composición de aquellos que no están afectados.

Debajo del capó, Compose usa un complemento de compilador de Kotlin personalizado, de modo que cuando los datos subyacentes cambian, las funciones componibles se pueden volver a invocar para actualizar la jerarquía de la interfaz de usuario.

Para agregar un estado interno a un componible, use la función mutableStateOf, que proporciona una memoria mutable componible. Para no tener un estado diferente para cada recomposición, recuerde el estado mutable usando recordar. Y, si hay varias instancias del componible en diferentes lugares de la pantalla, cada copia obtendrá su propia versión del estado. Puede pensar en el estado interno como una variable privada en una clase.

remember{X} y remember{mutableStateOf(X)} son útiles en diferentes escenarios.

El primero es necesario cuando no es necesario crear una instancia de su objeto en cada recomposición, y hay otro activador que activa la composición.

Un ejemplo de esto es remember{Paint()}, cualquier objeto que no necesite instanciarse más de una vez o mucha memoria para instanciar. Si se recompone un Composable que posee este objeto, las propiedades de su objeto no cambian gracias a remembersi no usas remember su objeto se instancia en cada recomposición y todas las propiedades establecidas previamente se restablecen.

Si necesitas un trigger(mutableStateOf) y necesita tener el valor más reciente (recuerde) como en cuestión, elija remember{mutableStateOf()}

Las variables se borran cada compositon.

Usando remember obtendrá el valor anterior.

Creo que es equivalente a declarar un estado mutable en ViewModel.

¿Ha sido útil esta solución?