Skip to content

Parallel Devices

The DevicePool lets you run operations across multiple devices concurrently — useful for multi-device testing, fleet management, and parallel automation.

Create a Pool

from adbflow import ADB
from adbflow.parallel import DevicePool

async def create_pool():
    adb = ADB()

    # Get all connected devices
    entries = await adb.devices_async()
    devices = [await adb.device_async(serial=e.serial) for e in entries]

    pool = DevicePool(devices)
    print(f"Pool size: {len(pool)}")

Run on All Devices

async def parallel_run(pool):
    # Run a function on all devices in parallel
    async def get_model(device):
        return await device.info.model_async()

    results = await pool.run_async(get_model)
    for result in results:
        if isinstance(result, BaseException):
            print(f"Error: {result}")
        else:
            print(f"Model: {result}")

Sequential Execution

async def sequential_run(pool):
    # Run on devices one at a time
    async def install_app(device):
        await device.apps.install_async("/path/to/app.apk")

    results = await pool.run_sequential_async(install_app)

Filter Devices

async def filter_devices(pool):
    # Find devices matching a criteria
    async def is_samsung(device):
        manufacturer = await device.info.manufacturer_async()
        return "samsung" in manufacturer.lower()

    samsung_devices = await pool.filter_async(is_samsung)
    print(f"Found {len(samsung_devices)} Samsung devices")

    # Get the first matching device
    async def has_large_screen(device):
        size = await device.info.screen_size_async()
        return size and size.width >= 1440

    device = await pool.first_async(has_large_screen)
    if device:
        print(f"Large screen: {device.serial}")

Iterate Devices

async def iterate_pool(pool):
    for device in pool:
        model = await device.info.model_async()
        print(f"{device.serial}: {model}")

Common Patterns

Install app on all devices

async def install_everywhere(pool):
    async def install(device):
        await device.apps.install_async("/path/to/app.apk")
        await device.apps.start_async("com.example.app")

    await pool.run_async(install)

Screenshot all devices

async def screenshot_all(pool):
    async def capture(device):
        model = await device.info.model_async()
        await device.media.screenshot.capture_to_file_async(f"{model}.png")

    await pool.run_async(capture)

Run test across devices

async def multi_device_test(pool):
    async def test_login(device):
        await device.apps.start_async("com.example.app")
        await device.wait_for_text_async("Login", timeout=10.0)

        username = await device.ui.find_async(
            Selector().resource_id("com.example:id/username")
        )
        await username.tap_async()
        await username.text_input_async("test@test.com")

        await device.wait_for_text_async("Dashboard", timeout=10.0)
        return True

    results = await pool.run_async(test_login)
    passed = sum(1 for r in results if r is True)
    print(f"Passed: {passed}/{len(pool)}")

Tips

  • run_async executes in parallel using asyncio.gather. Errors from individual devices are returned as BaseException instances, not raised.
  • Use run_sequential_async when operations might conflict (e.g., ADB server resource contention).
  • filter_async returns Device objects, not a new pool. Wrap them in DevicePool() if needed.