Guía Completa del Ciclo de Vida de una Actividad en Android: Desde Conceptos Básicos hasta Jetpack Compose

  • Estados fundamentales: Análisis detallado de las devoluciones de llamada desde onCreate() hasta onDestroy().
  • Gestión de recursos: Estrategias para optimizar la batería y la memoria evitando fugas de datos en segundo plano.
  • Persistencia de estado: Implementación de rememberSaveable y ViewModels para sobrevivir a cambios de configuración.
  • Modernización con Compose: Adaptación del ciclo de vida al paradigma declarativo de Jetpack Compose.

guia-basica-programacion-android-2

Cuando se empieza a programar en un lenguaje como C++ o Java, lo primero que se enseña es el método main, el punto de entrada al que llamará el sistema operativo cuando vayamos a arrancar nuestra aplicación. Sin embargo, en el ecosistema de Android, el paradigma es distinto.

En Android no existe un método main como tal, sino que el sistema operativo (SSOO) gestiona la ejecución a través de componentes. Existen varios métodos de devolución de llamada (callbacks) en nuestra actividad que serán llamados por el sistema cuando ocurran eventos importantes. En este artículo estudiaremos a fondo cuáles son esos eventos y cómo funciona el ciclo completo de una actividad de Android. La documentación oficial ofrece una explicación extensa, pero aquí analizaremos los elementos clave, la gestión de memoria y los errores más comunes.

El ciclo de vida de Android sigue un esquema de estados predefinidos que permiten a la aplicación saber cuándo debe liberar recursos, guardar datos o reanudar una tarea.

android-lifecycle

Análisis Detallado de los Eventos del Ciclo de Vida

La clase Activity proporciona un conjunto básico de devoluciones de llamada que el sistema invoca cuando la actividad entra en un nuevo estado. A continuación, detallamos cada uno de ellos:

  1. onCreate(Bundle savedInstanceState)
    • Representa el momento en el que la actividad se crea por primera vez. Es el estado Created. Aquí se ejecuta la lógica de arranque básica que ocurre una sola vez, como vincular datos a listas, asociar la actividad con un ViewModel o crear instancias de variables de clase.
    • Recibe un objeto Bundle llamado savedInstanceState, que contiene el estado guardado previamente. Si la actividad nunca existió, este valor será nulo. Es el lugar ideal para llamar a setContent() en Compose o setContentView() en vistas tradicionales.
  2. onStart()
    • La actividad pasa al estado Started y va a estar en pantalla, aunque no necesariamente sea interactiva. El sistema la prepara para que entre en primer plano.
    • Si la actividad proviene de una parada (estado Stopped), pasaremos primero por onRestart(). Es un método que se completa rápidamente antes de transicionar al siguiente estado.
  3. onRestart()
    • Se invoca específicamente cuando la actividad estaba detenida (onStop()) y el usuario regresa a ella. Es el lugar adecuado para colocar código que solo debe ejecutarse si la actividad no se inicia por primera vez.
  4. onResume()
    • La actividad entra en el estado Resumed, pasa al primer plano y comienza a responder a la interacción del usuario. Es el estado donde la app tiene el foco de atención.
    • Se debe utilizar para inicializar componentes que se liberan en onPause(), como la vista previa de la cámara o sensores que requieran atención exclusiva.
  5. onPause()
    • Es la primera indicación de que el usuario está abandonando la actividad. La actividad deja de responder a la interacción, pero sigue siendo visible (por ejemplo, en modo multiventana o si aparece un diálogo semitransparente).
    • Se recomienda usarlo para pausar operaciones que no deben continuar sin foco o liberar recursos que afecten la duración de la batería (como el GPS).
  6. onStop()
    • La actividad ha pasado completamente a segundo plano y ya no es visible para el usuario. El sistema mantiene el objeto en memoria, pero ya no está vinculado al administrador de ventanas.
    • Es el lugar correcto para realizar operaciones de finalización que consuman CPU, como guardar borradores en una base de datos a través de un ViewModel.
  7. onDestroy()
    • La actividad va a ser destruida y sus recursos liberados definitivamente. Esto ocurre porque el usuario la descarta, se llama a finish(), o hay un cambio de configuración (como rotar la pantalla).

Implementación Práctica y Buenas Prácticas

Cuando necesitemos implementar uno de estos métodos, lo haremos mediante la anulación (override) en nuestra clase:

public class MiActividad extends Activity {
     protected void onCreate(Bundle savedInstanceState) {
          super.onCreate(savedInstanceState);
          // Lógica de creación
     }
     protected void onStart() {
          super.onStart();
          // Lógica de visibilidad
     }
     protected void onRestart() {
          super.onRestart();
          // Lógica de reinicio
     }
     protected void onResume() {
          super.onResume();
          // Lógica de interacción
     }
     protected void onPause() {
          super.onPause();
          // Lógica de pausa
     }
     protected void onStop() {
          super.onStop();
          // Lógica de detención
     }
     protected void onDestroy() {
          super.onDestroy();
          // Lógica de destrucción
     }
}

Es fundamental mantener la llamada al método de la superclase (super.on...()). En los eventos de entrada, debe ir al principio; en los de salida, al final. Esto asegura que los elementos internos del sistema se gestionen correctamente antes y después de nuestra lógica personalizada.

No es necesario añadir todos los eventos. Los que no se declaren usarán la implementación por defecto. Los más críticos suelen ser onCreate, onPause y onResume.

Gestión de Recursos y el Riesgo de la Destrucción

Un error común es confiar en que onStop() o onDestroy() se ejecutarán siempre. El sistema operativo Android puede matar el proceso de la aplicación en cualquier momento para liberar RAM sin previo aviso. Por ello, onPause() es la última oportunidad segura para salvar datos críticos y detener servicios costosos como la geolocalización.

La probabilidad de que el sistema finalice un proceso depende del estado de la actividad:

  • Primer plano (Resumed): Probabilidad más baja de ser finalizado.
  • Visible (Started/Paused): Probabilidad baja.
  • Segundo plano (Stopped): Probabilidad alta.
  • Vacío (Finalizado): Probabilidad más alta.

Cambios de Configuración y Persistencia de Estado

Un cambio de configuración ocurre cuando el dispositivo cambia su estado de forma radical, como al rotar la pantalla de vertical a horizontal o cambiar el idioma. En estos casos, Android destruye y recrea la actividad por completo, ejecutando el ciclo onPause > onStop > onDestroy > onCreate > onStart > onResume.

Para evitar la pérdida de datos durante este proceso, existen varias estrategias:

  1. Estado de instancia: El sistema guarda automáticamente pares clave-valor básicos (como texto en un campo).
  2. ViewModel: Se utiliza para contener datos de vista complejos que deben sobrevivir a la recreación de la Activity.
  3. rememberSaveable (Jetpack Compose): A diferencia de remember, que solo mantiene el estado durante la recomposición, rememberSaveable guarda la información en el paquete de estado de la instancia, permitiendo que los datos persistan tras rotar el dispositivo.

Integración con Jetpack Compose

En la programación moderna con Compose, se recomienda evitar colocar lógica de negocio compleja directamente en los callbacks de la Activity. En su lugar, se utilizan efectos optimizados para el ciclo de vida:

  • collectAsStateWithLifecycle: Permite consumir flujos de datos (Flows) del ViewModel de forma eficiente, deteniendo la recopilación cuando la app pasa a segundo plano.
  • Arquitectura de Actividad Única: La tendencia actual es usar una sola Activity que aloja múltiples pantallas componibles mediante la biblioteca de Navigation, reduciendo la necesidad de gestionar múltiples ciclos de vida de actividades independientes.

Coordinación entre Actividades e Intents

Cuando una actividad inicia otra mediante un Intent, ambas experimentan transiciones. Si la Actividad A inicia la Actividad B, el orden es:

  1. Se ejecuta onPause() de la Actividad A.
  2. Se ejecutan onCreate(), onStart() y onResume() de la Actividad B.
  3. Si la Actividad A queda totalmente oculta, se ejecuta su onStop().

Esto permite una transición fluida de información. Para iniciar actividades externas (como la cámara o el navegador), se utilizan Intents implícitos, describiendo la acción deseada en lugar de la clase específica.

Ideas Finales para un Desarrollo Robusto

  • Domina cada evento: Utiliza onCreate para inicializar, onPause para liberar recursos inmediatos y onResume para reactivarlos.
  • Cuidado con las variables estáticas: Evita usar variables estáticas no finales, ya que la app puede quedar cargada en memoria con valores obsoletos al regresar.
  • Optimiza el modo multiventana: Recuerda que en este modo, una actividad puede estar en onPause() pero seguir siendo totalmente visible.
  • Prueba con Logcat: Utiliza la clase Log.d(TAG, "mensaje") para monitorizar en tiempo real cómo transita tu app entre los estados del ciclo de vida.

Más información – Guía básica de programación en Android

El dominio del ciclo de vida de las actividades es el pilar fundamental para crear aplicaciones Android fluidas, eficientes y libres de errores. Comprender que la plataforma tiene el control total sobre la destrucción y recreación de los procesos permite al desarrollador implementar estrategias de persistencia adecuadas, optimizando el consumo de batería y garantizando que la experiencia del usuario sea ininterrumpida, independientemente de las llamadas entrantes, la rotación del dispositivo o la gestión de memoria del sistema.


Surfshark Antivirus para Android
Pode que che interese:
Cómo eliminar virus en Android: Guía Completa y Actualizada para Limpiar tu Móvil
Añadir como fuente preferida en Google