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
En esta página
- Antes de empezar
- Cómo encajan las piezas
- Crear el proyecto
- Tamaño de pantalla y coordenadas
- Hacer una captura de pantalla con Python y adb
- Tocar
- Deslizar
- Escribir
- Pulsar teclas
- Abrir y comprobar apps
- Un script completo
- Dónde encaja adb en este tutorial de Python
- Qué puede salir mal
- Could not discover RPC port
- Los toques caen en el sitio equivocado
- input_text no escribió nada
- 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
masdentro.
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
- Abre Macros y haz clic en Crear nuevo proyecto.
- Elige Basado en código, pon Dispositivo objetivo en móvil, nombra el proyecto y haz clic en Crear proyecto.
- Abre el proyecto. El Code Editor muestra
src/app.py. - 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.
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
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
mas.click(270, 800) # tap, then wait 1000 ms
mas.click(270, 800, delay_ms=300) # shorter pause for tight loopsclick(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
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 deliberateswipe(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
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 thereinput_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
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 presskey_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
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.
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.
clickse convierte enadb shell input tap,swipeenadb shell input touchscreen swipe,input_textenadb shell input textykey_pressenadb shell input keyevent. Los gestos de pellizco se escriben directamente en el dispositivo de entrada táctil del emulador. - Pantalla.
take_screenshoty cadafind_objectcapturan conadb exec-out screencap -p, la forma segura para binarios que no estropea el PNG en Windows.get_screen_sizeleewm size;get_current_applee el gestor de ventanas. - Apps.
open_applanza el intent del launcher mediantemonkeyy recurre aam startsi falla;close_appejecutaam 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
Gracias. Si algo está mal, cuéntanoslo en Discord.
¿Dudas? Pregunta en Discord