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.

Durante su vida útil, una actividad pasa por varios estados y, a veces, regresa a ellos. Esta transición entre 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

  • Cómo imprimir información de registro en Logcat
  • Los conceptos básicos del ciclo de vida de Activity y las devoluciones de llamada que se invocan cuando la actividad pasa de un estado a otro
  • Cómo 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 DessertClicker 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 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 DessertClicker. 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 partida

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 quizás reconoces 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 la app se ejecuta, y puedes usarlo para que te muestre cuándo se activan las 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 Desserts sold 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 hayas reconocido 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 mensajes 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 etiqueta tag del registro (el primer parámetro), en este caso, "MainActivity". La etiqueta es una cadena 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), que es una cadena corta. En 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.

cedcce52592c6665.png

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

37080c4e00561b0.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 a fin de 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.

20c34cbad8dce892.png

  1. Comienza a ingresar onStart a fin de 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 Aceptar para insertar el código estándar de anulación. El código se verá como el 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 DessertClicker, y abre el panel de Logcat.
  2. Escribe tag:MainActivity en el campo de búsqueda para 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-04-26 14:54:48.721  5386-5386  MainActivity            com.example.dessertclicker           D  onCreate Called
2024-04-26 14:54:48.756  5386-5386  MainActivity            com.example.dessertclicker           D  onStart Called
2024-04-26 14:55:41.674  5386-5386  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-04-26 14:56:48.684  5484-5484  MainActivity            com.example.dessertclicker           D  onCreate Called
2024-04-26 14:56:48.709  5484-5484  MainActivity            com.example.dessertclicker           D  onStart Called
2024-04-26 14:56:48.713  5484-5484  MainActivity            com.example.dessertclicker           D  onResume Called

Cuando una actividad se inicie desde cero, verás las 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 para el registro, está todo listo para comenzar a usar la app y explorar cómo se activan las devoluciones de llamada de 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 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-04-26 14:56:48.684  5484-5484  MainActivity            com.example.dessertclicker           D  onCreate Called
2024-04-26 14:56:48.709  5484-5484  MainActivity            com.example.dessertclicker           D  onStart Called
2024-04-26 14:56:48.713  5484-5484  MainActivity            com.example.dessertclicker           D  onResume Called
  1. Presiona el pastelito varias veces.
  2. Presiona el botón Atrás en el dispositivo.

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

2024-04-26 14:58:19.984  5484-5484  MainActivity            com.example.dessertclicker           D  onPause Called
2024-04-26 14:58:20.491  5484-5484  MainActivity            com.example.dessertclicker           D  onStop Called
2024-04-26 14:58:20.517  5484-5484  MainActivity            com.example.dessertclicker           D  onDestroy Called

En este caso, si usas el botón Atrás, 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í solo si tu app no ha estado 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 la actividad:

  • Cuando tu actividad ya no está visible en la pantalla, se habla de que se colocó la actividad en segundo plano. (Lo contrario de esto es 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 a fin de conservar 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 Dessert Clicker en ejecución, haz clic varias veces en el pastelito.
  2. Presiona el botón de inicio 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-04-26 15:00:04.905  5590-5590  MainActivity            com.example.dessertclicker           D  onPause Called
2024-04-26 15:00:05.430  5590-5590  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 detuvo, el objeto Activity todavía está 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-04-26 15:00:39.371  5590-5590  MainActivity            com.example.dessertclicker           D  onRestart Called
2024-04-26 15:00:39.372  5590-5590  MainActivity            com.example.dessertclicker           D  onStart Called
2024-04-26 15:00:39.374  5590-5590  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 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-04-26 15:01:49.535  5590-5590  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, la actividad no tiene la atención del usuario, y el usuario 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 visualización. Además, si cambia la orientación del dispositivo (si el dispositivo rota del modo vertical al modo horizontal, o viceversa), es posible que el diseño deba modificarse a fin de que se ajuste a la nueva orientación. Veamos cómo se comporta la app en este caso.

La última devolución de llamada de ciclo de vida que se muestra es onDestroy(), a la que se llama después de onStop(). Se llama justo antes de que se destruya 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 el bloqueo de rotación de pantalla del emulador esté inhabilitado.
  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 observa que, cuando se cierra la actividad, esta llama a onPause(), onStop() y onDestroy(), en ese orden.
2024-04-26 15:03:32.183  5716-5716  MainActivity            com.example.dessertclicker           D  onPause Called
2024-04-26 15:03:32.185  5716-5716  MainActivity            com.example.dessertclicker           D  onStop Called
2024-04-26 15:03:32.205  5716-5716  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 el pastelito y observa que los postres vendidos y los ingresos totales no son cero.
  3. Asegúrate de que el bloqueo de rotación de pantalla del emulador esté inhabilitado.
  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.

11c9d83a11651608.png

  1. Revisa el resultado en Logcat. Filtra el resultado en MainActivity.
2024-04-26 15:04:29.356  5809-5809  MainActivity            com.example.dessertclicker           D  onCreate Called
2024-04-26 15:04:29.378  5809-5809  MainActivity            com.example.dessertclicker           D  onStart Called
2024-04-26 15:04:29.382  5809-5809  MainActivity            com.example.dessertclicker           D  onResume Called
2024-04-26 15:06:52.168  5809-5809  MainActivity            com.example.dessertclicker           D  onPause Called
2024-04-26 15:06:52.183  5809-5809  MainActivity            com.example.dessertclicker           D  onStop Called
2024-04-26 15:06:52.219  5809-5809  MainActivity            com.example.dessertclicker           D  onDestroy Called
2024-04-26 15:06:52.302  5809-5809  MainActivity            com.example.dessertclicker           D  onCreate Called
2024-04-26 15:06:52.308  5809-5809  MainActivity            com.example.dessertclicker           D  onStart Called
2024-04-26 15:06:52.312  5809-5809  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 el dispositivo se rota y la actividad se cierra y se vuelve a crear, esta se inicia con los valores predeterminados: la imagen del postre, la cantidad de postres vendidos y los ingresos totales se restablecen 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 componible 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. Para 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 para mantener su valor actualizado. Cada vez que se vuelva a ejecutar el elemento que admite composición, se reiniciará el valor de los ingresos a su valor predeterminado inicial de 0.

Para 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 programa que todas las funciones de componibilidad lean este valor para la recomposición.

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.

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

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

Usa la función rememberSaveable a fin de 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 para 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 el pastelito 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 cadena 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 sea destruir y volver a crear la actividad por completo.
  • El ejemplo más común de un cambio de configuración es 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 permanecer vigente tras un cambio de configuración, se deben declarar sus variables con rememberSaveable.

Más información