Ciclo de vida de procesos y aplicaciones

En la mayoría de los casos, cada aplicación para Android se ejecuta en su propio proceso de Linux. Se crea este proceso para la aplicación cuando es necesario ejecutar parte de su código, y seguirá ejecutándose hasta que ya no sea necesario y el sistema necesite recuperar su memoria a fin de que la usen otras aplicaciones.

Una característica poco común y fundamental de Android es que la duración del proceso de una aplicación no se controla directamente desde la aplicación. En su lugar, el sistema la determina mediante una combinación de las partes de la aplicación que el sistema sabe que se están ejecutando, la importancia de estas para el usuario y la cantidad total de memoria disponible en el sistema.

Es importante que los desarrolladores de aplicaciones comprendan cómo los diferentes componentes de la aplicación (en especial Activity, Service y BroadcastReceiver) afectan la duración del proceso de la aplicación. Si estos componentes no se usan correctamente, el sistema puede eliminar el proceso de la aplicación mientras realiza una tarea importante.

Un ejemplo común de un error del ciclo de vida del proceso es un BroadcastReceiver que inicia un subproceso cuando recibe un intent en su método BroadcastReceiver.onReceive() y, luego, regresa de la función. Una vez que regresa, el sistema considera que el objeto BroadcastReceiver ya no está activo y, por lo tanto, 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, es posible que el sistema elimine el proceso en cualquier momento a fin de recuperar memoria, con lo cual finaliza el subproceso generado que se está ejecutando en el proceso. Una solución común a este problema es programar un JobService desde el BroadcastReceiver de modo que el sistema sepa que aún se está trabajando en el proceso.

A fin de determinar qué procesos deben eliminarse cuando hay poca memoria, Android coloca cada proceso en una "jerarquía de importancia" que se basa en los componentes que se ejecutan en ellos y el estado de esos componentes. Los tipos de procesos son los siguientes (en orden de importancia):

  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 los 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 habrá unos pocos procesos de este tipo en el sistema, y solo se eliminarán como último recurso si la memoria es tan baja que ni siquiera estos pueden continuar ejecutándose. En general, en este punto, el dispositivo alcanzó un estado de paginación de memoria, por lo que esta acción es necesaria para que la interfaz de usuario responda.

  3. Un proceso visible realiza una tarea sobre la que el usuario tiene conocimiento, por lo que su eliminación tendría un impacto negativo notable en la experiencia del usuario. Un proceso se considera visible si cumple con las siguientes condiciones:
    • Ejecuta una Activity que es visible para el usuario en pantalla, pero no en primer plano (se llamó a su método onPause()), lo cual puede ocurrir, por ejemplo, si la actividad en primer plano se muestra como un diálogo que permite ver la actividad anterior detrás de ella.
    • Tiene un Service que se ejecuta como servicio en primer plano por medio de Service.startForeground() (que le pide al sistema que trate el servicio como algo que el usuario conoce o que es esencialmente visible para este).
    • Aloja un servicio que el sistema usa para una función que el usuario conoce, como un fondo de pantalla animado, un servicio de método de entrada, etcétera.

    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 sumamente importantes y no se eliminarán 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(). Si bien estos procesos no son directamente visibles para el usuario, generalmente están realizando tareas que le interesan (como la carga o descarga de datos de la red en segundo plano), por lo que el sistema siempre los mantendrá en ejecución a menos que no haya suficiente memoria para retener todos los que estén visibles y en primer plano.

    Es posible que se descienda el nivel de importancia de los servicios que han estado ejecutándose por un tiempo prolongado (como 30 minutos o más) a fin de permitir que sus procesos se incluyan en la lista de LRU almacenada en caché que se describe a continuación. De esta manera, se evitan situaciones en las que los servicios de duración prolongada con pérdidas de memoria o algún otro problema consuman tanta memoria RAM que impidan que el sistema use los procesos almacenados en caché de manera eficaz.

  5. Un proceso almacenado en caché es uno que no se necesita actualmente, por lo que el sistema puede eliminarlo cuando lo desee si se necesita memoria en otra parte. En un sistema con comportamiento normal, estos son los únicos procesos involucrados en la administración de memoria: un sistema que funcione bien tendrá varios procesos almacenados en caché siempre disponibles (para que la alternancia entre aplicaciones sea más eficiente) y eliminará regularmente los más antiguos según sea necesario. Solo en situaciones muy críticas (y también indeseables) el sistema llegará a un punto en el que todos los procesos almacenados en caché se eliminarán y deberá comenzar a eliminar los procesos del servicio.

    Estos procesos suelen contener una o varias instancias de una Activity que el usuario no puede ver en este momento (se llamó y se mostró el método onStop()). Siempre que implementen el ciclo de vida de su actividad correctamente (consulta Activity para obtener más detalles), cuando el sistema elimine dichos procesos, la experiencia del usuario no se verá afectada al volver a esa app: puede restaurar el estado guardado previamente cuando la actividad asociada vuelve a crearse en un nuevo proceso.

    Estos procesos se mantienen en una lista de pseudo-LRU, donde el último proceso de la lista es el primero que se elimina a fin de recuperar memoria. La política exacta de orden en esta lista es un detalle de implementación de la plataforma, pero generalmente tratará de mantener los procesos más útiles (uno que aloje la aplicación de inicio del usuario, la última actividad que vio, etc.) encima de otros tipos de procesos. Es posible que también se apliquen otras políticas para eliminar procesos: límites más restrictivos en la cantidad de procesos permitidos, límites en la cantidad de tiempo que un proceso puede permanecer almacenado en caché de manera continua, etcétera.

En el momento de decidir cómo clasificar un proceso, el sistema basará su decisión en el nivel más importante que se encuentre 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. En la documentación de cada una de estas clases, se describe con más detalle cómo impactan en el ciclo de vida general de su 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 será al menos tan importante como la del proceso A.