PythonでAndroidを制御!ADBコマンド自動化の基礎から応用まで(コード付き)

プログラミング

前回の記事では、PythonとADBを組み合わせるメリットと、自動化を始めるための環境構築方法について解説しました。環境の準備が整った今、いよいよ本記事では具体的なADBコマンドをPythonスクリプトで自動化する方法に焦点を当てていきます。

ここでは、開発やテストで頻繁に利用されるADBコマンドを厳選し、adb-shellライブラリを使った実践的なコード例を豊富に提供します。

  • アプリのインストール・アンインストール
  • ファイルのプッシュ・プル
  • スクリーンショットの取得シェルコマンドの実行

など実際の現場で役立つ自動化レシピを通じて、PythonによるAndroid操作の可能性を実感してください。


スポンサーリンク

基本的な接続と操作の準備

adb-shellライブラリを使ってAndroidデバイスに接続し、基本的な操作を行うための準備を行います。前提として、adb serverがPCで起動している必要があります。

Python

from adb_shell.adb_device import AdbDevice
from adb_shell.auth.sign_pure_python import AdbKey # 認証が必要な場合
import time
import subprocess # adbコマンドを直接実行するために使用

# adb serverが起動していることを確認・起動
# コマンドプロンプト/ターミナルで `adb start-server` を手動で実行しておくことを推奨します。
# またはPythonから subprocess を使って起動することも可能です。
# subprocess.run("adb start-server", shell=True)

# デバイスへの接続設定
# 通常は localhost:5037 で adb server に接続します。
# デバイスがPCに接続され、USBデバッグが有効になっている必要があります。
# 認証が必要な場合(通常は初回接続時):
# key_path = '/path/to/your/.android/adbkey' # 環境に合わせてパスを修正
# with open(key_path, 'rb') as f:
#     signer = AdbKey(f.read())
# device = AdbDevice(host='127.0.0.1', port=5037)
# device.connect(rsa_keys=[signer], auth_timeout_s=0.1)

# 認証が不要、または事前に認証済みの場合(一般的なケース):
try:
    device = AdbDevice(host='127.0.0.1', port=5037)
    device.connect(rsa_keys=None, auth_timeout_s=0.1)
    if device.available:
        print("デバイスに正常に接続しました。")
    else:
        print("デバイスに接続できませんでした。USBデバッグの有効化、adb serverの起動を確認してください。")
except Exception as e:
    print(f"接続エラーが発生しました: {e}")

  • 注意点

adb-shellはデバイスとの直接的な通信を扱うため、Pythonスクリプトを実行するPC上でadb start-serverが実行されている必要があります。また、デバイスがPCに適切に接続され、USBデバッグが有効になっていることを確認してください。


Pythonで実践!よく使うADBコマンド自動化レシピ

それでは、具体的なADBコマンドの自動化レシピを見ていきましょう。

デバイス接続の確認とリスト表示

adb-shellは単一デバイスへの接続を前提としていますが、subprocessモジュールを使えば、接続されているデバイスのリストを取得できます。

Python

def get_connected_devices():
    """接続されているADBデバイスのシリアル番号と状態をリストで取得します。"""
    command = "adb devices"
    process = subprocess.run(command, shell=True, capture_output=True, text=True, encoding='utf-8')
    
    if process.returncode == 0:
        output_lines = process.stdout.strip().split('\n')
        devices = []
        if len(output_lines) > 1: # ヘッダー行を除外
            for line in output_lines[1:]:
                if line.strip():
                    parts = line.split('\t')
                    if len(parts) == 2:
                        devices.append({"serial": parts[0], "status": parts[1]})
        return devices
    else:
        print(f"adb devicesコマンドの実行に失敗しました: {process.stderr}")
        return []

# 接続されているデバイスをリストアップ
connected_devices = get_connected_devices()
if connected_devices:
    print("接続されているデバイス:")
    for dev in connected_devices:
        print(f"  シリアル: {dev['serial']}, 状態: {dev['status']}")
else:
    print("接続されているデバイスはありません。")

# シリアル番号を指定して接続したい場合(複数のデバイスがある場合)
# device = AdbDevice(host='127.0.0.1', port=5037, serial='YOUR_DEVICE_SERIAL')
# device.connect(...)

アプリケーションのインストール・アンインストール

APKファイルのインストールや、既存アプリのアンインストールを行います。adb-shellには直接インストールする高レベルなAPIがないため、subprocessモジュールを使ってadb installコマンドを直接呼び出すのが一般的です。

Python

def install_app(apk_path):
    """指定されたAPKファイルをデバイスにインストールします。"""
    command = f"adb install \"{apk_path}\"" # パスにスペースが含まれる可能性があるためクォート
    print(f"アプリ {apk_path} をインストール中...")
    process = subprocess.run(command, shell=True, capture_output=True, text=True, encoding='utf-8')
    if process.returncode == 0:
        print(f"アプリ '{apk_path}' のインストールに成功しました。")
        print(process.stdout)
    else:
        print(f"アプリ '{apk_path}' のインストールに失敗しました: {process.stderr}")

def uninstall_app(package_name):
    """指定されたパッケージ名のアプリをデバイスからアンインストールします。"""
    command = f"adb uninstall {package_name}"
    print(f"アプリ {package_name} をアンインストール中...")
    process = subprocess.run(command, shell=True, capture_output=True, text=True, encoding='utf-8')
    if process.returncode == 0:
        print(f"アプリ '{package_name}' のアンインストールに成功しました。")
        print(process.stdout)
    else:
        print(f"アプリ '{package_name}' のアンインストールに失敗しました: {process.stderr}")

# 使用例:
# install_app("C:/Users/YourUser/Downloads/your_app.apk")
# uninstall_app("com.example.your_package_name")

ファイルのプッシュ・プル

PCとデバイス間でファイルをやり取りします。これはadb-shellライブラリの得意分野です。

Python

# PCからデバイスへファイルをプッシュ (adb push)
local_file_path = 'local_data.txt' # PC上のファイルパス
remote_file_path = '/sdcard/Download/remote_data.txt' # デバイス上のパス

try:
    # テスト用のファイルをPCに作成
    with open(local_file_path, 'w', encoding='utf-8') as f:
        f.write("これはPythonからプッシュされるテストファイルです。\n")
        f.write("Hello Android Automation!")

    device.push(local_file_path, remote_file_path)
    print(f"ファイル '{local_file_path}' をデバイスの '{remote_file_path}' にプッシュしました。")
except Exception as e:
    print(f"ファイルプッシュに失敗しました: {e}")

# デバイスからPCへファイルをプル (adb pull)
pulled_local_path = 'pulled_data.txt' # PCに保存するパス
pulled_remote_path = '/sdcard/Download/remote_data.txt' # デバイス上のパス

try:
    device.pull(pulled_remote_path, pulled_local_path)
    print(f"デバイスの '{pulled_remote_path}' からファイル '{pulled_local_path}' をプルしました。")
    with open(pulled_local_path, 'r', encoding='utf-8') as f:
        print("プルしたファイルの内容:")
        print(f.read())
except Exception as e:
    print(f"ファイルプルに失敗しました: {e}")

スクリーンショットの取得

デバイスのスクリーンショットを撮り、PCに保存します。これはシェルコマンドの実行とファイルのプルを組み合わせます。

Python

def take_screenshot(device, local_save_path="screenshot.png"):
    """デバイスのスクリーンショットを取得し、PCに保存します。"""
    remote_screenshot_path = '/sdcard/temp_screenshot.png'
    
    try:
        # デバイス上でスクリーンショットを撮影し、一時的に保存
        print(f"デバイスでスクリーンショットを撮影中...")
        device.shell(f'screencap -p {remote_screenshot_path}')
        time.sleep(1) # スクリーンショットが保存されるまで少し待つ
        print(f"スクリーンショットを '{remote_screenshot_path}' に保存しました。")

        # PCにプル
        device.pull(remote_screenshot_path, local_save_path)
        print(f"スクリーンショットをPCにプルしました: '{local_save_path}'")

        # デバイス上の一時ファイルを削除
        device.shell(f'rm {remote_screenshot_path}')
        print(f"デバイス上の一時ファイル '{remote_screenshot_path}' を削除しました。")
    except Exception as e:
        print(f"スクリーンショットの取得またはプルに失敗しました: {e}")

# 使用例:
# take_screenshot(device, "my_app_screenshot.png")

シェルコマンドの実行

任意のシェルコマンドをデバイス上で実行します。これはadb-shellの主要な機能です。

Python

# デバイスのプロパティを取得
result = device.shell('getprop ro.build.version.release')
print(f"Androidバージョン: {result.strip()}")

# インストールされているパッケージの一覧を取得
result = device.shell('pm list packages -f') # -f オプションでAPKファイルのパスも表示
print("\nインストールされているパッケージ (パス付き):")
for line in result.splitlines():
    if line.strip():
        print(f"- {line.strip()}")

# テキスト入力のシミュレーション(例:検索ボックスにテキストを入力)
# 注意:特定のアプリの入力フィールドにフォーカスがある必要があります
# device.shell("input text 'Hello Automation'")
# print("デバイスに 'Hello Automation' と入力しました。")

# キーイベントの送信(例:HOMEボタンを押す)
# keyeventのコードはAndroid開発者サイトを参照
# device.shell("input keyevent 3") # KEYCODE_HOME
# print("HOMEボタンを押しました。")

# 画面タップのシミュレーション(X, Y座標)
# device.shell("input tap 500 1000") # 例: 画面中央付近をタップ
# print("座標 (500, 1000) をタップしました。")

パッケージ情報の取得

特定のアプリの情報を取得したり、インストールされているパッケージをより詳細にリストアップしたりします。

Python

# 特定のパッケージの情報を取得
package_name_to_check = "com.android.chrome"
result = device.shell(f'dumpsys package {package_name_to_check} | grep "versionName"')
print(f"\n{package_name_to_check} のバージョン情報:")
if result.strip():
    print(result.strip())
else:
    print("情報が見つかりませんでした。パッケージ名を確認してください。")

# アプリの起動 (例: Google Chrome)
# 実行にはパッケージ名とActivity名を指定します。
# Activity名は `dumpsys package <package_name> | grep -A 1 "Main activities"` などで確認できます。
# chrome_package = "com.android.chrome"
# chrome_activity = "com.google.android.apps.chrome.Main"
# device.shell(f'am start -n {chrome_package}/{chrome_activity}')
# print(f"アプリ {chrome_package} を起動しました。")

# デバイスとの接続を切断
if device.available:
    device.close()
    print("デバイスとの接続を切断しました。")

スポンサーリンク
スポンサーリンク

まとめ

本記事では、Pythonとadb-shellライブラリを用いて、Androidデバイスの基本的なADB操作を自動化する具体的なレシピを実践的に解説しました。

  • Pythonのsubprocessモジュールとadb-shellを組み合わせることで、デバイス情報の取得アプリのインストール・アンインストールファイルのプッシュ・プルといった一連の作業を効率的に自動化できます。
  • 特に、device.shell()メソッドを使えば、任意のシェルコマンドを実行し、デバイスを細かく制御することが可能です。これにより、スクリーンショットの取得やテキスト入力、キーイベントの送信など、多岐にわたる操作が実現できます。

これらの自動化レシピを組み合わせることで、日常の開発・テスト業務におけるAndroidデバイス操作の多くの部分をスクリプト化し、時間と労力を大幅に節約できます。次の記事では、ADB単体では難しいUI操作の自動化や、スクリプト開発中に遭遇しがちなトラブルシューティングについて、さらに掘り下げて解説します。より高度な自動化を目指す方は、ぜひ次の記事も参考にしてください。

スポンサーリンク

コメント

タイトルとURLをコピーしました