Skip to content

Most visited

Recently visited

navigation

Procesos y subprocesos

Cuando un componente de la aplicación se inicia y la aplicación no tiene ningún otro componente ejecutándose, el sistema de Android inicia un nuevo proceso de Linux para la aplicación con un único subproceso de ejecución. De manera predeterminada, todos los componentes de la misma aplicación se ejecutan en el mismo proceso y subproceso (que se denomina "subproceso principal"). Si el componente de una aplicación se inicia y ya existe un proceso para la aplicación (porque otro componente de la aplicación existe), el componente se inicia dentro de ese proceso y usa el mismo subproceso de ejecución. Sin embargo, puedes configurar que diferentes componentes de la aplicación se ejecuten en procesos separados y puedes crear subprocesos adicionales para cualquier proceso.

En este documento, se analiza cómo funcionan los procesos y los subprocesos en una aplicación para Android.

Procesos

De manera predeterminada, todos los componentes de la misma aplicación se ejecutan en el mismo proceso y la mayoría de las aplicaciones no deben cambiar esto. No obstante, si necesitas controlar a qué procesos pertenece un componente determinado, puedes hacerlo en el archivo de manifiesto.

La entrada de manifiesto de cada tipo de elemento de componente (<activity>, <service>, <receiver> y <provider>) admite un atributo android:process que puede especificar un proceso en el cual el componente debe ejecutarse. Puedes establecer este atributo para que cada componente se ejecute en su propio proceso o para que algunos componentes compartan un proceso y otros no. También puedes establecer android:process para que los componentes de diferentes aplicaciones se ejecuten en el mismo proceso (si las aplicaciones comparten el mismo ID de usuario de Linux y están firmadas con los mismos certificados).

El elemento <application> también admite un atributo android:process para establecer un valor que se aplica a todos los componentes.

Android puede decidir finalizar un proceso en algún momento, cuando la memoria es baja y la requieren otros procesos que son inmediatamente más necesarios para lo que el usuario desea. En consecuencia, los componentes de la aplicación que se ejecutan en el proceso que se cancela son destruidos. Un proceso se inicia de nuevo para esos componentes cuando vuelve a haber trabajo para ellos.

Cuando decidas qué procesos cancelar, el sistema Android pondera su importancia relativa para el usuario. Por ejemplo, cierra más prontamente un proceso que aloja actividades que ya no se ven en la pantalla que un proceso que aloja actividades visibles. La decisión de si se debe finalizar un proceso, por lo tanto, depende del estado de los componentes que se ejecutan en ese proceso. Las reglas que se usan para decidir qué procesos finalizar se analizan a continuación.

Ciclo de vida de los procesos

El sistema Android trata de mantener el proceso de una aplicación el mayor tiempo posible; pero , finalmente, necesita quitar los procesos viejos a fin de recuperar memoria para procesos nuevos o más importantes. Para determinar qué procesos mantener y cuáles finalizar, el sistema coloca cada proceso en una "jerarquía de importancia" según los componentes que se ejecutan en el proceso y el estado de esos componentes. Los procesos con la importancia más baja se eliminan primero, luego, los siguientes con la importancia más baja y así sucesivamente, según sea necesario para recuperar recursos del sistema.

La jerarquía de importancia tiene cinco niveles. La siguiente lista presenta los distintos tipos de procesos en orden de importancia (el primer proceso es el más importante y es el que se finaliza de último):

  1. Proceso en primer plano

    Un proceso que es necesario para lo que el usuario está haciendo en ese momento. Se considera que un proceso está en primer plano si alguna de estas condiciones es verdadera:

    Por lo general, solo existen unos pocos procesos en primer plano en un momento determinado. Se los cancela solamente como último recurso (si la memoria está tan lenta que no todos pueden seguir ejecutándose). Generalmente, en ese punto, el dispositivo alcanzó un estado de paginación de memoria, por lo que finalizar procesos en primer plano es necesario para mantener la capacidad de respuesta de la interfaz de usuario.

  2. Proceso visible

    Un proceso que no tiene ningún componente en primer plano, pero que puede afectar lo que el usuario ve en la pantalla. Se considera que un proceso es visible si alguna de estas condiciones es verdadera:

    • Aloja una Activity que no esté en primer plano y sea visible para el usuario (se llamó a su método onPause()). Esto puede ocurrir, por ejemplo, si la actividad en primer plano inicia un cuadro de diálogo que permite la visualización de la actividad anterior detrás del cuadro.
    • Aloja un Service que está vinculado con una actividad visible (o en primer plano).

    Un proceso visible se considera extremadamente importante y no se lo finalizará a menos que sea necesario para mantener todos los procesos en primer plano en ejecución.

  3. Proceso de servicio

    Proceso que ejecuta un servicio iniciado con el método startService() y que no corresponde a ninguna de las dos categorías precedentes de más importancia. Aunque los procesos de servicio no están vinculados directamente con nada de lo que el usuario ve, generalmente hacen cosas que le interesan al usuario (como reproducir música en segundo plano o descargar datos en la red), por lo que el sistema los mantiene en ejecución a menos que no haya suficiente memoria para retenerlos junto con los procesos en primer plano y visibles.

  4. Proceso en segundo plano

    Un proceso que tiene una actividad que actualmente no es visible para el usuario (se llamó al método onStop() de la actividad). Estos procesos no tienen un efecto directo en la experiencia del usuario, y el sistema puede finalizarlos en cualquier momento para recuperar memoria para un proceso en primer plano, visible o de servicio. Por lo general, se ejecutan muchos procesos en segundo plano, por lo que se los mantiene en una lista LRU (menos usado recientemente) para garantizar que el proceso con la actividad que el usuario vio más recientemente sea la última en finalizarse. Si una actividad implementa sus métodos de ciclo de vida correctamente, y guarda su estado actual, finalizar sus procesos no tendrá un efecto visible en la experiencia del usuario porque, cuando el usuario vuelve a la actividad, esta restaura todos los estados visibles. Para obtener información sobre cómo guardar y restaurar el estado, consulta el documento Actividades.

  5. Procesos vacíos

    Un proceso que no tiene ningún componente de la aplicación activo. El único motivo para mantener este tipo de proceso activo es por fines de la memoria caché, para mejorar el tiempo de inicio la siguiente vez que un componente necesite ejecutarlo. El sistema suele finalizar estos procesos para equilibrar los recursos generales del sistema entre las memorias caché de procesos y las memorias caché de kernel subyacentes.

Android coloca un proceso en el nivel más alto que puede, según la importancia de los componentes actualmente activos en el proceso. Por ejemplo, si un proceso aloja un servicio y una actividad visible, el proceso se clasifica como un proceso visible y no como uno de servicio.

Además, la clasificación de un proceso se puede incrementar porque otros procesos dependen de él (un proceso que presta servicios a otro proceso nunca se puede clasificar con una posición inferior a la del proceso al que asiste). Por ejemplo, si un proveedor de contenido en el proceso A asiste a un cliente en el proceso B o si un servicio en el proceso A está vinculado con un componente en el proceso B, el proceso A siempre se considera, al menos, tan importante como el proceso B.

Como a un proceso que ejecuta un servicio se le asigna una posición superior que a un proceso con actividades en segundo plano, una actividad que inicia una operación prolongada debería iniciar un servicio para esa operación, en lugar de tan solo crear un subproceso de trabajo, especialmente si la operación durará más que la actividad. Por ejemplo, una actividad que está cargando una imagen en un sitio web debe iniciar un servicio para realizar la carga a fin de que esta pueda continuar en segundo plano incluso si el usuario deja la actividad. El uso de un servicio garantiza que la operación tendrá, como mínimo, prioridad de "proceso de servicio", independientemente de lo que suceda con la actividad. Este es el mismo motivo por el que los receptores de mensajes deben emplear servicios en lugar de tan solo poner las operaciones prolongadas en subprocesos.

Subprocesos

Cuando una aplicación se inicia, el sistema crea un subproceso de ejecución para la aplicación, que se denomina "principal". Este subproceso es muy importante porque está a cargo de distribuir eventos a los widgets correspondientes de la interfaz de usuario, incluidos los eventos de dibujo. También es el subproceso en el cual tu aplicación interactúa con los componentes del kit de herramientas de la IU de Android (componentes de los paquetes android.widget y android.view). Por esto, al subproceso principal también se lo suele denominar el subproceso de IU.

El sistema no crea un subproceso separado para cada instancia de un componente. Se crean instancias de todos los componentes que se ejecutan en el mismo proceso en el subproceso de IU, y las llamadas del sistema a cada componente se distribuyen desde ese subproceso. Por lo tanto, los métodos que responden a las callbacks del sistema (como onKeyDown() para informar acciones del usuario o un método callback de ciclo de vida) siempre se ejecutan en el subproceso de IU del proceso.

Por ejemplo, cuando el usuario toca un botón en la pantalla, el subproceso de IU de la aplicación distribuye el evento de toque al widget, el cual, a su vez, establece su estado presionado y publica una solicitud de invalidación en la cola de eventos. El subproceso de IU saca de la cola la solicitud y notifica al widget que debe volver a dibujarse.

Cuando la aplicación realiza trabajo intensivo en respuesta a una interacción del usuario, este modelo de subproceso único puede generar un rendimiento deficiente, a menos que implemente la aplicación de una manera correcta. Específicamente, si todo sucede en el subproceso de IU, realizar operaciones prolongadas, como acceder a la red o consultar la base de datos, bloqueará toda la IU. Cuando el subproceso se bloquea, no se pueden distribuir los eventos, incluidos los de dibujo. Desde la perspectiva del usuario, la aplicación no responde. Lo que es peor, si el subproceso de IU está bloqueado por más de unos pocos segundos (unos 5 segundos actualmente), al usuario se le presenta el infame cuadro de diálogo "la aplicación no responde" (ANR). El usuario podría decidir salir de la aplicación y desinstalarla si no está contento.

Además, el paquete de herramientas de la IU de Android no es seguro para subprocesos. Así que no debes manipular la IU desde un subproceso de trabajo (debes realizar la manipulación de la interfaz de usuario desde el subproceso de IU). Por lo tanto, solo existen dos reglas para el modelo de subproceso único de Android:

  1. No bloquear el subproceso de IU
  2. No acceder al paquete de herramientas de la IU de Android desde fuera del subproceso de IU

Subprocesos de trabajo

Debido al modelo de subproceso único descrito previamente, es fundamental para la capacidad de respuesta de la IU de la aplicación que no bloquee el subproceso de IU. Si tienes que realizar operaciones que no son inmediatas, debes asegurarte de hacerlo en subprocesos separados (subprocesos "en segundo plano" o "de trabajo").

Por ejemplo, a continuación, se muestra código para un gestor de clics que descarga una imagen de un subproceso separado y la muestra en una ImageView:

public void onClick(View v) {
    new Thread(new Runnable() {
        public void run() {
            Bitmap b = loadImageFromNetwork("http://example.com/image.png");
            mImageView.setImageBitmap(b);
        }
    }).start();
}

Primero, esto parece funcionar correctamente porque crea un subproceso nuevo para manejar la operación en la red. Sin embargo, viola la segunda regla del modelo de subproceso único: no acceder al paquete de herramientas de la IU de Android desde fuera del subproceso de IU (este ejemplo modifica la ImageView desde el subproceso de trabajo en lugar del subproceso de IU). Esto puede dar como resultado un comportamiento indefinido e inesperado que puede ser difícil y prolongado de localizar.

Para solucionar este problema, Android ofrece varias maneras para acceder al subproceso de IU desde otros subprocesos. En la siguiente lista, se enumeran métodos que pueden ser útiles:

Por ejemplo, puedes corregir el código anterior usando el método View.post(Runnable):

public void onClick(View v) {
    new Thread(new Runnable() {
        public void run() {
            final Bitmap bitmap =
                    loadImageFromNetwork("http://example.com/image.png");
            mImageView.post(new Runnable() {
                public void run() {
                    mImageView.setImageBitmap(bitmap);
                }
            });
        }
    }).start();
}

Ahora esta implementación es segura para subprocesos: la operación de la red se realiza desde un subproceso separado mientras que la ImageView se manipula desde el subproceso de IU.

No obstante, a medida que la complejidad de la operación se incrementa, este tipo de código puede volverse complicado y difícil de mantener. Para manejar interacciones más complejas con una subproceso de trabajo, deberías considerar usar un Handler en el subproceso de trabajo para procesar mensajes enviados desde el subproceso de IU. Posiblemente, sin embargo, la mejor solución sea extender la clase AsyncTask, que simplifica la ejecución de las tareas del subproceso de trabajo que tienen que interactuar con la IU.

Uso de la AsyncTask

La AsyncTask te permite realizar trabajo asincrónico en la interfaz de usuario. Realiza las operaciones de bloqueo en un subproceso de trabajo y, luego, publica los resultados en el subproceso de IU, sin que tú tengas que manejar los subprocesos o los controladores.

Para usarla, debes crear una subclase de la AsyncTask e implementar el método de callback doInBackground(), que se ejecuta en un grupo de subprocesos en segundo plano. Para actualizar la IU, debes implementar onPostExecute(), que entrega el resultado de doInBackground() y se ejecuta en el subproceso de IU. Por ello, puedes actualizar con seguridad tu IU. Luego, puedes ejecutar la tarea llamando a execute() desde el subproceso de IU.

Por ejemplo, puedes implementar el ejemplo precedente con la AsyncTask de esta manera:

public void onClick(View v) {
    new DownloadImageTask().execute("http://example.com/image.png");
}

private class DownloadImageTask extends AsyncTask<String, Void, Bitmap> {
    /** The system calls this to perform work in a worker thread and
      * delivers it the parameters given to AsyncTask.execute() */
    protected Bitmap doInBackground(String... urls) {
        return loadImageFromNetwork(urls[0]);
    }

    /** The system calls this to perform work in the UI thread and delivers
      * the result from doInBackground() */
    protected void onPostExecute(Bitmap result) {
        mImageView.setImageBitmap(result);
    }
}

Ahora la IU es segura y el código es más simple porque separa el trabajo en dos partes: la parte que se debe realizar en un subproceso de trabajo y la parte que se debe realizar en el subproceso de IU.

Debes leer la referencia de la AsyncTask para comprender completamente cómo usar esta clase, pero aquí presentamos información general sobre cómo funciona:

Advertencia: Otro problema que podrías encontrar cuando uses un subproceso de trabajo son los reinicios inesperados de la actividad debido a cambios de configuración del tiempo de ejecución (como cuando el usuario cambia la orientación de la pantalla), que puede destruir el subproceso de trabajo. Para conocer cómo puedes preservar la tarea durante uno de estos reinicios y cómo cancelar correctamente la tarea cuando la actividad se destruye, consulta el código fuente para la aplicación de ejemplo Shelves.

Métodos seguros para subprocesos

En algunos casos, los métodos que implementas podrían ser llamados por más de un subproceso, y por lo tanto, se los debe escribir para que sean seguros para subprocesos.

Esto es particularmente cierto para los métodos que se pueden llamar de manera remota (como los métodos en un servicio vinculado). Cuando la llamada a un método implementado en un IBinder se origina en el mismo proceso en el que el IBinder se está ejecutando, el método se ejecuta en el subproceso del emisor. Sin embargo, cuando la llamada se origina en otro proceso, el método se ejecuta en un subproceso elegido de entre un grupo de subprocesos que el sistema mantiene en el mismo proceso que el IBinder (no se ejecuta en el subproceso de IU del proceso). Por ejemplo, mientras que el método onBind() de un servicio sería llamado desde un subproceso de IU del proceso de servicio, los métodos implementados en el objeto que onBind() devuelve (como una subclase que implementa métodos de RPC) sería llamada desde los subprocesos del grupo. Como un servicio puede tener más de un cliente, más de un subproceso de grupo puede ocupar al mismo método IBinder al mismo tiempo. Los métodos IBinder deben, por lo tanto, implementarse para ser seguros para subprocesos.

Asimismo, un proveedor de contenido puede recibir solicitudes de datos que se originan en otros procesos. Aunque las clases ContentResolver y ContentProvider ocultan los detalles sobre cómo se administra la comunicación entre los procesos, se llama a los métodos ContentProvider que respondan a esas solicitudes (los métodos query(), insert(), delete(), update() y getType()) desde un grupo de subprocesos en el proceso del proveedor de contenido, no el subproceso de IU para el proceso. Como se puede llamar a estos métodos desde cualquier cantidad de subprocesos al mismo tiempo, también se los debe implementar para que sean seguros para subprocesos.

Comunicación entre procesos

Android ofrece un mecanismo para comunicación entre procesos (IPC) con llamadas de procedimiento remoto (RPC), en el que una actividad u otro componente de la aplicación llama a un método, pero se lo ejecuta de manera remota (en otro proceso), y los resultados se devuelven al emisor. Esto implica la descomposición de una llamada a un método y sus datos a un nivel en el que el sistema operativo pueda comprender, su transmisión desde el proceso y el espacio de dirección locales al proceso y el espacio de dirección remotos y, a continuación, el reensamble y la recreación de la llamada allí. Los valores de retorno se transmiten en la dirección opuesta. Android proporciona todo el código para realizar estas transacciones de IPC, de manera que tú puedas centrarte en definir e implementar la interfaz de programación de RPC.

Para realizar una IPC, tu aplicación debe vincularse con un servicio mediante bindService(). Para obtener más información, consulta la guía para desarrolladores Servicios.

This site uses cookies to store your preferences for site-specific language and display options.

Get the latest Android developer news and tips that will help you find success on Google Play.

* Required Fields

Hooray!

Follow Google Developers on WeChat

Browse this site in ?

You requested a page in , but your language preference for this site is .

Would you like to change your language preference and browse this site in ? If you want to change your language preference later, use the language menu at the bottom of each page.

This class requires API level or higher

This doc is hidden because your selected API level for the documentation is . You can change the documentation API level with the selector above the left navigation.

For more information about specifying the API level your app requires, read Supporting Different Platform Versions.

Take a short survey?
Help us improve the Android developer experience.
(Sep 2017 survey)