Etapas del ciclo de vida de la actividad

1. Antes de comenzar

En este codelab, aprenderás sobre una parte fundamental de Android: el ciclo de vida de la actividad.

Desde el principio, una actividad pasa por varios estados y, a veces, regresa a ellos. Esta transición de estados se conoce como ciclo de vida de la actividad.

En Android, una actividad es el punto de entrada para interactuar con el usuario.

En el pasado, una actividad mostraba una pantalla en una app. Con las prácticas recomendadas actuales, una actividad puede mostrar varias pantallas, intercambiándolas según sea necesario.

El ciclo de vida de la actividad se extiende desde la creación de la actividad hasta su destrucción, cuando el sistema recupera los recursos de esa actividad. A medida que un usuario navega dentro de una actividad y fuera de ella, cada actividad pasa de un estado a otro en su ciclo de vida.

Como desarrollador de Android, debes comprender el ciclo de vida de la actividad. Si las actividades no responden correctamente a los cambios de estado del ciclo de vida, es posible que tu app genere errores extraños o comportamientos confusos para los usuarios, o que use demasiados recursos del sistema Android. Comprender el ciclo de vida de Android y responder correctamente a los cambios de estado del ciclo de vida es una parte importante del desarrollo de Android.

Requisitos previos

  • Saber qué es una actividad y cómo crear una en tu app
  • Conocer lo que hace el método onCreate() de una actividad y el tipo de operaciones que se realizan en ese método

Qué aprenderás

  • La forma de imprimir información de registro en Logcat
  • Los conceptos básicos del ciclo de vida de la Activity y las devoluciones de llamada que se invocan cuando la actividad pasa de un estado a otro
  • La manera de anular métodos de devolución de llamada de ciclo de vida para realizar operaciones en diferentes momentos del ciclo de vida de la actividad

Qué compilarás

  • Modifica una app de partida llamada Dessert Clicker para agregar información de registro que se muestra en Logcat.
  • Anula los métodos de devolución de llamada de ciclo de vida y registra los cambios de estado en el estado de la actividad.
  • Ejecuta la app y observa la información de registro que aparece cuando se inicia, se detiene y se reanuda la actividad.
  • Implementa rememberSaveable para conservar los datos de la app que podrían perderse si cambia la configuración del dispositivo.

2. Descripción general de la app

En este codelab, trabajarás con una app de partida llamada Dessert Clicker. En esta app, cada vez que el usuario presiona un postre en la pantalla, la app "compra" el postre para el usuario. La app actualiza los valores en el diseño para los siguientes aspectos:

  • Cantidad de postres "comprados"
  • Los ingresos totales relativos a los postres "comprados"

245d0bdfc09f4d54.png

Esta app contiene varios errores relacionados con el ciclo de vida de Android. Por ejemplo, en determinadas circunstancias, la app restablece los valores de los postres a 0. Comprender el ciclo de vida de Android te ayudará a entender por qué ocurren estos problemas y cómo corregirlos.

Descarga el código de inicio

En Android Studio, abre la carpeta basic-android-kotlin-compose-training-dessert-clicker.

3. Explora los métodos del ciclo de vida y agrega registros básicos

Cada actividad tiene lo que se conoce como un ciclo de vida. Este término es una alusión a los ciclos de vida de las plantas y los animales, como el ciclo de vida de una mariposa. Los diferentes estados de la mariposa muestran su crecimiento a lo largo de las fases de huevo, oruga, crisálida y mariposa hasta la muerte.

Ciclo de vida de las mariposas: Crecimiento a lo largo de las fases de huevo, oruga, crisálida y mariposa hasta la muerte.

De manera similar, el ciclo de vida de la actividad consiste en los diferentes estados por los que puede pasar una actividad, desde que esta se inicializa hasta su destrucción, momento en el cual el sistema operativo (SO) recupera su memoria. Por lo general, el punto de entrada de un programa es el método main(). Sin embargo, las actividades de Android comienzan con el método onCreate(); este método sería equivalente a la fase del huevo en el ejemplo anterior. Ya usaste actividades muchas veces durante este curso, y es posible que reconozcas el método onCreate(). Cuando el usuario inicia tu app y navega dentro y fuera de ella, y entre las actividades, la actividad cambia de estado.

En el siguiente diagrama, se muestran todos los estados del ciclo de vida de la actividad. Como los nombres lo indican, estos estados representan el estado de la actividad. Ten en cuenta que, a diferencia del ciclo de vida de las mariposas, una actividad puede ir y volver entre estados durante todo el ciclo de vida, en lugar de moverse solo en una dirección.

ca808edb1c95f07a.png

A menudo, quieres cambiar algún comportamiento o ejecutar algún código cuando cambia el estado del ciclo de vida de la actividad. Por lo tanto, la clase Activity en sí misma y cualquier subclase de Activity, como ComponentActivity, implementan un conjunto de métodos de devolución de llamada de ciclo de vida. Android invoca estas devoluciones de llamada cuando la actividad pasa de un estado a otro, y puedes anular esos métodos en tus propias actividades a fin de realizar tareas en respuesta a esos cambios de estado del ciclo de vida. En el siguiente diagrama, se muestran los estados del ciclo de vida junto con las devoluciones de llamada anulables disponibles.

El esquema del ciclo de vida de la actividad

Es importante saber cuándo Android invoca las devoluciones de llamada anulables y qué hacer en cada método de devolución de llamada. Sin embargo, ambos diagramas son complejos y pueden ser confusos. En este codelab, en lugar de solo leer lo que significa cada estado y devolución de llamada, investigarás y desarrollarás tus ideas sobre el ciclo de vida de la actividad de Android.

Paso 1: Observa el método onCreate() y agrega registros

Para determinar lo que sucede con el ciclo de vida de Android, es útil saber cuándo se llama a los diferentes métodos de ciclo de vida. Esta información te ayuda a identificar los problemas en la app de Dessert Clicker.

Una forma sencilla de determinar esta información es usar la funcionalidad de registro de Android. El registro te permite escribir mensajes cortos en una consola mientras se ejecuta la app, y luego usarlo para detectar cuándo se activan diferentes devoluciones de llamada.

  1. Ejecuta la app de Dessert Clicker y presiona varias veces la imagen del postre. Observa cómo cambia el valor de los postres vendidos y el importe total en dólares.
  2. Abre MainActivity.kt y examina el método onCreate() para esta actividad:
override fun onCreate(savedInstanceState: Bundle?) {
    // ...
}

En el diagrama del ciclo de vida de la actividad, es posible que reconozcas el método onCreate(), dado que ya usaste esta devolución de llamada con anterioridad. Este es el único método que deben implementar todas las actividades. El método onCreate() es aquel en el cual debes realizar las inicializaciones únicas para tu actividad. Por ejemplo, en onCreate(), debes llamar a setContent(), que especifica el diseño de la IU de la actividad.

El método de ciclo de vida de onCreate

Se llama al método de ciclo de vida de onCreate() una vez, justo después de que se inicializa la actividad, cuando el SO crea el nuevo objeto Activity en la memoria. Después de que se ejecuta onCreate(), la actividad se considera creada.

  1. Agrega la siguiente constante en el nivel superior de MainActivity.kt, sobre la declaración de clase class MainActivity.

Una buena convención es declarar una constante TAG en tu archivo, ya que su valor no cambiará.

Para marcarla como una constante de tiempo de compilación, usa const cuando declares la variable. Una constante de tiempo de compilación es un valor que se conoce durante la compilación.

private const val TAG = "MainActivity"
  1. En el método onCreate(), justo después de la llamada a super.onCreate(), agrega la siguiente línea:
Log.d(TAG, "onCreate Called")
  1. Si es necesario, importa la clase Log (presiona Alt+Enter, o Option+Enter en una Mac, y selecciona Import). Si habilitaste las importaciones automáticas, esto debe ocurrir automáticamente.
import android.util.Log

La clase Log escribe mensajes en Logcat. Logcat es la consola para registrar mensajes. Aquí aparecen los mensajes de Android sobre tu app, incluidos los que envías de manera explícita al registro con el método Log.d() u otros métodos de clase Log.

Hay tres aspectos importantes de la instrucción Log:

  • La prioridad del mensaje de registro, es decir, cuán importante es el mensaje. En este caso, Log.v() registra mensajes detallados. El método Log.d() escribe un mensaje de depuración. Otros métodos de la clase Log incluyen Log.i() para mensajes informativos, Log.w() para advertencias y Log.e() para mensajes de error.
  • La tag del registro (el primer parámetro), en este caso, "MainActivity". La etiqueta es una string que te permite encontrar con mayor facilidad los mensajes de registro en Logcat. La etiqueta suele ser el nombre de la clase.
  • El mensaje de registro real, llamado msg (el segundo parámetro), es una cadena corta que, este caso, es "onCreate Called".

a4ff4aa74384ff6.png

  1. Compila y ejecuta la app de Dessert Clicker. No verás ninguna diferencia de comportamiento en la app cuando presiones el postre. En Android Studio, en la parte inferior de la pantalla, haz clic en la pestaña Logcat.

ed03d4bb1f020995.png

  1. En la ventana Logcat, escribe tag:MainActivity en el campo de búsqueda.

961ea44c4b9ee3c.png

Logcat puede contener muchos mensajes, y la mayoría no te resultará útil. Puedes filtrar las entradas de Logcat de muchas maneras, pero es más sencillo realizar una búsqueda. Dado que usaste MainActivity como la etiqueta de registro en tu código, puedes usar esa etiqueta para filtrar el registro. El mensaje de registro incluye la fecha y hora, la etiqueta de registro, el nombre del paquete (com.example.dessertclicker) y el mensaje en sí. Debido a que este mensaje aparece en el registro, sabes que se ejecutó onCreate().

Paso 2: Implementa el método onStart()

Se llama al método de ciclo de vida de onStart() justo después de onCreate(). Una vez que se ejecute onStart(), tu actividad se visualizará en la pantalla. A diferencia de lo que ocurre con onCreate(), que se llama solo una vez para inicializar tu actividad, el sistema puede llamar a onStart() muchas veces durante el ciclo de vida de tu actividad.

a357d2291de472d9.png

Ten en cuenta que onStart() está vinculado con el método de ciclo de vida onStop() correspondiente. Si el usuario inicia tu app y luego regresa a la pantalla principal del dispositivo, la actividad se detendrá y ya no estará visible en la pantalla.

  1. En Android Studio, con MainActivity.kt abierto y el cursor dentro de la clase MainActivity, selecciona Code > Override Methods… o presiona Control+O. Aparecerá un diálogo con una larga lista de todos los métodos que puedes anular en esta clase.

11ff93bee1c3940f.png

  1. Comienza a ingresar onStart para buscar el método adecuado. Para desplazarte al siguiente elemento coincidente, usa la flecha hacia abajo. Selecciona onStart() de la lista y haz clic en OK para insertar el código estándar de anulación. El código es similar al siguiente ejemplo:
override fun onStart() {
    super.onStart()
}
  1. Dentro del método onStart(), agrega un mensaje de registro:
override fun onStart() {
    super.onStart()
    Log.d(TAG, "onStart Called")
}
  1. Compila y ejecuta la app de Dessert Clicker, y abre el panel de Logcat.
  2. Escribe tag:MainActivity en el campo de búsqueda a los efectos de filtrar el registro. Ten en cuenta que los métodos onCreate() y onStart() se llamaron uno tras otro y que tu actividad se visualiza en pantalla.
  3. Presiona el botón de inicio del dispositivo y luego usa la pantalla Recientes para volver a la actividad. Ten en cuenta que la actividad se reanudará donde la dejaste, con los mismos valores, y que onStart() se registrará por segunda vez en Logcat. Observa también que no se vuelve a llamar al método onCreate().
2024-02-20 10:30:00.231  5684-5684  MainActivity            com.example.dessertclicker           D  onCreate Called
2024-02-20 10:30:00.278  5684-5684  MainActivity            com.example.dessertclicker           D  onStart Called
2024-02-20 10:30:39.020  5684-5684  MainActivity            com.example.dessertclicker           D  onStart Called

Paso 3: Agrega más instrucciones de registro

En este paso, implementarás el registro de todos los demás métodos del ciclo de vida.

  1. Anula el resto de los métodos del ciclo de vida en tu MainActivity y agrega instrucciones de registro para cada uno, como se muestra en el siguiente código:
override fun onResume() {
    super.onResume()
    Log.d(TAG, "onResume Called")
}

override fun onRestart() {
    super.onRestart()
    Log.d(TAG, "onRestart Called")
}

override fun onPause() {
    super.onPause()
    Log.d(TAG, "onPause Called")
}

override fun onStop() {
    super.onStop()
    Log.d(TAG, "onStop Called")
}

override fun onDestroy() {
    super.onDestroy()
    Log.d(TAG, "onDestroy Called")
}
  1. Vuelve a compilar y ejecutar Dessert Clicker, y revisa Logcat.

Ten en cuenta que esta vez, además de onCreate() y onStart(), hay un mensaje de registro para la devolución de llamada de ciclo de vida onResume().

2024-02-20 10:33:36.033  5789-5789  MainActivity            com.example.dessertclicker           D  onCreate Called
2024-02-20 10:33:36.073  5789-5789  MainActivity            com.example.dessertclicker           D  onStart Called
2024-02-20 10:33:36.075  5789-5789  MainActivity            com.example.dessertclicker           D  onResume Called

Cuando una actividad se inicie desde el principio, verás estas tres devoluciones de llamada de ciclo de vida llamadas en orden:

  • onCreate() cuando el sistema crea la app.
  • onStart(), que hace que la app sea visible en la pantalla, aunque el usuario aún no puede interactuar con ella.
  • onResume(), que lleva la app al primer plano y permite que el usuario ahora interactúe con ella.

A pesar de su nombre, se llama al método onResume() durante el inicio, incluso si no hay nada para reanudar.

El esquema del ciclo de vida de la actividad

4. Explora casos de uso del ciclo de vida

Ahora que configuraste la app de Dessert Clicker en lo relativo al registro, está todo listo para comenzar a usar la app y explorar cómo se activan las devoluciones de llamada del ciclo de vida.

Caso de uso 1: Cómo abrir y cerrar la actividad

Comenzarás con el caso de uso más básico, que es iniciar tu app por primera vez y, luego, cerrarla.

  1. Compila y ejecuta la app de Dessert Clicker si aún no se está ejecutando. Como ya viste, se llama a las devoluciones de llamada onCreate(), onStart() y onResume() cuando la actividad se inicia por primera vez.
2024-02-20 10:33:36.033  5789-5789  MainActivity            com.example.dessertclicker           D  onCreate Called
2024-02-20 10:33:36.073  5789-5789  MainActivity            com.example.dessertclicker           D  onStart Called
2024-02-20 10:33:36.075  5789-5789  MainActivity            com.example.dessertclicker           D  onResume Called
  1. Presiona el pastelito algunas veces.
  2. Presiona el botón Back en el dispositivo.

En Logcat, observa que se llama a onPause() y onStop() en ese orden.

2024-02-20 10:34:41.974  5789-5789  MainActivity            com.example.dessertclicker           D  onPause Called
2024-02-20 10:34:42.411  5789-5789  MainActivity            com.example.dessertclicker           D  onStop Called

En este caso, si usas el botón Back, se quitará la actividad (y la app) de la pantalla y se moverá a la parte posterior de la pila de actividades.

El SO Android podría cerrar tu actividad si el código llama manualmente al método finish() de la actividad o si el usuario fuerza el cierre de la app. Por ejemplo, el usuario puede forzar el cierre o cerrar la app en la pantalla Recientes. Es posible que el SO también cierre tu actividad por sí mismo si tu app no está en la pantalla por mucho tiempo. Android lo hace para conservar la duración de la batería y recuperar los recursos que la app estaba usando, de modo que estén disponibles para otras apps. Estos son solo algunos ejemplos de por qué el sistema Android destruye tu actividad. Existen casos adicionales en los que el sistema Android destruye tu actividad sin proporcionar una advertencia.

Caso de uso 2: Cómo salir de la actividad y volver a ella

Ahora que iniciaste la app y la cerraste, viste la mayoría de los estados del ciclo de vida para cuando la actividad se crea por primera vez. También conoces la mayoría de los estados del ciclo de vida que la actividad atraviesa cuando se cierra. Sin embargo, a medida que los usuarios interactúan con sus dispositivos Android, pasan de una app a otra, regresan al inicio, inician apps nuevas y manejan interrupciones provocadas por otras actividades (por ejemplo, llamadas telefónicas).

Tu actividad no se cierra por completo cada vez que el usuario sale de ella:

  • Cuando tu actividad ya no está visible en la pantalla, a ese estado se lo conoce como el de colocar la actividad en segundo plano. Lo contrario de esto ocurre cuando la actividad está en primer plano o en pantalla.
  • Cuando el usuario regresa a tu app, se reinicia la misma actividad y la app vuelve a estar visible. Esta parte del ciclo de vida se denomina ciclo de vida visible de la app.

Cuando la app está en segundo plano, en general, no debe estar activa; de esta forma se conservan los recursos del sistema y la duración de la batería. Puedes usar el ciclo de vida de Activity y sus devoluciones de llamada para saber en qué momento tu app pasa a segundo plano de modo que puedas pausar las operaciones en curso. Luego, reinicia las operaciones cuando tu app pase a primer plano.

En este paso, observas el ciclo de vida de la actividad cuando la app pasa a segundo plano y vuelve a primer plano.

  1. Con la app de Dessert Clicker en ejecución, haz clic varias veces en la magdalena.
  2. Presiona el botón Home en el dispositivo y observa el Logcat en Android Studio. Cuando regresas a la pantalla principal, tu app se ejecuta en segundo plano en lugar de cerrarse por completo. Observa que se llama a los métodos onPause() y onStop().
2024-02-20 10:35:26.832  5789-5789  MainActivity            com.example.dessertclicker           D  onPause Called
2024-02-20 10:35:27.233  5789-5789  MainActivity            com.example.dessertclicker           D  onStop Called

Cuando se llama a onPause(), la app ya no tiene foco. Después de onStop(), la app deja de estar visible en la pantalla. Aunque la actividad se detiene, el objeto Activity sigue en la memoria en segundo plano. El SO Android no destruyó la actividad. El usuario podría regresar a la app, por lo que Android conserva los recursos de la actividad.

c470ee28ab7f8a1a.png

  1. Usa la pantalla Recientes para volver a la app. En el emulador, se puede acceder a esta pantalla desde el botón cuadrado del sistema que se muestra en la siguiente imagen.

Observa en Logcat que la actividad se reinicia con onRestart() y onStart() y, luego, se reanuda con onResume().

bc156252d977e5ae.png

2024-02-20 10:36:05.837  5789-5789  MainActivity            com.example.dessertclicker           D  onRestart Called
2024-02-20 10:36:05.839  5789-5789  MainActivity            com.example.dessertclicker           D  onStart Called
2024-02-20 10:36:05.842  5789-5789  MainActivity            com.example.dessertclicker           D  onResume Called

Cuando la actividad regresa a primer plano, no se vuelve a llamar al método onCreate(). No se destruyó el objeto de la actividad, por lo que no es necesario volver a crearlo. En lugar de onCreate(), se llama al método onRestart(). Ten en cuenta que esta vez, cuando la actividad regrese a primer plano, se conservará la cantidad de postres vendidos.

  1. Inicia por lo menos una app que no sea Dessert Clicker de modo que el dispositivo tenga algunas apps en su pantalla Recientes.
  2. Abre la pantalla Recientes y abre otra actividad reciente. Luego, regresa a las apps recientes y vuelve a poner Dessert Clicker en primer plano.

Observa que ves las mismas devoluciones de llamada en Logcat aquí que cuando presionaste el botón de inicio. Se llama a onPause() y onStop() cuando la app pasa a segundo plano, y luego a onRestart(), onStart() y onResume() cuando regresa.

Se llama a estos métodos cuando la app se detiene y pasa a segundo plano, o bien cuando la app se reinicia y regresa al primer plano. Si necesitas realizar algunas tareas en tu app durante estos casos, anula el método de devolución de llamada de ciclo de vida correspondiente.

Caso de uso 3: Cómo ocultar parcialmente la actividad

Ya vimos que, cuando se inicia una app y se llama a onStart(), la app se hace visible en la pantalla. Cuando se llama a onResume(), la app atrae el foco del usuario, es decir, el usuario puede interactuar con ella. La parte del ciclo de vida en la que la app se muestra en pantalla y tiene la atención del usuario se denomina ciclo de vida en primer plano.

Cuando la app pasa a segundo plano, el foco de atención se pierde después de onPause(), y la app deja de ser visible después de onStop().

La diferencia entre enfoque y visibilidad es importante. Una actividad puede ser parcialmente visible en la pantalla, pero no tener la atención del usuario. En este paso, verás un caso de este tipo.

  1. Con la app de Dessert Clicker en ejecución, haz clic en el botón Share, en la parte superior derecha de la pantalla.

La actividad de la función compartir aparece en la mitad inferior de la pantalla, pero la actividad aún está visible en la mitad superior.

677c190d94e57447.pngca6285cbbe3801cf.png

  1. Revisa Logcat y observa que solo se llamó a onPause().
2024-02-20 10:36:42.886  5789-5789  MainActivity            com.example.dessertclicker           D  onPause Called

En este caso de uso, no se llama a onStop() porque la actividad aún está parcialmente visible. Sin embargo, no tiene la atención del usuario, y este no puede interactuar con ella (la actividad "compartir" que está en primer plano tiene el foco de atención del usuario).

¿Por qué es importante esta diferencia? Por lo general, la interrupción con solo onPause() dura un tiempo breve antes de volver a tu actividad o navegar a otra actividad o app. Por lo general, querrás seguir actualizando la IU de modo que el resto de tu app no parezca bloqueado.

Cualquier código que se ejecute en onPause() bloquea otras cosas, por lo que debes mantener un código liviano en onPause(). Por ejemplo, si llegara una llamada telefónica, el código de onPause() podría retrasar la notificación de llamada entrante.

  1. Haz clic fuera del cuadro de diálogo de la función compartir para volver a la app y observa que se llama a onResume().

onResume() y onPause() están relacionados con el foco. Se llama al método onResume() cuando la actividad tiene el foco, y se llama a onPause() cuando esta lo pierde.

5. Explora los cambios de configuración

Hay otro caso en la administración del ciclo de vida de la actividad que es importante comprender: se trata de cómo los cambios en la configuración afectan el ciclo de vida de tus actividades.

Un cambio de configuración ocurre cuando cambia el estado del dispositivo de manera tan radical que la forma más simple de que el sistema resuelva el cambio es cerrar y volver a crear la actividad por completo. Por ejemplo, si el usuario cambia el idioma del dispositivo, es posible que todo el diseño deba cambiar para adaptarse a diferentes orientaciones y longitudes de texto. Si el usuario enchufa el dispositivo a un conector o agrega un teclado físico, es posible que el diseño de la app deba aprovechar otro diseño o tamaño de pantalla. Además, si cambia la orientación del dispositivo (si el dispositivo rota del modo vertical al horizontal, o viceversa), es posible que el diseño deba modificarse para que se ajuste a la nueva orientación. Veamos cómo se comporta la app en este caso.

La última devolución de llamada del ciclo de vida que se muestra es onDestroy(), a la que se llama después de onStop(). Se llama justo antes de que finalice la actividad. Esto puede suceder cuando el código de la app llama a finish() o cuando el sistema necesita destruir y volver a crear la actividad debido a un cambio de configuración.

El cambio de configuración hace que se llame a onDestroy()

La rotación de pantalla es un tipo de cambio de configuración que provoca que la actividad se cierre y se reinicie. Para simular este cambio de configuración y examinar sus efectos, completa los siguientes pasos:

  1. Compila y ejecuta tu app.
  2. Asegúrate de que esté inhabilitado el bloqueo de rotación de pantalla del emulador.
  3. Rota el dispositivo o el emulador al modo horizontal. Puedes rotar el emulador hacia la izquierda o la derecha con los botones de rotación.
  4. Examina Logcat y comprende que, a medida que se cierra la actividad, esta llama a onPause(), onStop() y onDestroy(), en ese orden.
2024-02-20 10:37:57.078  5987-5987  MainActivity            com.example.dessertclicker           D  onPause Called
2024-02-20 10:37:57.087  5987-5987  MainActivity            com.example.dessertclicker           D  onStop Called
2024-02-20 10:37:57.102  5987-5987  MainActivity            com.example.dessertclicker           D  onDestroy Called

Pérdida de datos tras la rotación del dispositivo

  1. Compila y ejecuta tu app, y abre Logcat.
  2. Haz clic varias veces en la magdalena y observa que los postres vendidos y los ingresos totales no son cero.
  3. Asegúrate de que esté inhabilitado el bloqueo de rotación de pantalla del emulador.
  4. Rota el dispositivo o el emulador al modo horizontal. Puedes rotar el emulador hacia la izquierda o la derecha con los botones de rotación.

f745d0e2697415fd.png

  1. Revisa el resultado en Logcat. Filtra el resultado en MainActivity.
2024-02-20 10:39:22.724  6087-6087  MainActivity            com.example.dessertclicker           D  onCreate Called
2024-02-20 10:39:22.752  6087-6087  MainActivity            com.example.dessertclicker           D  onStart Called
2024-02-20 10:39:22.753  6087-6087  MainActivity            com.example.dessertclicker           D  onResume Called
2024-02-20 10:39:40.508  6087-6087  MainActivity            com.example.dessertclicker           D  onPause Called
2024-02-20 10:39:40.540  6087-6087  MainActivity            com.example.dessertclicker           D  onStop Called
2024-02-20 10:39:40.549  6087-6087  MainActivity            com.example.dessertclicker           D  onDestroy Called
2024-02-20 10:39:40.582  6087-6087  MainActivity            com.example.dessertclicker           D  onCreate Called
2024-02-20 10:39:40.584  6087-6087  MainActivity            com.example.dessertclicker           D  onStart Called
2024-02-20 10:39:40.585  6087-6087  MainActivity            com.example.dessertclicker           D  onResume Called

Ten en cuenta que, cuando el dispositivo o el emulador rotan la pantalla, el sistema llama a todas las devoluciones de llamada de ciclo de vida para cerrar la actividad. Luego, mientras se vuelve a crear la actividad, el sistema llama a todas las devoluciones de llamada de ciclo de vida para iniciarla.

Cuando se rota el dispositivo y se cierra y se vuelve a crear la actividad, esta se reinicia con los valores predeterminados: la imagen del postre, la cantidad de postres vendidos y los ingresos totales restablecidos a cero.

Para saber por qué se restablecen estos valores y cómo corregirlos, debes obtener información sobre el ciclo de vida de un elemento que admite composición y cómo este observa y retiene su estado.

Ciclo de vida de un elemento componible

La IU de tu app se compila inicialmente a partir de funciones de componibilidad en un proceso llamado Composición.

Cuando cambia el estado de tu app, se programa una recomposición. La recomposición ocurre cuando Compose vuelve a ejecutar las funciones de componibilidad cuyo estado podría haber cambiado y crea una IU actualizada. La composición se actualiza para reflejar estos cambios.

La única forma de crear o actualizar una composición es mediante su composición inicial y las recomposiciones posteriores.

Las funciones de componibilidad tienen su propio ciclo de vida, que es independiente del ciclo de vida de la actividad. Su ciclo de vida está compuesto por los siguientes eventos: ingresar a la composición, volver a componer 0 o más veces y, luego, salir de la composición.

Para que Compose realice el seguimiento y active una recomposición, debe saber cuándo cambió el estado. A fin de indicarle a Compose que debe seguir el estado de un objeto, este debe ser del tipo State o MutableState. El tipo State es inmutable y solo se puede leer. Un tipo MutableState es mutable y permite lecturas y escrituras.

Ya viste y usaste MutableState en la app de Lemonade y la app de Tip Time en codelabs anteriores.

Para crear la variable mutable revenue, debes declararla con mutableStateOf. 0 es su valor predeterminado inicial.

var revenue = mutableStateOf(0)

Si bien esto es suficiente para que Compose active una recomposición cuando cambie el valor de los ingresos, no es suficiente a los efectos de mantener su valor actualizado. Cada vez que se vuelva a ejecutar el elemento que admite composición, se reiniciará el valor 0 inicial de los ingresos.

A fin de indicarle a Compose que conserve y vuelva a usar su valor durante las recomposiciones, debes declararlo con la API de remember.

var revenue by remember { mutableStateOf(0) }

Si cambia el valor de revenue, Compose programará para la recomposición todas las funciones de componibilidad que leen este valor.

Si bien Compose recuerda el estado de los ingresos durante las recomposiciones, no retiene este estado durante un cambio de configuración. Para que lo haga, debes usar rememberSaveable.

Para obtener información y prácticas adicionales, consulta el codelab Introducción al estado en Compose.

Usa rememberSaveable para guardar valores entre los cambios de configuración

Usa la función rememberSaveable para guardar los valores que necesitarás si el SO Android destruye y vuelve a crear la actividad.

Para guardar valores durante las recomposiciones, debes usar remember. Usa rememberSaveable a los efectos de guardar valores durante las recomposiciones Y los cambios de configuración.

Guardar el valor con rememberSaveable garantiza que esté disponible cuando se restablezca la actividad, si es necesario.

  1. En MainActivity, actualiza el grupo de cinco variables que actualmente usan remember a rememberSaveable.
var revenue by remember { mutableStateOf(0) }
...
var currentDessertImageId by remember {
    mutableStateOf(desserts[currentDessertIndex].imageId)
}
var revenue by rememberSaveable { mutableStateOf(0) }
...
var currentDessertImageId by rememberSaveable {
    mutableStateOf(desserts[currentDessertIndex].imageId)
}
  1. Compila y ejecuta tu app.
  2. Haz clic varias veces en la magdalena y observa que los postres vendidos y los ingresos totales no son cero.
  3. Rota el dispositivo o el emulador al modo horizontal.
  4. Observa que, una vez que la actividad se destruye y se vuelve a crear, la imagen del postre, los postres vendidos y los ingresos totales se restablecen a sus valores anteriores.

6. Código de solución

7. Resumen

Ciclo de vida de la actividad

  • El ciclo de vida de la actividad es un conjunto de estados a través de los cuales transiciona una actividad. El ciclo de vida de la actividad comienza cuando el SO Android crea la actividad por primera vez y finaliza cuando el SO la destruye.
  • A medida que el usuario navega entre las actividades de tu app (y también cuando ingresa a la app y sale de ella), cada actividad cambia entre diferentes estados del ciclo de vida de la actividad.
  • Cada estado del ciclo de vida de la actividad tiene un método de devolución de llamada correspondiente que puedes anular en tu clase Activity. El conjunto principal de métodos de ciclo de vida consta de los siguientes: onCreate(), onRestart(), onStart(), onResume(), onPause(), onStop(), onDestroy().
  • Para agregar un comportamiento que ocurra cuando tu actividad pase a un estado del ciclo de vida, anula el método de devolución de llamada del estado.
  • Para agregar el esqueleto de métodos de anulación a tus clases en Android Studio, selecciona Code > Override Methods… o presiona Control+O.

Realiza un registro con Log

  • La API de Logging de Android y, en particular, la clase Log te permiten escribir mensajes cortos que se muestran en Logcat dentro de Android Studio.
  • Usa Log.d() para escribir un mensaje de depuración. Este método usa dos argumentos: la etiqueta de registro (en general, el nombre de la clase) y el mensaje de registro (una string corta).
  • Usa la ventana Logcat de Android Studio a fin de ver los registros del sistema, incluidos los mensajes que escribas.

Cambios de configuración

  • Un cambio de configuración ocurre cuando cambia el estado del dispositivo de manera tan radical que la forma más simple de que el sistema resuelva el cambio es cerrar y volver a crear la actividad.
  • El ejemplo más común de un cambio de configuración se da cuando el usuario rota el dispositivo del modo vertical al modo horizontal, o viceversa. Un cambio de configuración también puede ocurrir cuando cambia el idioma del dispositivo o cuando un usuario conecta un teclado físico.
  • Cuando se produce un cambio de configuración, Android invoca todas las devoluciones de llamada de cierre del ciclo de vida de la actividad. Luego, Android reinicia la actividad desde cero y ejecuta todas las devoluciones de llamada de inicio del ciclo de vida.
  • Cuando Android cierra una app debido a un cambio de configuración, reinicia la actividad con onCreate().
  • Para guardar un valor que necesita sobrevivir a un cambio de configuración, se deben declarar sus variables con rememberSaveable.

Más información