El ciclo de vida de una Activity en Android es el pilar esencial para diseñar, programar y optimizar aplicaciones móviles en esta plataforma. Cada pantalla que ves en una app Android es una instancia de Activity, y el modo en que el usuario navega, cambia de apps, rota el dispositivo o recibe llamadas impacta directamente en los estados internos de esta Activity. Comprender este ciclo es obligatorio para cualquier desarrollador que persigue aplicaciones robustas, rápidas y con una experiencia de usuario impecable.
¿Qué es una Activity en Android y por qué es tan importante?
Una Activity es más que una simple pantalla gráfica: representa la unidad fundamental de interacción entre usuario y aplicación. Desde el punto de vista técnico, una Activity es una clase de Java/Kotlin que extiende de Activity
o AppCompatActivity
y se declara en el archivo AndroidManifest.xml. Cada vez que el usuario abre una pantalla, realiza una acción importante o navega entre funcionalidades, hay una Activity gestionando los recursos, la lógica y la interfaz.
El sistema Android orquesta las Activity usando un sistema de pila (stack), garantizando así que el usuario pueda regresar a pantallas anteriores mediante el botón ‘atrás’. Android también gestiona de forma automática el ciclo de vida, liberando recursos cuando una Activity deja de ser relevante, para optimizar memoria y batería.
Esto implica que no existe una función main() como en otros lenguajes: Android decide qué Activity se inicia primero, cuándo detenerlas, recrearlas o finalizarlas, dependiendo de la interacción del usuario y del estado del dispositivo.
¿Por qué es esencial dominar el ciclo de vida de una Activity?
Comprender y gestionar el ciclo de vida es clave para la calidad de cualquier app Android:
- Evita fugas de memoria: Si no liberas recursos en el momento adecuado, puedes agotar la memoria del sistema y provocar cierres inesperados.
- Previene pérdidas de información del usuario: Si no guardas el estado de la pantalla ante cambios como rotación, la información puede desaparecer, frustrando al usuario.
- Ofrece una experiencia fluida ante interrupciones: Al recibir una llamada, cambiar de app o rotar el dispositivo, la app debe reaccionar elegante y rápidamente.
- Permite optimizar recursos (CPU, batería, sensores): Solo activos cuando son estrictamente necesarios.
- Facilita la navegación entre pantallas (Intents, backstack): Acciones y resultados deben gestionarse de acuerdo al ciclo vital de cada Activity.
Estados y callbacks principales del ciclo de vida de una Activity
El ciclo de vida de una Activity se gestiona mediante métodos de callback que Android invoca automáticamente. Cada uno marca un punto específico en la vida de la pantalla:
- onCreate(Bundle savedInstanceState): Inicialización y creación de la interfaz. Aquí se infla el layout y se configuran recursos básicos.
- onStart(): La Activity se vuelve visible pero aún no es interactiva.
- onResume(): La Activity toma el foco y es plenamente interactiva.
- onPause(): La Activity pierde el foco pero puede seguir siendo parcialmente visible (por ejemplo, diálogo superpuesto).
- onStop(): La Activity deja de ser visible, permanece en memoria pero no consume recursos activos.
- onRestart(): Se invoca si la Activity estaba detenida y regresa al primer plano.
- onDestroy(): La Activity está a punto de ser destruida y debe liberar todos los recursos.
Explicación detallada de cada método y mejores prácticas
onCreate(Bundle)
El primer método que se ejecuta en la vida de una Activity. Aquí se realiza la inicialización completa:
- Se infla la UI mediante
setContentView(R.layout.tu_layout)
- Se recuperan datos del
Intent
que inició la Activity - Se inicializan variables, ViewModels, adaptadores y listeners
- Se restauran datos guardados en
savedInstanceState
si correspondiera
Se ejecuta una sola vez por instancia. Toda inicialización pesada debería ir aquí, pero sin bloquear el hilo principal.
onStart()
Marca el momento en que la Activity se vuelve visible. El usuario puede verla pero no necesariamente interactuar (aún no tiene el foco). Perfecto para tareas de actualización de UI no críticas o para iniciar recursos visuales.
onResume()
La Activity toma el foco; el usuario puede pulsar, desplazarse, escribir, etc. Es el momento para:
- Reactivar sensores
- Reanudar animaciones o procesos en pausa
- Actualizar la UI según cambios externos
Todo lo que requiera que la Activity esté visible y activa debe iniciarse aquí, y pararse en onPause().
onPause()
Android lo invoca si la Activity está por perder el foco: cuando aparece un diálogo, se inicia otra Activity por encima, o el usuario navega fuera.
- Pausa animaciones, música, sensores, tareas en segundo plano
- Guarda datos efímeros o en curso, pero no persistentes
- Mantiene el método muy ligero; no es momento para guardar información crítica o realizar operaciones pesadas
onStop()
La Activity ya no es visible. Aquí debes liberar recursos pesados, detener actualizaciones, cerrar conexiones a bases de datos, desregistrar listeners y realizar guardados persistentes.
Se ejecuta justo antes de onDestroy() si la Activity va a ser destruida, o previo a onRestart() si el usuario vuelve.
onRestart()
Se invoca solo si la Activity estaba detenida y vuelve a primer plano. Permite diferenciar entre una creación completa y una reanudación desde memoria.
onDestroy()
Última llamada: Android está destruyendo la instancia de la Activity. Libera recursos, finaliza tareas, guarda lo imprescindible. No siempre se garantiza su ejecución (si el sistema mata el proceso abruptamente, no se llama).
Estados visuales y conceptuales de una Activity
El ciclo se puede dividir en estados conceptuales:
- Activo (Resumed): En primer plano, interactiva y visible. Solo una Activity puede estar así por app.
- Pausado (Paused): Parcialmente visible, sin foco. El usuario no puede interactuar, pero la Activity sigue en pantalla (por ejemplo, si aparece un diálogo).
- Detenido (Stopped): No es visible, pero permanece en memoria para reacción rápida si el usuario regresa.
- Destruido (Destroyed): Instancia eliminada. Si el usuario regresa, se crea desde cero salvo que se haya guardado el estado.
Transiciones habituales: ejemplos prácticos en la navegación
- Lanzamiento (app abierta por primera vez):
onCreate()
→onStart()
→onResume()
- Inicio de una nueva Activity:
onPause()
→onStop()
en la Activity previa,onCreate()
→onStart()
→onResume()
en la nueva - Regresar a una Activity parada:
onRestart()
→onStart()
→onResume()
- Pulsar Home o cambiar de app:
onPause()
→onStop()
- Rotar la pantalla:
onPause()
→onStop()
→onDestroy()
→ [nueva instancia]onCreate()
→onStart()
→onResume()
Estos flujos son frecuentes y deben implementarse para evitar sorpresas en el comportamiento de la aplicación.
Gestión de cambios de configuración: rotación, idioma, recursos
Un cambio de configuración (rotación de pantalla, cambio de idioma, modo oscuro, teclado físico) provoca que Android destruya y recree la Activity para adaptarse al nuevo entorno, lo que implica perder los datos almacenados en memoria de la instancia a menos que se gestionen correctamente. Los mecanismos para conservar información son:
- onSaveInstanceState(Bundle): Para guardar pares clave-valor con el estado de la UI (textos, posiciones, etc.) justo antes de destruirse. Se recuperan en
onCreate()
oonRestoreInstanceState()
. - ViewModel: Permite almacenar datos y estados relacionados con UI que deben sobrevivir a cambios de configuración. Es la solución moderna y recomendada.
- rememberSaveable (Jetpack Compose): Permite que variables resistan recomposiciones y reinicios de la interfaz.
Por ejemplo, si el usuario completa un formulario y gira el móvil, onSaveInstanceState guardará los campos escritos y los restaurará tras la recreación de la pantalla.
Diferencias clave: onSaveInstanceState, ViewModel y rememberSaveable
- onSaveInstanceState(Bundle): Para datos simples (cadenas, números). No apto para colecciones grandes o objetos complejos, porque requiere serialización/deserialización.
- ViewModel: Ideal para datos medios o complejos, pues vive mientras la Activity o Fragment existe (incluyendo recreaciones por cambios de configuración).
- rememberSaveable: Específico para Jetpack Compose, almacena variables para que resistan recomposiciones y recreaciones de la Activity.
La elección depende del tipo de datos, la arquitectura y la tecnología (UI tradicional vs Compose).
Gestión de Activities en segundo plano y ahorro de recursos
Android puede pausar, detener o destruir Activities no visibles para optimizar memoria y batería. Las Activities en segundo plano tienen mayor probabilidad de desaparecer si el sistema necesita recursos. Por eso, debes:
- Liberar sensores, cámaras, ubicaciones en
onPause()
yonStop()
- Guardar el estado de la UI antes de dejar la pantalla
- Evitar tareas de larga duración fuera de una Activity visible
Navegación y comunicación entre Activities: Intents y resultados
Las Activities comunican y navegan entre sí usando Intents, objetos que describen la intención de realizar una acción:
- startActivity(Intent): Inicia una Activity sin esperar resultado.
- startActivityForResult(Intent, int): Inicia una Activity esperando que devuelva resultado (útil para seleccionar un contacto, tomar una foto, etc.).
El resultado se recibe en onActivityResult()
con un código de estado y los datos devueltos.
Intents explícitos e implícitos: cómo usarlos y diferencias
Los Intents pueden ser:
- Explícitos: Apuntan a una clase concreta. Utilizados generalmente para navegar dentro de la misma aplicación.
- Implícitos: Describen una acción (por ejemplo, compartir o abrir un enlace), permitiendo que otras aplicaciones respondan si pueden gestionar esa acción.
Ejemplos:
- Para abrir tu propia Activity:
Intent(this, ConfigActivity::class.java)
- Para compartir texto por email:
Intent(Intent.ACTION_SEND)
con tipotext/plain
Patrones, buenas prácticas y errores frecuentes en el ciclo de vida
- No bloquees el hilo UI en
onCreate()
: Las operaciones costosas (bases de datos, red) deben ejecutarse en segundo plano. - Evita guardar datos persistentes en onPause(): Mejor usar
onStop()
o mecanismos como ViewModel o bases de datos. - No confíes sólo en onDestroy(): A este método no siempre se le invoca si el sistema mata el proceso abruptamente.
- Utiliza LiveData y Observers para mantener la UI reactiva a cambios de datos almacenados en ViewModel.
- Super siempre debe llamarse en callbacks: Llama a
super.onCreate()
y otros para no romper comportamientos por defecto del sistema.
Llevar una correcta segmentación de tareas según el ciclo minimiza errores y maximiza el rendimiento de la app.
Ejemplos de casos de uso y flujos comunes de ciclo de vida
- Abrir la app por primera vez: Secuencia
onCreate()
→onStart()
→onResume()
. - Activity parcialmente visible: Aparece un diálogo; solo se ejecuta
onPause()
pero sigue visible. - Salir y volver a la app: Después de pulsar Home y regresar,
onRestart()
→onStart()
→onResume()
(sin volver aonCreate()
). - Rotación del dispositivo: La Activity es destruida y recreada; se vuelve a ejecutar
onCreate()
trasonDestroy()
. - Salir definitivamente (botón atrás):
onPause()
→onStop()
→onDestroy()
Estos flujos son cotidianos y el desarrollador debe anticipar y probar cada caso para evitar fugas, pérdidas de datos o comportamientos inconsistentes.
Integración avanzada: ciclo de vida, procesos y gestión de memoria
Android puede finalizar procesos enteros para liberar memoria, especialmente en dispositivos con pocos recursos. El grado de vulnerabilidad a ser destruido depende del estado:
Estado | Visible | En foco | Probabilidad de ser destruida |
---|---|---|---|
Resumed (Activa) | Sí | Sí | Baja |
Started/Paused | Sí | No | Media |
Stopped | No | No | Alta |
Destroyed | No | No | Total |
Por este motivo, el sistema nunca destruye una Activity individualmente: elimina el proceso completo, y con él todas las instancias de Activities, Fragments y servicios en ese proceso.
Cómo guardar y restaurar el estado de la interfaz correctamente
Para asegurar que el usuario no pierda información relevante después de eventos como rotación, cierre por memoria o navegación, combina estos enfoques:
- Usa onSaveInstanceState(Bundle) para datos ligeros y temporales (texto, selecciones, scroll)
- Utiliza ViewModel para datos complejos y persistentes (listas, objetos, resultados de red)
- Emplea bases de datos o almacenamiento local para información de largo plazo o usuario
No olvides que para restaurar correctamente, onCreate()
debe comprobar si el Bundle
es nulo, y sólo leer datos si contiene valores válidos. onRestoreInstanceState()
se invoca sólo si hay algo guardado. Siempre llama a los métodos super
para no interrumpir la restauración de la jerarquía de vistas por defecto.
Jetpack Compose y ciclo de vida: integración moderna
Con Jetpack Compose, el ciclo de vida incorpora el de los componibles (composables), cuyas variables de estado pueden sobrevivir recomposiciones (con remember
) y cambios de configuración (rememberSaveable
). Usa:
- remember para variables que solo necesitas durante la composición actual
- rememberSaveable para persistencia ante recreación de la Activity
- ViewModel para datos globales o compartidos entre múltiples pantallas o componibles
Errores comunes en el ciclo de vida de la Activity y consejos para evitarlos
- Pérdida de datos tras rotación: No guardar correctamente en
onSaveInstanceState()
o ViewModel. Solución: siempre guarda lo indispensable. - Fugas de memoria: No liberar listeners ni cerrar conexiones en
onStop()
yonDestroy()
. - UI bloqueada al volver al foreground: Dejar procesos pesados activos tras
onPause()
oonStop()
. Usa hilos o corutinas para separar tareas costosas. - Crash tras regresar a una Activity: No inicializar recursos o no restaurar el estado consistente según la secuencia del ciclo.
Recursos recomendados para profundizar en el ciclo de vida de Activities
- Guía oficial de Android: Ciclo de vida de la actividad
- Codelab práctico: Etapas del ciclo de vida de la actividad
- Tutoriales en GitHub sobre
ViewModel
y gestión avanzada de estado en Compose
Dominar el ciclo de vida de la Activity es el primer paso hacia el desarrollo Android profesional. Desde la declaración en el manifiesto hasta la gestión avanzada de estados y comunicación entre pantallas, cada detalle cuenta. Utilizando correctamente los callbacks, ViewModel y la integración con Compose, garantizas aplicaciones estables, rápidas y preparadas para cualquier situación, incluso en dispositivos con recursos limitados.