Creating an Android Driver — Session Lifecycle in Python

6 min read

webdriver.Remote with UiAutomator2Options creates an Android session. The returned object is typed as WebDriver but exposes Android-specific commands through Appium's execute_script interface and specialized methods available after importing from appium.webdriver.extensions.

Creating the driver

from appium import webdriver
from appium.options import UiAutomator2Options
import os
 
def create_android_driver() -> webdriver.Remote:
    options = UiAutomator2Options()
    options.device_name = os.getenv("ANDROID_DEVICE", "emulator-5554")
    options.platform_version = os.getenv("ANDROID_VERSION", "13")
    options.app = os.path.abspath("apps/app-debug.apk")
    options.auto_grant_permissions = True
    options.no_reset = True
    options.new_command_timeout = 60
 
    return webdriver.Remote("http://127.0.0.1:4723", options=options)

App lifecycle

driver = create_android_driver()
 
# Check if installed
installed = driver.is_app_installed("com.example.myapp")
 
# Activate (bring to foreground)
driver.activate_app("com.example.myapp")
 
# Run in background for N seconds
driver.background_app(3)
 
# Terminate
driver.terminate_app("com.example.myapp")
 
# Remove
driver.remove_app("com.example.myapp")

Hardware key simulation

from appium.webdriver.extensions.android.nativekey import AndroidKey
 
# Back button
driver.press_keycode(AndroidKey.BACK)
 
# Home button
driver.press_keycode(AndroidKey.HOME)
 
# Recent apps
driver.press_keycode(AndroidKey.APP_SWITCH)
 
# Volume up/down
driver.press_keycode(AndroidKey.VOLUME_UP)
driver.press_keycode(AndroidKey.VOLUME_DOWN)

Clipboard

# Set clipboard text (useful for paste-flow testing)
driver.set_clipboard_text("paste this value")
 
# Read clipboard
text = driver.get_clipboard_text()
print(f"Clipboard: {text}")

Network simulation

# Toggle WiFi
driver.toggle_wifi()
 
# Toggle airplane mode
driver.toggle_airplane_mode()
 
# Get connection state
state = driver.network_connection
print(f"Connection state: {state}")
# 0 = airplane mode, 1 = WiFi off, 2 = WiFi only, 4 = data only, 6 = all

Checking current activity (debugging locator failures)

When a NoSuchElementException occurs, print the current activity to confirm you're on the right screen:

activity = driver.current_activity
package = driver.current_package
print(f"Current: {package}/{activity}")
# Output: Current: com.example.myapp/.LoginActivity

If the activity doesn't match what your page object expects, the navigation failed before the locator was attempted.

Screenshot and screen recording

import base64
 
# Screenshot
driver.get_screenshot_as_file("screenshots/current_state.png")
 
# Or get bytes for Allure attachment
screenshot_bytes = driver.get_screenshot_as_png()
 
# Screen recording (Android 4.4+)
driver.start_recording_screen()
# ... run test steps ...
video_base64 = driver.stop_recording_screen()
video_bytes = base64.b64decode(video_base64)
 
import pathlib
pathlib.Path("recordings/test.mp4").write_bytes(video_bytes)

Running shell commands via ADB

For setup operations that are faster via ADB than through the app UI:

# Grant a permission directly
driver.execute_script("mobile: shell", {
    "command": "pm grant com.example.app android.permission.ACCESS_FINE_LOCATION"
})
 
# Clear app data (faster than full reset)
driver.execute_script("mobile: shell", {
    "command": "pm clear com.example.app"
})
 
# Get device property
result = driver.execute_script("mobile: shell", {
    "command": "getprop ro.build.version.release"
})
print(f"Android version: {result}")

Typical conftest.py fixture

# conftest.py
import pytest
from appium import webdriver
from appium.options import UiAutomator2Options
 
@pytest.fixture(scope="function")
def android_driver():
    options = UiAutomator2Options()
    options.device_name = "emulator-5554"
    options.app = "apps/app-debug.apk"
    options.auto_grant_permissions = True
    options.no_reset = True
 
    driver = webdriver.Remote("http://127.0.0.1:4723", options=options)
    yield driver
    
    try:
        driver.quit()
    except Exception:
        pass  # Session may already be dead if the test crashed it

The try/except around driver.quit() prevents teardown failures from masking the real test failure.

// tip to track lessons you complete and pick up where you left off across devices.