Puntos clave
App Lifecycle es una máquina de estados finitos que describe todos los estados posibles de una aplicación móvil y las transiciones entre ellos. Cada estado determina si la aplicación puede ejecutar código, mostrar la interfaz de usuario y consumir recursos. iOS y Android tienen modelos similares pero no idénticos — las diferencias se deben a la arquitectura del SO: iOS utiliza un control de memoria estricto (estado suspended), mientras que Android utiliza una gestión flexible de procesos mediante onSaveInstanceState.
En iOS, el lifecycle está estrechamente vinculado al foreground/background: la aplicación está activa o suspendida. En Android, el lifecycle es más granular — una Activity/Ventana pasa por 6+ estados, y el proceso en sí tiene un lifecycle adicional a través de ProcessLifecycleOwner. Las aplicaciones modernas (desde 2024) utilizan cada vez más patrones de arquitectura basados en lifecycle: SwiftUI Lifecycle (iOS 16+) mediante el entorno .scenePhase, Compose Lifecycle mediante LifecycleEventObserver. Estos patrones manejan automáticamente el cambio de contexto y previenen fugas de memoria. Según Google, el manejo correcto del lifecycle en Android reduce el consumo de memoria entre un 30 y un 40 % durante el trabajo en segundo plano.
En iOS, el ciclo de vida de la aplicación se gestiona a través de la clase UIApplicationDelegate (AppDelegate). Los métodos principales: application(_:didFinishLaunchingWithOptions:) — inicialización en el primer inicio; applicationDidBecomeActive — la aplicación es visible y está lista para la interacción; applicationWillResignActive — transición al estado inactivo (llamada entrante, cierre de la cortinilla de notificaciones); applicationDidEnterBackground — la aplicación está oculta, ~5 segundos para guardar datos; applicationWillEnterForeground — regreso del fondo; applicationWillTerminate — finalización (se llama solo para aplicaciones antiguas sin suspended).
A partir de iOS 13, Apple introdujo SceneDelegate (UISceneDelegate) para soportar aplicaciones multiventana en iPad y macOS Catalyst. SceneDelegate gestiona el lifecycle de cada escena (ventana) individual: scene(_:willConnectTo:options:) — creación de la escena; sceneDidBecomeActive — la escena está activa; sceneWillResignActive — la escena pierde el foco; sceneDidEnterBackground — la escena está oculta. AppDelegate maneja eventos globales (carga de datos, notificaciones push), mientras que SceneDelegate maneja el estado de la interfaz de usuario de cada ventana. En SwiftUI, el lifecycle se maneja a través del valor del entorno @Environment(\.scenePhase): .active, .inactive, .background — esto proporciona una API unificada tanto para aplicaciones SwiftUI como UIKit.
En Android, el ciclo de vida comienza con la clase Application — un singleton creado antes que cualquier Activity. El método Application.onCreate() es el primer punto de entrada, utilizado para la inicialización global (Analytics, DI, Crash Reporting). Después, se lanza la Activity con la secuencia: onCreate() → onStart() → onResume(). Al minimizar: onPause() → onStop() → onSaveInstanceState(). Al destruir: onDestroy(). Al regresar: onRestart() → onStart() → onResume().
ProcessLifecycleOwner (de AndroidX Lifecycle 2.2+) rastrea el lifecycle de todo el proceso, no de una Activity individual. Proporciona dos estados: ON_RESUME (aplicación en foreground) y ON_STOP (aplicación oculta). ProcessLifecycleOwner resuelve el problema de «cuándo la aplicación pasó a segundo plano» a nivel de proceso — esto es crítico para SDK, bibliotecas y aplicaciones modulares. En Jetpack Compose, el lifecycle se gestiona a través de LifecycleEventObserver y collectAsStateWithLifecycle, que suspende automáticamente la recolección de datos en modo de segundo plano. Android 14 (API 34) agregó notificación de ciclo de vida del servicio en primer plano a través de Service.onTimeout — el servicio debe completarse en 6 horas, de lo contrario el sistema lo detiene forzosamente.
| iOS | Android | Descripción |
|---|---|---|
| Not Running | — | La aplicación no está iniciada |
| didFinishLaunching | Application.onCreate | Primera inicialización al iniciar |
| Active | onResume | La aplicación es visible y acepta entrada |
| Inactive | onPause | Pérdida temporal de foco (llamada, cortinilla de notificaciones) |
| Background | onStop | La aplicación está oculta, el código puede ejecutarse |
| Suspended | — | El código no se ejecuta, la memoria está reservada |
| Will Terminate | onDestroy | La aplicación está finalizando |
La principal diferencia: iOS fuerza la transición de la aplicación a Suspended (el código deja de ejecutarse) 5-10 segundos después de entrar en segundo plano. Android permite el trabajo en segundo plano (servicios, WorkManager) durante un período más largo, pero con limitaciones del Modo Doze (Android 6+) y restricciones de fondo (Android 12+). ProcessLifecycleOwner en Android es el equivalente de UIApplicationDidEnterBackgroundNotification en iOS, con la diferencia de que se activa en cualquier transición a segundo plano, no solo después de perder el foco.
Implementación básica de AppDelegate manejando todos los estados del ciclo de vida. Guardar datos al pasar a segundo plano y actualizar la interfaz de usuario al regresar.
import UIKit
@main
class AppDelegate: UIResponder, UIApplicationDelegate {
func application(
_ application: UIApplication,
didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?
) -> Bool {
Analytics.shared.initialize()
return true
}
func applicationDidBecomeActive(_ application: UIApplication) {
NotificationCenter.default.post(name: .appDidBecomeActive, object: nil)
}
func applicationWillResignActive(_ application: UIApplication) {
NotificationCenter.default.post(name: .appWillResignActive, object: nil)
}
func applicationDidEnterBackground(_ application: UIApplication) {
CoreDataManager.shared.saveContext()
UserDefaults.standard.synchronize()
}
func applicationWillEnterForeground(_ application: UIApplication) {
NetworkManager.shared.refreshSession()
}
}
AppDelegate es el punto central del ciclo de vida. applicationDidEnterBackground es un método crítico: iOS da ~5 segundos para guardar. applicationDidBecomeActive — actualice la interfaz de usuario, reinicie animaciones. Para SceneDelegate (iOS 13+), la lógica del ciclo de vida se distribuye entre escenas: sceneDidBecomeActive / sceneDidEnterBackground gestionan ventanas individuales.
SceneDelegate para aplicaciones multiventana de iPad. Cada escena tiene su propio ciclo de vida, independiente de otras ventanas.
import UIKit
class SceneDelegate: UIResponder, UIWindowSceneDelegate {
var window: UIWindow?
func scene(
_ scene: UIScene,
willConnectTo session: UISceneSession,
options connectionOptions: UIScene.ConnectionOptions
) {
guard let windowScene = (scene as? UIWindowScene) else { return }
window = UIWindow(windowScene: windowScene)
window?.rootViewController = ViewController()
window?.makeKeyAndVisible()
}
func sceneDidBecomeActive(_ scene: UIScene) {
PlayerManager.shared.resume()
}
func sceneDidEnterBackground(_ scene: UIScene) {
PlayerManager.shared.pause()
CoreDataManager.shared.saveContext()
}
func sceneDidDisconnect(_ scene: UIScene) {
PlayerManager.shared.cleanup()
}
}
SceneDelegate gestiona el ciclo de vida de una escena individual. sceneDidBecomeActive / sceneDidEnterBackground son los equivalentes de los métodos de AppDelegate para una ventana específica. sceneDidDisconnect se llama al cerrar una escena (el usuario cerró una ventana en iPad). Para compatibilidad con iOS 12 e inferiores, AppDelegate debe duplicar la lógica de SceneDelegate. En iOS 17+, puede usar @Observable para pausar/reanudar automáticamente tareas según scenePhase.
ProcessLifecycleOwner rastrea cuándo la aplicación está en foreground/background a nivel de proceso. Este es el enfoque óptimo para la gestión global del ciclo de vida.
import android.app.Application
import androidx.lifecycle.ProcessLifecycleOwner
import androidx.lifecycle.Lifecycle
import androidx.lifecycle.LifecycleObserver
import androidx.lifecycle.OnLifecycleEvent
class MainApplication : Application() {
override fun onCreate() {
super.onCreate()
ProcessLifecycleOwner.get().lifecycle.addObserver(AppLifecycleObserver())
}
}
class AppLifecycleObserver : LifecycleObserver {
@OnLifecycleEvent(Lifecycle.Event.ON_START)
fun onEnterForeground() {
Analytics.shared.onForeground()
NetworkMonitor.shared.start()
}
@OnLifecycleEvent(Lifecycle.Event.ON_STOP)
fun onEnterBackground() {
Analytics.shared.onBackground()
NetworkMonitor.shared.stop()
}
}
ProcessLifecycleOwner es la única forma correcta de rastrear la transición a segundo plano a nivel de aplicación. ON_START se activa cuando la aplicación se vuelve visible (foreground), ON_STOP — cuando está completamente oculta (background). A diferencia del ciclo de vida de Activity, ProcessLifecycleOwner no está vinculado a una pantalla específica. Para Jetpack Compose, use LifecycleResumeEffect o collectAsStateWithLifecycle en lugar de la observación manual.
ViewModel + Lifecycle es la arquitectura correcta de Android para la gestión del ciclo de vida. ViewModel se limpia automáticamente en onDestroy, evitando fugas de memoria.
import androidx.lifecycle.ViewModel
import androidx.lifecycle.LiveData
import androidx.lifecycle.MutableLiveData
class TimerViewModel : ViewModel() {
private val _time = MutableLiveData<Long>()
val time: LiveData<Long> = _time
private var startTime = System.currentTimeMillis()
init {
updateTime()
}
private fun updateTime() {
_time.value = System.currentTimeMillis() - startTime
}
override fun onCleared() {
super.onCleared()
Logger.d("ViewModel cleared — liberando recursos")
}
}
// En Activity
class TimerActivity : AppCompatActivity() {
private val viewModel: TimerViewModel by viewModels()
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
Logger.d("Activity created")
}
override fun onResume() {
super.onResume()
viewModel.time.observe(this) { Logger.d("Time: $it") }
}
override fun onPause() {
super.onPause()
Logger.d("Activity paused — pausando animaciones")
}
override fun onDestroy() {
super.onDestroy()
Logger.d("Activity destroyed — limpieza")
}
}
ViewModel sobrevive a la rotación de pantalla y se destruye solo cuando finaliza la Activity. onCleared() es el lugar para liberar recursos y cancelar suscripciones a flujos. LiveData pausa automáticamente la observación en onPause y la reanuda en onResume — esto proporciona protección contra fugas y fallos durante el trabajo en segundo plano. En Compose, use collectAsStateWithLifecycle() para el mismo propósito.
Preguntas frecuentes
Al minimizar, la aplicación pasa de activo a inactivo (brevemente), luego a segundo plano. Después de unos segundos, el sistema puede moverla a suspended — el código deja de ejecutarse, la memoria está reservada. Cuando hay poca memoria, el sistema termina la aplicación suspendida (no se llama a willTerminate). Guarde sus datos en applicationDidEnterBackground — este es el último punto garantizado para guardar.
Mínimo obligatorio: applicationDidEnterBackground — guardar datos del usuario; applicationWillEnterForeground — actualizar la interfaz de usuario; en Android onPause — pausar animaciones/sensores. ProcessLifecycleOwner simplifica el manejo: onResume — onPause para foreground, onStart — onStop para visibilidad. Maneje los otros estados según sea necesario — no todos los estados son críticos para cada aplicación.
AppDelegate es el delegado global de toda la aplicación (inicio, transición a segundo plano). SceneDelegate (iOS 13+) gestiona el ciclo de vida de una ventana individual (escena) — en aplicaciones multiventana de iPad, cada escena tiene su propio ciclo de vida. Para iOS 12 e inferiores, todo el ciclo de vida se maneja a través de AppDelegate. Con iPadOS 16+, SceneDelegate es obligatorio para Stage Manager.
ProcessLifecycleOwner es un componente de AndroidX Lifecycle que rastrea el ciclo de vida de toda la aplicación (proceso), no de una Activity individual. Proporciona dos eventos principales: ON_RESUME (aplicación en foreground) y ON_STOP (aplicación en background). Se utiliza para la inicialización/limpieza de SDK, análisis, monitores de red — todo lo que deba reaccionar a la transición de la aplicación a segundo plano, independientemente de la pantalla actual.
En Jetpack Compose, use LifecycleEventObserver a través de LocalLifecycleOwner.current.lifecycle o la función collectAsStateWithLifecycle(). Para pantallas de Compose, use LifecycleResumeEffect — similar a DisposableEffect, que se ejecuta en onResume y se limpia en onPause. lifecycleScope.launchWhenResumed — una corrutina que se suspende automáticamente en segundo plano.
Resumen
Desarrollaremos una aplicación móvil llave en mano
IT Sectr crea aplicaciones para iOS y Android para startups y empresas desde 2017. Le asesoraremos y le propondremos la mejor solución.
Lea también