Buscar

Guías

Tutorial de ADB en Python: controlar un emulador Android

Tutorial de ADB en Python para Macro Automation Studio: toca, desliza, escribe, pulsa teclas y haz capturas en BlueStacks mientras la app gestiona adb por ti.

  • Windows
  • Mac
  • Emulador
  • Dispositivo en la nube
  • Teléfono
  • SDK de Python
Intermedio Actualizado 7 min de lectura
En esta página
  1. Antes de empezar
  2. Cómo encajan las piezas
  3. Crear el proyecto
  4. Tamaño de pantalla y coordenadas
  5. Hacer una captura de pantalla con Python y adb
  6. Tocar
  7. Deslizar
  8. Escribir
  9. Pulsar teclas
  10. Abrir y comprobar apps
  11. Un script completo
  12. Dónde encaja adb en este tutorial de Python
  13. Qué puede salir mal
  14. Could not discover RPC port
  15. Los toques caen en el sitio equivocado
  16. input_text no escribió nada
  17. Dispositivo no encontrado u offline

La mayoría de los tutoriales de ADB en Python acaban en un envoltorio de subprocess alrededor de adb shell input tap. Este tutorial de ADB en Python para Macro Automation Studio (MAS) toma otro camino: la app es la dueña de adb, y tu script habla con la app a través del paquete mas. Esta guía va desde un proyecto nuevo hasta un script que abre una app, busca un término, hace scroll y guarda una captura. Está pensada para desarrolladores de Python que quieren controlar BlueStacks, LDPlayer, MuMu Player o MEmu sin escribir llamadas a adb.

Antes de empezar

  • MAS está instalado y has iniciado sesión con la prueba o un plan. Consulta Instalar MAS.
  • Un emulador está en marcha con ADB activado y añadido como dispositivo en Grupos de dispositivos. Consulta Dispositivos.
  • Sabes algo de Python. No instalas nada más: MAS incluye su propio entorno de Python 3.13 con mas dentro.

Cómo encajan las piezas

MAS inicia tu script como python -u -m src.app con la carpeta del proyecto como directorio de trabajo. Le pasa los datos de conexión en variables de entorno (MAS_RPC_PORT, MAS_RPC_HOST, MAS_DEVICE_ID, MAS_SESSION_TOKEN). Cada llamada mas.* se convierte en una petición JSON-RPC 2.0 a la app. La app ejecuta el comando adb, la búsqueda de plantilla o el OCR, y devuelve el resultado. El dispositivo está vinculado antes de que se ejecute tu primera línea, así que no hay llamada connect() ni número de serie que gestionar. Consulta Conceptos.

Crear el proyecto

  1. Abre Macros y haz clic en Crear nuevo proyecto.
  2. Elige Basado en código, pon Dispositivo objetivo en móvil, nombra el proyecto y haz clic en Crear proyecto.
  3. Abre el proyecto. El Code Editor muestra src/app.py.
  4. Elige el emulador en el selector de dispositivo y haz clic en Ejecutar (F5) cada vez que quieras probar un fragmento. Detener es Mayús+F5.

Tamaño de pantalla y coordenadas

Cada coordenada que pasas al SDK es un desplazamiento en píxeles desde la esquina superior izquierda de la pantalla del dispositivo, en la resolución propia del dispositivo. El tamaño de la ventana del emulador en tu monitor no importa.

python
import mas

size = mas.get_screen_size()
print(f"Screen: {size.width}x{size.height}")

center = (size.width // 2, size.height // 2)
mas.click(*center)

get_screen_size() devuelve un ScreenSize con width y height. get_device_info() devuelve lo mismo más el name, el type y el indicador connected del dispositivo. Cuando un script debe sobrevivir a un cambio de resolución, calcula las posiciones como fracciones del tamaño, como hace el ejemplo del final. Las imágenes de plantilla y las regiones OCR no lo sobreviven; consulta la guía de reconocimiento de imagen.

Hacer una captura de pantalla con Python y adb

python
import base64
import mas

shot = mas.take_screenshot()
print(shot.width, shot.height, shot.timestamp)

with open("screen.png", "wb") as f:
    f.write(base64.b64decode(shot.base64))

take_screenshot() devuelve un Screenshot: base64 contiene un PNG, width y height son su tamaño en píxeles y timestamp es una cadena ISO 8601. El archivo de arriba acaba en la carpeta del proyecto porque ese es el directorio de trabajo. Pasa el mismo objeto a varias llamadas find_object con screenshot=shot y todas buscan en un solo fotograma en lugar de capturar de nuevo.

Tocar

python
mas.click(270, 800)                 # tap, then wait 1000 ms
mas.click(270, 800, delay_ms=300)   # shorter pause for tight loops

click(x, y, delay_ms=1000) toca una vez y espera delay_ms después para que la app reaccione. Un toque no tiene duración de mantenimiento; para una pulsación larga usa key_press con duration_ms.

Deslizar

python
mas.swipe((270, 750), (270, 300), duration_ms=400)    # scroll a list down: drag from lower to upper
mas.swipe((100, 500), (400, 500), duration_ms=1000)   # drag and drop: slow and deliberate

swipe(from_coords, to_coords, duration_ms=1000) recibe dos tuplas (x, y). Una duración corta es un gesto rápido; una larga es un arrastre. Los gestos de pellizco son llamadas aparte: zoom_in() y zoom_out() usan por defecto el centro de la pantalla con percent=50.

Escribir

python
mas.click(270, 120, delay_ms=500)          # focus the field first
mas.input_text("hello world")
mas.input_text("new value", clear=True)    # replace what is there

input_text(text, delay_ms=0, clear=False) escribe en el campo con el foco, así que toca el campo primero. clear=True mueve el cursor al final y borra los caracteres existentes antes de escribir.

Pulsar teclas

python
from mas import KeyCode

mas.key_press(KeyCode.BACK)
mas.key_press(KeyCode.HOME)
mas.key_press(KeyCode.ENTER)
mas.key_press(KeyCode.DELETE, repeat=10)          # ten presses in one command
mas.key_press(KeyCode.POWER, duration_ms=3000)    # long press

key_press(key_code, modifiers=None, duration_ms=100, repeat=1) envía un evento de tecla de Android. Un duration_ms de 500 o más se convierte en pulsación larga; el número exacto de milisegundos por encima de eso no se respeta. repeat va de 1 a 100 y es mucho más rápido que un bucle de Python. KeyCode también tiene las teclas de la cruceta, el volumen, los medios y los números.

Abrir y comprobar apps

python
mas.open_app("com.android.settings", timeout_ms=5000)
print(mas.get_current_app())                        # package name in front
print(mas.is_app_focused("com.android.settings"))   # True or False

if mas.get_app_state("com.android.chrome") == mas.NOT_RUNNING:
    mas.open_app("com.android.chrome")

mas.close_app("com.android.settings")

open_app(package_name, timeout_ms=2000) lanza por nombre de paquete y espera timeout_ms. Para conocer un nombre de paquete, abre la app a mano e imprime mas.get_current_app(). get_app_state devuelve NOT_INSTALLED, NOT_RUNNING, RUNNING_IN_BACKGROUND_SUSPENDED, RUNNING_IN_BACKGROUND o RUNNING_IN_FOREGROUND. close_app fuerza la detención del paquete.

Un script completo

El script abre los Ajustes de Android, busca un término, hace scroll por los resultados, guarda una captura y comprueba con OCR que el término está en pantalla. El cuadro de búsqueda es una plantilla que recortas en Asset Lab; sustituye el ID por el tuyo. El deslizamiento usa fracciones del tamaño de pantalla para que funcione a cualquier resolución.

python
import base64
import sys
import time

import mas
from mas import KeyCode

PACKAGE = "com.android.settings"
TERM = "Display"

images = mas.images({"search_box": 301})


def main():
    size = mas.get_screen_size()
    mas.log(f"Screen {size.width}x{size.height}")

    mas.open_app(PACKAGE, timeout_ms=5000)
    if not mas.is_app_focused(PACKAGE):
        mas.log(f"{PACKAGE} did not come to the front", level="error")
        sys.exit(2)

    box = mas.find_object_retry(images.search_box, total_tries=3, time_sleep=2.0)
    if box is None:
        mas.log("Search box not found; crop it in Asset Lab", level="error")
        sys.exit(2)
    mas.click(box.x, box.y, delay_ms=800)
    mas.input_text(TERM, clear=True)
    mas.key_press(KeyCode.ENTER)
    time.sleep(2)

    x = size.width // 2
    mas.swipe((x, int(size.height * 0.75)), (x, int(size.height * 0.35)), duration_ms=600)
    time.sleep(1)

    shot = mas.take_screenshot()
    with open("search_results.png", "wb") as f:
        f.write(base64.b64decode(shot.base64))
    mas.log(f"Saved search_results.png ({shot.width}x{shot.height})")

    text = mas.read_text(screenshot=shot, psm=11)
    mas.log(f"Term on screen: {TERM.lower() in text.text.lower()}")

    mas.key_press(KeyCode.HOME)


if __name__ == "__main__":
    main()

read_text con psm=11 lee texto disperso por todo el fotograma, lo que va bien para una lista de resultados. La guía de OCR cubre las regiones y los modos para leer un solo contador. Si prefieres describir la tarea en vez de escribirla, MAS Agent escribe este tipo de script por ti.

Dónde encaja adb en este tutorial de Python

Nunca llamas a adb, pero es quien hace el trabajo por debajo.

  • Binario. El instalador incluye adb. En Windows está en C:\ProgramData\MacroAutomationStudio\3rdparty, con una copia integrada como alternativa. En un Mac está dentro del paquete de la app, con el adb de Homebrew como alternativa. Nada entra en tu PATH. La página Instalación tiene los detalles.
  • Conexión. Los emuladores exponen adb en un puerto TCP local. Cuando haces clic en Iniciar o Ejecutar, MAS ejecuta adb connect 127.0.0.1:<port> con el puerto de la tarjeta del dispositivo y mantiene esa sesión durante la ejecución.
  • Entrada. click se convierte en adb shell input tap, swipe en adb shell input touchscreen swipe, input_text en adb shell input text y key_press en adb shell input keyevent. Los gestos de pellizco se escriben directamente en el dispositivo de entrada táctil del emulador.
  • Pantalla. take_screenshot y cada find_object capturan con adb exec-out screencap -p, la forma segura para binarios que no estropea el PNG en Windows. get_screen_size lee wm size; get_current_app lee el gestor de ventanas.
  • Apps. open_app lanza el intent del launcher mediante monkey y recurre a am start si falla; close_app ejecuta am force-stop.
  • Servidores. MAS ejecuta su propio servidor adb. Si otra versión de adb en tu PATH inicia el suyo, los dos se sustituyen entre sí y tu dispositivo cae a Conectando. La página Solución de problemas de ADB lista las soluciones, puerto por puerto.

Como la app hace todo esto, un script escrito en BlueStacks se ejecuta sin cambios en LDPlayer, en un dispositivo en la nube o en tu propio móvil a través de un puerto local.

Qué puede salir mal

Could not discover RPC port

El script se inició desde una terminal en lugar de desde MAS, así que faltan las variables de entorno. Ejecútalo con Ejecutar en el Code Editor o desde una tarjeta de dispositivo.

Los toques caen en el sitio equivocado

Estás usando píxeles de la ventana en lugar de píxeles del dispositivo, o la resolución del emulador cambió. Imprime mas.get_screen_size() y compáralo con las coordenadas que pasas. Las coordenadas de Asset Lab ya son píxeles del dispositivo.

input_text no escribió nada

Ningún campo tenía el foco. Toca el campo con click y dale delay_ms=500 antes de escribir. Algunas apps abren un teclado que tapa el campo; key_press(KeyCode.BACK) lo cierra después de escribir.

Dispositivo no encontrado u offline

ADB está apagado en el emulador, el puerto de la tarjeta del dispositivo es incorrecto o otro servidor adb tomó el control. Sigue Solución de problemas de ADB.

Si la app que controlas es un juego, trátala con el mismo cuidado que cualquier otra cuenta automatizada. Ninguna herramienta de automatización está libre de riesgos al 100 %, así que automatiza con responsabilidad y bajo tu propio criterio.

Siguientes pasos

Páginas relacionadas

¿Te ha servido esta página?

¿Dudas? Pregunta en Discord