# Python で adb を使い Android エミュレータを操作する

> Macro Automation Studio 向けの Python adb チュートリアル: BlueStacks でタップ、スワイプ、文字入力、キー操作、スクリーンショットを、adb の管理はアプリに任せて行います。

Source: https://automationmacro.com/ja/docs/guides/control-an-emulator-from-python (ガイド, updated 2026-09-05)

多くの Python adb チュートリアルは、`adb shell input tap` を包む `subprocess` のラッパーで終わります。Macro Automation Studio (MAS) 向けのこの Python adb チュートリアルは別の道を取ります: adb はアプリが持ち、スクリプトは `mas` パッケージを通してアプリと話します。このガイドでは、新しいプロジェクトから、アプリを開き、語句を検索し、スクロールしてスクリーンショットを保存するスクリプトまでを進みます。adb コマンドを書かずに BlueStacks、LDPlayer、MuMu Player、MEmu を操作したい Python 開発者向けです。

## 始める前に

- MAS がインストール済みで、トライアルかプランでサインインしていること。[MAS のインストール](/docs/install) を参照してください。
- エミュレータが ADB を有効にして動いており、**デバイスグループ** にデバイスとして追加されていること。[デバイス](/docs/devices) を参照してください。
- 基本的な Python を知っていること。他に自分でインストールするものはありません。MAS は `mas` 入りの独自の Python 3.13 環境を同梱しています。

## 各部品の組み合わさり方

MAS はプロジェクトフォルダーを作業ディレクトリとして、スクリプトを `python -u -m src.app` として起動します。接続情報は環境変数 (`MAS_RPC_PORT`、`MAS_RPC_HOST`、`MAS_DEVICE_ID`、`MAS_SESSION_TOKEN`) で渡します。すべての `mas.*` 呼び出しは、アプリへの JSON-RPC 2.0 リクエストになります。アプリが adb コマンド、テンプレート検索、OCR を実行し、結果を返します。デバイスは最初の行が動く前に結び付けられているので、`connect()` の呼び出しも、管理するシリアルもありません。[基本概念](/docs/concepts) を参照してください。

## プロジェクトを作る

1. **マクロ** を開き、**新しいプロジェクトを作成** をクリックします。
2. **コードベース** を選び、**対象デバイス** をモバイルにし、プロジェクト名を付けて **プロジェクトを作成** をクリックします。
3. プロジェクトを開きます。Code Editor に `src/app.py` が表示されます。
4. デバイスセレクターでエミュレータを選び、スニペットを試したいときはいつでも **実行** (<kbd>F5</kbd>) をクリックします。**停止** は <kbd>Shift</kbd>+<kbd>F5</kbd> です。

## 画面サイズと座標

SDK に渡すすべての座標は、デバイス画面の左上からの、デバイス自身の解像度でのピクセルオフセットです。モニター上のエミュレータウィンドウの大きさは関係ありません。

```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()` は `width` と `height` を持つ `ScreenSize` を返します。`get_device_info()` はそれに加えてデバイスの `name`、`type`、`connected` フラグを返します。スクリプトが解像度の変更に耐える必要があるときは、最後の例のように位置をサイズの割合で計算します。テンプレート画像と OCR 領域は耐えられません。[画像認識ガイド](/docs/guides/image-recognition-macros) を参照してください。

## 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()` は `Screenshot` を返します: `base64` は PNG を保持し、`width` と `height` はそのピクセルサイズ、`timestamp` は ISO 8601 の文字列です。上のファイルは、作業ディレクトリであるプロジェクトフォルダーに保存されます。同じオブジェクトを `screenshot=shot` で複数の `find_object` 呼び出しに渡すと、すべてが再取得せずに 1 つのフレームを検索します。

## タップ

```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)` は 1 度タップし、アプリが反応できるよう `delay_ms` の後に待ちます。タップに押し続ける時間はありません。長押しには `duration_ms` を付けた `key_press` を使います。

## スワイプ

```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)` は 2 つの `(x, y)` タプルを受け取ります。短い時間はフリック、長い時間はドラッグになります。ピンチ操作は別の呼び出しです: `zoom_in()` と `zoom_out()` は既定で画面中央に `percent=50` で行います。

## 文字入力

```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)` はフォーカスされた欄に入力するので、先に欄をタップしてください。`clear=True` はカーソルを末尾に移動し、入力前に既存の文字を削除します。

## キーを押す

```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)` は Android のキーイベントを送ります。`duration_ms` が 500 以上なら長押しになります。それを超える正確なミリ秒数は反映されません。`repeat` は 1 から 100 で、Python のループよりずっと速く動きます。`KeyCode` には D パッド、音量、メディア、数字のキーもあります。

## アプリを開いて確認する

```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)` はパッケージ名で起動し、`timeout_ms` 待ちます。パッケージ名を知るには、手でアプリを開いて `mas.get_current_app()` を出力します。`get_app_state` は `NOT_INSTALLED`、`NOT_RUNNING`、`RUNNING_IN_BACKGROUND_SUSPENDED`、`RUNNING_IN_BACKGROUND`、`RUNNING_IN_FOREGROUND` のいずれかを返します。`close_app` はパッケージを強制停止します。

## 完全なスクリプト

このスクリプトは Android の設定を開き、語句を検索し、結果をスクロールし、スクリーンショットを保存し、その語句が画面にあることを OCR で確認します。検索ボックスは Asset Lab で切り出すテンプレートです。ID を自分のものに置き換えてください。スワイプは画面サイズの割合を使うので、どの解像度でも動きます。

```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()
```

`psm=11` を付けた `read_text` はフレーム全体のまばらな文字を読むので、結果一覧に向いています。1 つのカウンターを読むための領域とモードは [OCR ガイド](/docs/guides/ocr-text-reading) で説明しています。書くより説明したい場合は、[MAS Agent](/agent) がこの種のスクリプトを代わりに書きます。

## この Python チュートリアルで adb が担う部分

adb を呼ぶことはありませんが、その下で仕事をしているのは adb です。

- **バイナリ。** インストーラーが adb を同梱します。Windows では `C:\ProgramData\MacroAutomationStudio\3rdparty` にあり、内蔵のコピーが予備です。Mac ではアプリバンドル内にあり、Homebrew の adb が予備です。PATH には何も入りません。詳細は [インストール](/docs/install) ページにあります。
- **接続。** エミュレータはローカルの TCP ポートで adb を公開します。**開始** か **実行** をクリックすると、MAS はデバイスカードのポートに対して `adb connect 127.0.0.1:<port>` を実行し、そのセッションを実行の間保ちます。
- **入力。** `click` は `adb shell input tap` に、`swipe` は `adb shell input touchscreen swipe` に、`input_text` は `adb shell input text` に、`key_press` は `adb shell input keyevent` になります。ピンチ操作はエミュレータのタッチ入力デバイスに直接書き込まれます。
- **画面。** `take_screenshot` とすべての `find_object` は、Windows で PNG を壊さないバイナリセーフな形式の `adb exec-out screencap -p` で取得します。`get_screen_size` は `wm size` を読み、`get_current_app` はウィンドウマネージャーを読みます。
- **アプリ。** `open_app` は `monkey` でランチャーインテントを発行し、`am start` にフォールバックします。`close_app` は `am force-stop` を実行します。
- **サーバー。** MAS は独自の adb サーバーを動かします。PATH 上の 2 つ目の adb ビルドが独自のサーバーを起動すると、両者が互いを置き換え、デバイスは Connecting に落ちます。[ADB トラブルシューティング](/docs/adb-troubleshooting) ページに、ポートごとの対処があります。

アプリがこれらすべてを行うので、BlueStacks で書いたスクリプトは、LDPlayer でも、クラウドデバイスでも、ローカルポート経由のお手持ちのスマホでも、そのまま動きます。

## うまくいかないとき

### Could not discover RPC port

スクリプトが MAS からではなくターミナルから起動されたため、環境変数がありません。Code Editor の **実行** か、デバイスカードから実行してください。

### タップが違う場所に落ちる

デバイスのピクセルではなくウィンドウのピクセルを使っているか、エミュレータの解像度が変わりました。`mas.get_screen_size()` を出力し、渡している座標と比べてください。Asset Lab の座標はすでにデバイスのピクセルです。

### input_text が何も入力しない

どの欄にもフォーカスがありませんでした。`click` で欄をタップし、入力前に `delay_ms=500` を与えます。一部のアプリは欄を覆うキーボードを開きます。入力後に `key_press(KeyCode.BACK)` で閉じます。

### デバイスが見つからない、または offline

エミュレータで ADB がオフ、デバイスカードのポートが間違っている、または別の adb サーバーが乗っ取りました。[ADB トラブルシューティング](/docs/adb-troubleshooting) に従ってください。

操作するアプリがゲームの場合は、他の自動化されたアカウントと同じ注意を払ってください。100%リスクのない自動化ツールは存在しないため、自動化は責任を持って、ご自身の判断で行ってください。
