# Python ADB: điều khiển giả lập Android bằng Python

> Hướng dẫn Python ADB trong Macro Automation Studio: chạm, vuốt, gõ chữ, nhấn phím và chụp màn hình trên BlueStacks, với adb do ứng dụng quản lý thay bạn.

Source: https://automationmacro.com/vi/docs/guides/control-an-emulator-from-python (Hướng dẫn, updated 2026-09-05)

Hầu hết hướng dẫn Python ADB kết thúc bằng một lớp bọc `subprocess` quanh `adb shell input tap`. Hướng dẫn Python ADB này cho Macro Automation Studio (MAS) đi hướng khác: ứng dụng nắm adb, và mã của bạn nói chuyện với ứng dụng qua gói `mas`. Hướng dẫn đi từ một dự án mới đến đoạn mã mở ứng dụng, tìm một từ khóa, cuộn và lưu ảnh chụp màn hình. Trang dành cho lập trình viên Python muốn điều khiển BlueStacks, LDPlayer, MuMu Player hoặc MEmu mà không phải viết lời gọi adb.

## Trước khi bắt đầu

- MAS đã cài và bạn đã đăng nhập với bản dùng thử hoặc gói. Xem [Cài đặt MAS](/docs/install).
- Một giả lập đang chạy với ADB đã bật và đã thêm làm thiết bị trong **Nhóm thiết bị**. Xem [Thiết bị](/docs/devices).
- Bạn biết Python cơ bản. Bạn không phải cài gì thêm: MAS mang theo môi trường Python 3.13 riêng với `mas` bên trong.

## Các mảnh ghép khớp với nhau thế nào

MAS khởi chạy mã của bạn bằng `python -u -m src.app` với thư mục dự án làm thư mục làm việc. Nó truyền thông tin kết nối qua biến môi trường (`MAS_RPC_PORT`, `MAS_RPC_HOST`, `MAS_DEVICE_ID`, `MAS_SESSION_TOKEN`). Mỗi lời gọi `mas.*` trở thành một yêu cầu JSON-RPC 2.0 tới ứng dụng. Ứng dụng chạy lệnh adb, tìm ảnh mẫu hoặc OCR, và gửi kết quả về. Thiết bị đã được gắn trước khi dòng đầu tiên của bạn chạy, nên không có lời gọi `connect()` và không có số sê-ri nào phải quản lý. Xem [Khái niệm](/docs/concepts).

## Tạo dự án

1. Mở **Macro** và nhấn **Tạo dự án mới**.
2. Chọn **Code-Based**, đặt **Thiết bị đích** là mobile, đặt tên dự án và nhấn **Tạo dự án**.
3. Mở dự án. Code Editor hiển thị `src/app.py`.
4. Chọn giả lập trong ô chọn thiết bị và nhấn **Chạy** (<kbd>F5</kbd>) mỗi khi bạn muốn thử một đoạn mã. **Dừng** là <kbd>Shift</kbd>+<kbd>F5</kbd>.

## Kích thước màn hình và tọa độ

Mọi tọa độ bạn truyền cho SDK là độ lệch pixel tính từ góc trên bên trái màn hình thiết bị, theo độ phân giải riêng của thiết bị. Kích thước cửa sổ giả lập trên màn hình máy tính của bạn không quan trọng.

```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()` trả về một `ScreenSize` với `width` và `height`. `get_device_info()` trả về cùng thông tin đó kèm `name`, `type` và cờ `connected` của thiết bị. Khi mã phải sống sót qua thay đổi độ phân giải, hãy tính vị trí theo tỷ lệ của kích thước, như ví dụ ở cuối. Ảnh mẫu và vùng OCR thì không sống sót được; xem [hướng dẫn nhận diện hình ảnh](/docs/guides/image-recognition-macros).

## Chụp màn hình bằng Python 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()` trả về một `Screenshot`: `base64` chứa một ảnh PNG, `width` và `height` là kích thước pixel của nó, và `timestamp` là chuỗi ISO 8601. Tệp ở trên rơi vào thư mục dự án vì đó là thư mục làm việc. Truyền cùng đối tượng đó cho nhiều lời gọi `find_object` với `screenshot=shot` và tất cả sẽ tìm trên một khung hình thay vì chụp lại.

## Chạm

```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)` chạm một lần và chờ `delay_ms` sau đó để ứng dụng kịp phản ứng. Không có thời lượng giữ trên cú chạm; để nhấn giữ, dùng `key_press` với `duration_ms`.

## Vuốt

```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)` nhận hai bộ `(x, y)`. Thời lượng ngắn là vuốt nhanh; thời lượng dài là kéo. Cử chỉ chụm là các lời gọi riêng: `zoom_in()` và `zoom_out()` mặc định ở giữa màn hình với `percent=50`.

## Gõ chữ

```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)` gõ vào ô đang có tiêu điểm, nên hãy chạm vào ô trước. `clear=True` đưa con trỏ về cuối và xóa các ký tự hiện có trước khi gõ.

## Nhấn phím

```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)` gửi một sự kiện phím Android. `duration_ms` từ 500 trở lên trở thành nhấn giữ; số mili giây chính xác vượt quá mức đó không được tôn trọng. `repeat` chạy từ 1 đến 100 và nhanh hơn nhiều so với vòng lặp Python. `KeyCode` cũng có các phím D-pad, âm lượng, media và số.

## Mở và kiểm tra ứng dụng

```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)` khởi chạy theo tên gói và chờ `timeout_ms`. Để biết tên gói, mở ứng dụng bằng tay và in `mas.get_current_app()`. `get_app_state` trả về `NOT_INSTALLED`, `NOT_RUNNING`, `RUNNING_IN_BACKGROUND_SUSPENDED`, `RUNNING_IN_BACKGROUND` hoặc `RUNNING_IN_FOREGROUND`. `close_app` buộc dừng gói.

## Một đoạn mã hoàn chỉnh

Mã này mở Cài đặt Android, tìm một từ khóa, cuộn kết quả, lưu ảnh chụp màn hình và kiểm tra bằng OCR rằng từ khóa có trên màn hình. Ô tìm kiếm là một ảnh mẫu bạn cắt trong Asset Lab; thay ID bằng của bạn. Cú vuốt dùng tỷ lệ của kích thước màn hình nên hoạt động ở mọi độ phân giải.

```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` với `psm=11` đọc chữ rải rác trên toàn khung hình, hợp với một danh sách kết quả. [Hướng dẫn OCR](/docs/guides/ocr-text-reading) nói về vùng và chế độ để đọc một bộ đếm. Nếu bạn muốn mô tả tác vụ thay vì viết, [MAS Agent](/agent) viết loại mã này cho bạn.

## adb nằm ở đâu trong hướng dẫn Python ADB này

Bạn không bao giờ gọi adb, nhưng nó đang làm việc bên dưới.

- **Tệp nhị phân.** Bộ cài mang theo adb. Trên Windows nó nằm tại `C:\ProgramData\MacroAutomationStudio\3rdparty`, với bản tích hợp làm dự phòng. Trên Mac nó nằm trong gói ứng dụng, với adb của Homebrew làm dự phòng. Không có gì được thêm vào PATH của bạn. Trang [Cài đặt](/docs/install) có chi tiết.
- **Kết nối.** Giả lập mở adb trên một cổng TCP cục bộ. Khi bạn nhấn **Bắt đầu** hoặc **Chạy**, MAS chạy `adb connect 127.0.0.1:<port>` với cổng trên thẻ thiết bị và giữ phiên đó cho lần chạy.
- **Thao tác.** `click` trở thành `adb shell input tap`, `swipe` trở thành `adb shell input touchscreen swipe`, `input_text` trở thành `adb shell input text`, và `key_press` trở thành `adb shell input keyevent`. Cử chỉ chụm được ghi thẳng vào thiết bị nhập cảm ứng của giả lập.
- **Màn hình.** `take_screenshot` và mọi `find_object` chụp bằng `adb exec-out screencap -p`, dạng an toàn nhị phân không làm hỏng PNG trên Windows. `get_screen_size` đọc `wm size`; `get_current_app` đọc trình quản lý cửa sổ.
- **Ứng dụng.** `open_app` kích hoạt intent của launcher qua `monkey` và dự phòng bằng `am start`; `close_app` chạy `am force-stop`.
- **Máy chủ.** MAS chạy máy chủ adb riêng. Nếu một bản adb thứ hai trên PATH của bạn khởi động máy chủ riêng, hai bên thay thế lẫn nhau và thiết bị của bạn rơi về Đang kết nối. Trang [Khắc phục sự cố ADB](/docs/adb-troubleshooting) liệt kê cách sửa, theo từng cổng.

Vì ứng dụng làm tất cả những việc này, một đoạn mã viết trên BlueStacks chạy không đổi trên LDPlayer, trên thiết bị cloud, hoặc trên điện thoại của bạn qua một cổng cục bộ.

## Điều gì có thể sai

### Could not discover RPC port

Mã được khởi chạy từ terminal thay vì từ MAS, nên thiếu biến môi trường. Chạy bằng **Chạy** trong Code Editor hoặc từ thẻ thiết bị.

### Cú chạm rơi sai chỗ

Bạn đang dùng pixel của cửa sổ thay vì pixel của thiết bị, hoặc độ phân giải giả lập đã đổi. In `mas.get_screen_size()` và so với tọa độ bạn truyền. Tọa độ từ Asset Lab đã là pixel của thiết bị.

### input_text không gõ gì

Không có ô nào có tiêu điểm. Chạm vào ô bằng `click` và cho nó `delay_ms=500` trước khi gõ. Một số ứng dụng mở bàn phím che mất ô; `key_press(KeyCode.BACK)` đóng bàn phím sau khi gõ.

### Không tìm thấy thiết bị hoặc offline

ADB đang tắt trong giả lập, cổng trên thẻ thiết bị sai, hoặc một máy chủ adb khác đã chiếm quyền. Làm theo [Khắc phục sự cố ADB](/docs/adb-troubleshooting).

Nếu ứng dụng bạn điều khiển là game, hãy đối xử với nó cẩn thận như với mọi tài khoản tự động khác. Không có công cụ tự động hóa nào an toàn 100%, vì vậy hãy tự động hóa một cách có trách nhiệm và theo quyết định của riêng bạn.
