Un emulador Android dentro de Docker para pruebas móviles y CI/CD

Probar una aplicación Android no siempre debería exigir abrir Android Studio, mantener varios AVD configurados a mano o depender de una máquina de QA que «funciona porque nadie la toca». El proyecto HQarroum/docker-android propone una vía distinta: ejecutar el emulador de Android como un servicio dentro de Docker, con ADB expuesto por red, soporte KVM, ejecución headless y compatibilidad con scrcpy para controlar la pantalla en remoto.

La idea encaja muy bien con cómo trabajan hoy muchos equipos de desarrollo. Si las bases de datos, los backends, los brokers de mensajería y los entornos de testing ya se levantan con contenedores, tiene sentido que el emulador Android también pueda formar parte de ese flujo reproducible. No sustituye a todos los casos de uso de Android Studio ni a pruebas en dispositivos reales, pero puede simplificar mucho los pipelines CI/CD, las granjas de testing y los entornos de desarrollo remoto.

Qué aporta docker-android a un equipo de desarrollo

docker-android es una imagen Docker mínima y personalizable que ejecuta el emulador de Android como servicio. Está basada en Alpine, incluye Java Runtime Environment 11, soporte KVM y permite configurar la versión de Android, el tipo de imagen y la arquitectura en el momento de construir la imagen. El proyecto expone ADB en el puerto 5555 y permite controlar el emulador con scrcpy, una herramienta open source que muestra y controla dispositivos Android conectados por USB o TCP/IP sin requerir root ni instalar una app en el dispositivo.

El enfoque es muy concreto: no se trata de ejecutar «Android de escritorio» dentro de un contenedor, sino de encapsular el emulador oficial y sus dependencias mínimas para que pueda arrancarse, controlarse y destruirse de forma automatizada. Eso lo hace especialmente útil para tests instrumentados, validaciones visuales, automatización con ADB y pruebas repetibles en CI.

Característica

Qué permite en la práctica

Imagen basada en Alpine

Reducir componentes innecesarios frente a instalaciones completas

KVM

Mejorar el rendimiento del emulador si el host lo soporta

Modo headless

Ejecutar Android en servidores sin escritorio gráfico

ADB por red

Instalar APKs, lanzar comandos y leer logs desde el host

Compatibilidad con scrcpy

Ver y controlar la pantalla del emulador en remoto

API level configurable

Probar la app contra distintas versiones de Android

Imágenes Google APIs / Play Store

Validar casos que dependen de servicios de Google

Imágenes desechables

Arrancar cada ejecución desde un estado limpio

La aceleración por hardware no es un detalle menor. Google documenta que el Android Emulator puede usar aceleración gráfica y aceleración de máquina virtual para mejorar el renderizado y la velocidad de ejecución. En Linux, KVM es la vía habitual para esa aceleración de virtualización, por lo que montar /dev/kvm dentro del contenedor es una parte clave del rendimiento.

Primer arranque: Android con un comando

El flujo básico con Docker directo es sencillo. Primero se construye la imagen:

docker build -t android-emulator .

Después se lanza el contenedor con acceso a KVM y el puerto ADB publicado:

docker run -it --rm 
  --device /dev/kvm 
  -p 5555:5555 
  android-emulator

Cuando el emulador termina de arrancar, se conecta desde el host con ADB:

adb connect 127.0.0.1:5555
adb devices

Y, si se quiere controlar visualmente el dispositivo, basta con ejecutar:

scrcpy

Por defecto, el proyecto usa un preset tipo Pixel con resolución 1080 × 1920. Para un desarrollador, esto permite trabajar sin abrir Android Studio: compilar la APK, instalarla, lanzar pruebas, inspeccionar logcat y, si hace falta, ver la interfaz con scrcpy.

Un flujo mínimo para probar una APK podría ser:

adb connect 127.0.0.1:5555
adb install app/build/outputs/apk/debug/app-debug.apk
adb shell am start -n com.ejemplo.app/.MainActivity
adb logcat -d | grep "FATAL EXCEPTION"

Para una prueba rápida de estabilidad con Monkey:

adb shell monkey 
  -p com.ejemplo.app 
  --throttle 100 
  500

No es una estrategia de QA completa, pero sirve para detectar bloqueos obvios en un entorno que puede recrearse desde cero en cada ejecución.

Uso con Docker Compose y variantes con GPU

El repositorio también documenta servicios con Docker Compose. Para levantar el emulador estándar:

docker compose up android-emulator

Para la variante con aceleración CUDA:

docker compose up android-emulator-cuda

Y para una variante con CUDA y Google Play Store:

docker compose up android-emulator-cuda-store

Estas opciones son útiles cuando el equipo necesita estandarizar configuraciones. Un repositorio puede incluir su docker-compose.yml, sus scripts de test y sus imágenes de emulador para que cualquier desarrollador o runner de CI arranque el mismo entorno.

Uso

Comando

Emulador básico

docker compose up android-emulator

Variante CUDA

docker compose up android-emulator-cuda

Variante CUDA + Play Store

docker compose up android-emulator-cuda-store

Docker directo

docker run --device /dev/kvm -p 5555:5555 android-emulator

Conviene no prometer más de lo que el entorno puede dar. La aceleración dependerá del host, de los drivers, de la virtualización disponible y de la configuración de Docker. En servidores Linux con KVM bien configurado, el escenario es mucho más natural que en entornos donde Docker se ejecuta dentro de una capa adicional de virtualización.

Persistencia o emuladores limpios en cada ejecución

Una decisión importante es si el emulador debe conservar estado. En CI suele interesar lo contrario: cada ejecución debería partir de una imagen limpia para evitar falsos positivos o errores que solo aparecen por datos acumulados. El proyecto indica que las imágenes del emulador se limpian al reiniciar, lo que encaja con esa filosofía.

Para desarrollo local, en cambio, puede interesar conservar el AVD, la configuración o apps instaladas. En ese caso se puede montar /data:

docker run -it --rm 
  --device /dev/kvm 
  -p 5555:5555 
  -v ~/android_avd:/data 
  android-emulator

Así se obtiene una experiencia más parecida a un emulador persistente, pero manteniendo la instalación dentro de Docker.

Cambiar versión de Android e imagen del sistema

Una de las ventajas del proyecto es que permite construir imágenes con distintos API levels y tipos de imagen. Por defecto, el README indica API 33 con Google APIs y arquitectura x86_64, pero se puede modificar con argumentos de build.

Por ejemplo, para construir una imagen con Android Pie, Google Play Store y arquitectura x86:

docker build 
  --build-arg API_LEVEL=28 
  --build-arg IMG_TYPE=google_apis_playstore 
  --build-arg ARCHITECTURE=x86 
  --tag android-emulator:api-28-playstore .

Variables principales:

Variable

Función

API_LEVEL

Define la versión de Android mediante su nivel de API

IMG_TYPE

Selecciona imagen con Google APIs, Play Store u otras variantes disponibles

ARCHITECTURE

Define arquitectura, con soporte activo para x86_64 y x86

MEMORY

Memoria asignada al emulador

CORES

Número de núcleos asignados

EXTRA_FLAGS

Flags adicionales del emulador

DISABLE_ANIMATION

Permite desactivar animaciones

SKIP_AUTH

Controla la autenticación ADB

Para equipos que mantienen compatibilidad con varias versiones de Android, esto permite preparar una matriz de pruebas más clara:

strategy:
  matrix:
    api: [28, 30, 33]

Y asociar cada valor a una imagen distinta del emulador. El patrón es especialmente útil si se combina con pruebas Espresso, Appium, Detox o scripts propios sobre ADB.

Ejemplo de uso en un pipeline CI

Un pipeline real variará según GitHub Actions, GitLab CI, Jenkins o Buildkite, pero la idea base es siempre parecida: levantar emulador, esperar a que arranque, instalar APK, ejecutar pruebas y recoger logs.

Un script simple de espera podría ser:

#!/usr/bin/env bash
set -euo pipefail

adb connect 127.0.0.1:5555

echo "Esperando a que Android termine de arrancar..."
until adb shell getprop sys.boot_completed | grep -q "1"; do
  sleep 2
done

adb shell input keyevent 82 || true
echo "Emulador listo"

Después, instalación y pruebas:

./gradlew assembleDebug assembleAndroidTest

adb install -r app/build/outputs/apk/debug/app-debug.apk
adb install -r app/build/outputs/apk/androidTest/debug/app-debug-androidTest.apk

adb shell am instrument -w 
  com.ejemplo.app.test/androidx.test.runner.AndroidJUnitRunner

Y recogida de errores:

adb logcat -d > artifacts/logcat.txt
adb exec-out screencap -p > artifacts/screenshot.png

La gran ventaja frente a un emulador manual es la repetibilidad. Si algo falla, el equipo puede conservar logs, captura de pantalla y versión exacta de la imagen usada.

Tamaño de imagen: no es ligero como un microservicio

Aunque el proyecto busca una imagen mínima, Android no es pequeño. El README publica tamaños de referencia bastante claros:

Variante

Tamaño sin comprimir

Tamaño comprimido

API 33 + Emulator

5,84 GB

1,97 GB

API 32 + Emulator

5,89 GB

1,93 GB

API 28 + Emulator

4,29 GB

1,46 GB

Sin SDK ni emulador

414 MB

138 MB

Esto importa para CI. Si cada ejecución descarga una imagen de casi 2 GB, el tiempo de pipeline puede crecer. En entornos serios, conviene usar caché, registry interno, runners persistentes o una estrategia de imágenes base.

El proyecto permite construir sin incluir el SDK y montar /opt/android desde fuera:

docker build -t android-emulator 
  --build-arg INSTALL_ANDROID_SDK=0 .

Y ejecutar montando el SDK compartido:

docker run -it --rm 
  --device /dev/kvm 
  -p 5555:5555 
  -v /shared/android/sdk:/opt/android/ 
  android-emulator

Para empresas con muchos runners o almacenamiento compartido, esta opción puede reducir tiempos y evitar duplicar gigabytes en cada imagen.

Seguridad: ADB no debe quedar abierto a cualquiera

Hay una advertencia importante: exponer ADB en el puerto 5555 es práctico, pero también delicado. ADB permite instalar aplicaciones, ejecutar comandos, leer logs y manipular el entorno del dispositivo. En local no suele haber problema, pero en un servidor compartido o una red corporativa debe limitarse el acceso.

Mejor publicar solo en localhost:

docker run -it --rm 
  --device /dev/kvm 
  -p 127.0.0.1:5555:5555 
  android-emulator

Y evitar exponer ese puerto a internet. Si el emulador se usa en CI, lo ideal es que viva en una red interna del pipeline y se destruya al terminar.

También hay que revisar SKIP_AUTH. Que ADB no pida autenticación puede facilitar automatización, pero no es una buena idea en entornos compartidos. Para imágenes con Google Play Store, el proyecto documenta la necesidad de usar la misma clave ADB entre cliente y emulador.

Docker-android no sustituye a dispositivos reales

Para un sitio de programación conviene dejar claro el límite: un emulador en Docker no sustituye a una batería de dispositivos físicos. Hay diferencias de rendimiento, cámara, sensores, Bluetooth, NFC, comportamiento de fabricantes, capas de personalización y consumo energético. Un bug que no aparece en un Pixel emulado puede aparecer en un Samsung real de gama media.

Su espacio natural es otro: pruebas repetibles, automatización, validación rápida, regresiones, CI/CD y entornos donde lo importante es arrancar Android de forma controlada y programable. La fase final de QA debería seguir incluyendo dispositivos reales, sobre todo si la app usa hardware, multimedia, pagos, autenticación biométrica o integraciones profundas del sistema.

Lo interesante de docker-android es que acerca el mundo móvil a prácticas que los equipos backend ya tienen asumidas: infraestructura declarativa, entornos reproducibles, imágenes versionadas y pruebas que no dependen del portátil de una persona concreta. Para equipos que trabajan con Android a diario, esa mejora operativa puede ahorrar muchas horas. Puedes ampliar contexto en este análisis sobre Android dentro de Docker y revisar el código fuente en el repositorio oficial de docker-android.

Preguntas frecuentes

¿docker-android ejecuta Android completo dentro de Docker?

Ejecuta el Android Emulator dentro de un contenedor y lo expone como servicio mediante ADB. No es una distribución Android de escritorio, sino un emulador para desarrollo, pruebas y CI.

¿Necesita KVM para funcionar bien?

Sí. El proyecto está pensado para usar KVM en el host. Sin aceleración de virtualización, el rendimiento puede ser insuficiente para pruebas serias.

¿Puedo ver la pantalla del emulador?

Sí. Tras conectar ADB, se puede usar scrcpy para ver y controlar la pantalla desde el equipo local.

¿Sirve para GitHub Actions o GitLab CI?

Sí, aunque dependerá del tipo de runner y de si soporta virtualización acelerada. En runners propios con Linux y KVM suele tener más sentido.

¿Puede reemplazar pruebas en móviles reales?

No por completo. Es muy útil para automatización y regresiones, pero las pruebas finales deberían incluir dispositivos físicos cuando la app dependa de hardware, sensores o comportamientos concretos de fabricantes.

COMPARTE ESTE ARTÍCULO

COMPARTIR EN FACEBOOK
COMPARTIR EN TWITTER
COMPARTIR EN LINKEDIN
COMPARTIR EN WHATSAPP