Procesos y el ciclo de vida de la app

En la mayoría de los casos, cada aplicación para Android ejecuta su propio proceso de Linux. Este proceso se crea para la aplicación cuando parte de su código debe ejecutarse, y permanece en ejecución hasta que el sistema necesita recuperar su memoria para que la usen otras aplicaciones y ya no es necesaria.

Una función inusual y fundamental de Android es que la duración del proceso de una aplicación no está controlada directamente por la aplicación en sí. En cambio, lo determina el sistema a través de una combinación de las partes de la aplicación que el sistema sabe que están en ejecución, qué tan importantes son estas cosas para el usuario y cuánta memoria general hay disponible en el sistema.

Es importante que los desarrolladores de aplicaciones comprendan cómo los diferentes componentes de la aplicación (en particular, Activity, Service y BroadcastReceiver) afectan la vida útil del proceso de la aplicación. Si no usas correctamente estos componentes, es posible que el sistema cierre el proceso de la aplicación mientras realiza una tarea importante.

Un ejemplo común de un error en el ciclo de vida del proceso es un BroadcastReceiver que inicia un subproceso cuando recibe una Intent en su método BroadcastReceiver.onReceive() y, luego, regresa de la función. Una vez que se muestra, el sistema considera que BroadcastReceiver ya no está activo y que su proceso de hosting ya no es necesario, a menos que otros componentes de la aplicación estén activos en él.

Por lo tanto, el sistema puede finalizar el proceso en cualquier momento para recuperar memoria y, al hacerlo, finaliza el subproceso generado que se ejecuta en el proceso. Por lo general, la solución a este problema es programar una JobService desde BroadcastReceiver para que el sistema sepa que hay trabajo activo en el proceso.

Para determinar qué procesos cerrar cuando hay poca memoria, Android coloca cada proceso en una jerarquía de importancia según los componentes que se ejecutan en ellos y el estado de esos componentes. En orden de importancia, los tipos de procesos son los siguientes:

  1. Un proceso en primer plano es uno que se requiere para lo que el usuario está haciendo actualmente. Varios componentes de la aplicación pueden hacer que el proceso que lo contiene se considere en primer plano de diferentes maneras. Un proceso se considera en primer plano si se cumple alguna de las siguientes condiciones:
  2. Solo hay unos pocos procesos de este tipo en el sistema, y estos solo se eliminan como último recurso si la memoria es tan baja que ni siquiera estos pueden continuar ejecutándose. Por lo general, si esto sucede, el dispositivo alcanzó un estado de paginación de memoria. Por lo tanto, esta acción es necesaria para mantener la capacidad de respuesta de la interfaz de usuario.

  3. Un proceso visible realiza una tarea que el usuario conoce actualmente, por lo que su cierre tiene un impacto negativo notable en la experiencia del usuario. Un proceso se considera visible en las siguientes condiciones:
    • Ejecuta un Activity que es visible para el usuario en pantalla, pero no en primer plano (se llamó a su método onPause()). Esto puede ocurrir, por ejemplo, si el objeto Activity en primer plano se muestra como un diálogo que permite ver el objeto Activity anterior detrás de él.
    • Tiene un Service que se ejecuta como un servicio en primer plano, a través de Service.startForeground() (que le pide al sistema que trate el servicio como algo que el usuario conoce o, básicamente, como si fuera visible).
    • Aloja un servicio que el sistema utiliza para una función específica que el usuario conoce, como un fondo animado o un servicio de método de entrada.

    La cantidad de procesos que se ejecutan en el sistema es menos limitada que los procesos en primer plano, pero aún está relativamente controlada. Estos procesos se consideran extremadamente importantes y no se finalizan, a menos que sea necesario para mantener en ejecución todos los procesos en primer plano.

  4. Un proceso de servicio es aquel que contiene un Service que se inició con el método startService(). Aunque estos procesos no son directamente visibles para el usuario, por lo general, realizan acciones que le interesan (como subir o descargar datos de la red en segundo plano), por lo que el sistema siempre mantiene en ejecución esos procesos, a menos que no haya suficiente memoria para retener todos los procesos visibles y en primer plano.

    Es posible que los servicios que han estado en ejecución durante mucho tiempo (como 30 minutos o más) se desciendan de importancia para permitir que su proceso pase a la lista almacenada en caché.

    Los procesos que deben ejecutarse durante un período largo se pueden crear con setForeground. Si es un proceso periódico que requiere un tiempo de ejecución estricto, se puede programar a través de AlarmManager. Para obtener más información, consulta Asistencia para trabajadores de larga duración. Esto ayuda a evitar situaciones en las que los servicios de larga duración que usan recursos excesivos (por ejemplo, por fugas de memoria) impiden que el sistema brinde una buena experiencia del usuario.

  5. Un proceso almacenado en caché es uno que no se necesita actualmente, por lo que el sistema tiene la libertad de eliminarlo según sea necesario cuando se necesiten recursos (como la memoria) para otra parte. En un sistema que funciona con normalidad, estos son los únicos procesos involucrados en la administración de recursos.

    Un sistema que se ejecuta correctamente tiene varios procesos en caché siempre disponibles para lograr un cambio eficiente entre aplicaciones y, con frecuencia, finaliza las apps almacenadas en caché según sea necesario. Solo en situaciones muy críticas el sistema llega a un punto en el que todos los procesos almacenados en caché se cierran y debe comenzar a cerrar los procesos del servicio.

    Dado que el sistema puede finalizar los procesos almacenados en caché en cualquier momento, las apps deben cesar todo el trabajo mientras se encuentran en estado almacenado en caché. Si la app debe realizar un trabajo fundamental para el usuario, debe usar las APIs anteriores a fin de ejecutar el trabajo desde un estado de proceso activo.

    Los procesos almacenados en caché a menudo contienen una o más instancias de Activity que el usuario no puede ver en este momento (se llamó y mostró su método onStop()). Siempre que implementen correctamente su ciclo de vida de Activity cuando el sistema finalice esos procesos, esto no afectará la experiencia del usuario cuando regrese a esa app. Puede restablecer el estado guardado previamente cuando la actividad asociada se vuelva a crear en un proceso nuevo. Ten en cuenta que no se garantiza que se llame a onDestroy() en caso de que el sistema finalice un proceso. Para obtener más información, consulta Activity.

    A partir de Android 13, es posible que el proceso de una app reciba un tiempo de ejecución limitado o nulo hasta que entre en uno de los estados de ciclo de vida activo anteriores.

    Los procesos almacenados en caché se mantienen en una lista. La política de pedidos exacta de esta lista es un detalle de la implementación de la plataforma. En general, intenta mantener procesos más útiles, como los que alojan la aplicación de inicio del usuario o la última actividad que vio el usuario, antes que otros tipos de procesos. También se pueden aplicar otras políticas para finalizar procesos, como establecer límites estrictos en la cantidad de procesos permitidos o limitar la cantidad de tiempo que un proceso puede permanecer almacenado en caché de forma continua.

Cuando se decide cómo clasificar un proceso, el sistema basa su decisión en el nivel más importante que se encuentra entre todos los componentes actualmente activos en el proceso. Consulta la documentación de Activity, Service y BroadcastReceiver para obtener más detalles sobre cómo cada uno de estos componentes contribuye al ciclo de vida general de un proceso y de la aplicación.

La prioridad de un proceso también puede aumentar en función de otras dependencias que tenga un proceso. Por ejemplo, si el proceso A está vinculado a un Service con la marca Context.BIND_AUTO_CREATE o usa un ContentProvider en el proceso B, la clasificación del proceso B siempre es al menos tan importante como la del proceso A.