Explorando los procesos de Android

Hace unas semanas estaba arreglando un error en Android Studio y me encontré con la etiqueta «android:process». (Trabajo en Google en el equipo de Android Studio.) Sabía que la etiqueta permite especificar el proceso en el que se ejecutará el componente androide. Sin embargo, muchos de los detalles más finos y el razonamiento detrás de los procesos de Android eran desconocidos para mí. Así que leí sobre ello y escribí este artículo para compartir lo que aprendí.

Como siempre, estoy feliz de ser corregido en cualquier cosa en la que esté mal informado.

Una visión general sobre los procesos de Android

Android se basa en linux por lo que «el sistema Android inicia un nuevo proceso Linux para la aplicación con un único hilo de ejecución. Por defecto, todos los componentes de una misma aplicación se ejecutan en el mismo proceso e hilo (llamado hilo «principal»).» (de: Process and Threads Overview)

Generalmente el hilo «main» es también el hilo «UI» o el hilo «event», ya que cualquier modificación de UI o manejo de eventos ocurre en este hilo. (excepción: y̵o̵u̵ ̵c̵a̵n̵ ̵c̵h̵a̵n̵g̵e̵ ̵t̵h̵i̵s̵ ̵b̵y̵ ̵u̵s̵i̵n̵g̵ ̵T̵h̵r̵e̵a̵d̵ ̵A̵n̵n̵o̵t̵a̵t̵i̵o̵n̵s̵.̵ Me he equivocado, sólo es posible que las aplicaciones de sistemas tengan diferentes MainThread y UIThread: «Las herramientas de construcción tratan las anotaciones @MainThread y @UiThread como intercambiables, por lo que puedes llamar a métodos @UiThread desde métodos @MainThread, y viceversa. Sin embargo, es posible que un hilo de la interfaz de usuario sea diferente del hilo principal en el caso de las aplicaciones del sistema con múltiples vistas en diferentes hilos.»)

Por defecto, el proceso principal (y el hilo principal que inicia) se ejecuta con un nombre de proceso que es el mismo que el applicationId de la aplicación (declarado en el build.gradle de tu aplicación). Así que si fueras a crear un proyecto por defecto con Android Studio, obtendrías una aplicación Android que se ejecutará en el hilo: com.example.<username>.myapplication y el paquete por defecto correspondiente para tu código.

Qué hace la etiqueta «android:process»

Por defecto una aplicación Android sólo tendrá un único proceso. Todos los componentes se ejecutarán en el mismo proceso. Al declarar nuevos componentes en el manifiesto de tu aplicación, puedes utilizar la etiqueta «android:process» para declarar un nuevo proceso para este componente. Así:

<activity
android:name=".SettingsActivity"
android:process=":setting"
android:label="@string/title_activity_settings">
</activity>

En el fragmento anterior declaro una SettingActivity que se ejecutará en el proceso local com.example.kelvinma.myapplication:setting. (más sobre local vs global más adelante en este artículo)

La etiqueta «android:process» se puede aplicar en los 4 componentes android:

  • Activity
  • Service
  • Receiver
  • Provider

y también en la etiqueta Application. Cuando se aplica en la etiqueta Application, cualquier componente de la aplicación también hereda el android:process a menos que sea anulado por su propia etiqueta android:process.

No estoy seguro de cuál es el caso de uso para utilizar esta etiqueta en su declaración de la aplicación cuando se puede simplemente cambiar su applicationId en su lugar. Si usted sabe de uno, por favor dígame.

Un breve ejemplo visual

Para ilustrar una aplicación multiproceso, suponga que inicio la actividad por defecto en mi aplicación Pomodoro y luego usando el fragmento anterior, cambio mi SettingsActivity para que se ejecute en otro proceso. Entonces, después de iniciar mi aplicación y el inicio de la SettingsActivity, mi aplicación tendrá 2 procesos:

$ ./adb shell ps | grep kelvin
u0_a61 32175 1669 1292180 47600 SyS_epoll_ b738f2b5 S com.example.kelvinhanma.pomodoro
u0_a61 32223 1669 1284796 43736 SyS_epoll_ b738f2b5 S com.example.kelvinhanma.pomodoro:setting

Sin embargo, todos los componentes se ejecutan implícitamente en el proceso por defecto a menos que se especifique lo contrario por la etiqueta de proceso. Así que si añades android:process=":setting"a tu actividad por defecto y luego inicias otra actividad, tu aplicación tendrá 2 procesos:

com.example.kelvinhanma.pomodoro:setting
com.example.kelvinhanma.pomodoro

Porque la actividad iniciada se iniciará en el proceso por defecto ya que no tiene una etiqueta de proceso establecida.

¿Cuándo hago que mi aplicación sea multiproceso?

Hay 2 razones que conozco para crear una aplicación multiproceso:

  1. Su aplicación tiene acciones separadas que necesitan ser ejecutadas de forma independiente, el ejemplo común es una aplicación de reproducción de música tendría el proceso por defecto manejar la interfaz de usuario y otro proceso para manejar la reproducción de música.
  2. Su aplicación está golpeando las limitaciones de memoria. Android limita el uso de la memoria RAM por proceso. Así que usted puede aumentar el límite total de RAM de su aplicación mediante la ejecución de múltiples procesos. Sin embargo, ya que los procesos no comparten la memoria, no se puede compartir Singleton entre los procesos.

Local vs Global process

El tipo de proceso depende del valor que introduzca en la etiqueta process. Si comienza con dos puntos («:») entonces el proceso es local y privado para su aplicación (el nombre completo del proceso sería applicationId:processName). Si comienza con una letra minúscula entonces es un proceso global.

Un proceso global puede ser compartido entre aplicaciones. A menudo una biblioteca se ejecutará en un proceso global por lo que varias aplicaciones pueden compartir el proceso para reducir el uso de la memoria.

android:process quiz

Terminemos con algunos escenarios, a ver si puedes predecir la respuesta.

Q: ¿Qué pasa si usas un proceso global para establecer tu proceso a tu id de paquete? android:process="com.example.kelvinhanma.pomodoro"

A: Tu proceso no cambia y esto está permitido. Aunque es una cosa inútil y tonta.

P: ¿Y si lo usas para establecer tu proceso al paquete de otra aplicación? Supongamos que tengo una com.example.kelvinhanma.pomodoro corriendo en mi dispositivo. Establezco el proceso global de otra aplicación con el mismo nombre y lo despliego.

¡Terminaría con 2 procesos con el mismo nombre!

./adb shell ps | grep kelvin
u0_a181 28008 630 4324212 66060 SyS_epoll_wait 0 S com.example.kelvinhanma.pomodoro
u0_a179 28287 630 4327384 66200 SyS_epoll_wait 0 S com.example.kelvinhanma.pomodoro

Pero eso está bien. Los nombres de los procesos no tienen que ser únicos. Fíjate que los usuarios son diferentes (u0_a181, u0_a179) y los ids de los procesos son únicos.

Espero que hayas disfrutado del artículo y hayas aprendido algo. Siéntase libre de decirme si algo es incorrecto.