Appium and WebDriver
Run droidline webdriver and point Appium clients at it. Existing Appium scripts in Python, Java, JavaScript and other languages drive your phones through Droidline, without ADB.
droidline webdriver speaks the W3C WebDriver protocol with Appium's Android additions. Point an Appium client at it, and scripts written for Appium drive your phones through Droidline: same locators, same element commands, no ADB and no Appium server.
It is optional and runs only while the command runs. Your Droidline scripts do not need it.
Start it
The Droidline server must be running. In a second terminal:
droidline webdriver
WebDriver bridge on http://127.0.0.1:4723
Use it as the Appium server URL. Stop with Ctrl+C.
Use http://127.0.0.1:4723 wherever your client asks for the Appium server. --listen picks another address; keep it on 127.0.0.1, because the bridge has no password.
Connect
# pip install Appium-Python-Client
from appium import webdriver
from appium.options.android import UiAutomator2Options
from appium.webdriver.common.appiumby import AppiumBy
opts = UiAutomator2Options()
opts.set_capability("appium:udid", "shelf-01")
opts.set_capability("appium:appPackage", "com.android.settings")
d = webdriver.Remote("http://127.0.0.1:4723", options=opts)
d.find_element(AppiumBy.XPATH, "//*[@text='Network & internet']").click()
d.find_element(AppiumBy.ANDROID_UIAUTOMATOR, 'new UiSelector().text("Wi-Fi")')
d.quit()import { remote } from "webdriverio";
const d = await remote({
hostname: "127.0.0.1",
port: 4723,
capabilities: { platformName: "Android", "appium:udid": "shelf-01" },
});
await d.$("~More options").click();
await d.deleteSession();The Appium Python client above was tested against this bridge on a phone. Other clients that speak the same protocol, such as WebdriverIO, the Java and .NET clients, Robot Framework's AppiumLibrary and Appium Inspector, connect the same way. When a client sends a command the bridge does not have, it answers unknown command and names it.
Capabilities
| Capability | What it does |
|---|---|
platformName | Android, or leave it out. |
appium:udid or droidline:device | The phone, by ID or name. Without one, the only online phone. |
appium:deviceName | Used only if a paired phone has that name or ID; anything else is ignored. |
appium:appPackage, appium:appActivity | Start this app. Nothing is reset or cleared. mobile: clearApp clears the app's data, in device owner mode only. |
appium:newCommandTimeout | End the session after this many seconds without a command. Default 60; 0 never. |
droidline:lease | true leases a free phone for the session and gives it back when the session ends. |
Locators
| Strategy | Matches |
|---|---|
xpath | XPath over the page source, which has the same tags and attributes as UiAutomator2 (@text, @resource-id, @content-desc, @class, @bounds, the flags). |
id | resource-id, full or just the name after :id/. |
accessibility id | content-desc. |
class name | The class, such as android.widget.Button. |
-android uiautomator | new UiSelector() with text, textContains, textStartsWith, textMatches, description and its variants, resourceId, className, packageName, the boolean flags, instance, childSelector and fromParent, plus new UiScrollable(...).scrollIntoView(...) and scrollTextIntoView(...). |
-droidline query | A Droidline query as JSON, for example {"class":"android.widget.Switch","row":{"text":"Wi-Fi"}}. |
name | text. |
The implicit wait you set with implicitly_wait applies to every strategy; it is 0 until you set it, as in WebDriver.
What works
- Elements: find one or many, also inside another element;
click,send_keys,clear,text,get_attribute,rect,is_displayed,is_enabled,is_selected, and element screenshots. An element that left the screen raises a stale element error. - Page source, screenshots and the window size.
- W3C actions with one finger: tap, long press and swipe. Key actions type text and press Enter, Backspace and Tab; Escape presses Back.
- App and device calls:
activate_app,query_app_state,press_keycode,hide_keyboard,is_keyboard_shown,current_activity,current_package,open_notifications,lock,unlock. - Scripts:
mobile: clearApp(device owner mode only),mobile: activateApp,mobile: pressKey,mobile: clickGesture,mobile: longClickGesture, andmobile: droidline, which runs any Droidline command:
d.execute_script("mobile: droidline", {"cmd": "wait_idle"})
d.execute_script("mobile: droidline", {"cmd": "proxy_check"})
What is different from Appium
Droidline does not use ADB, so anything that needs it is missing: installing and uninstalling apps, mobile: shell, pushing and pulling files, logcat, and WEBVIEW contexts. Also:
send_keysreplaces the field's text, as Droidline'sinputdoes.- Gestures use one finger; multi-touch actions fail.
appium:noResetandappium:fullResetare ignored. The bridge never clears data by itself.terminate_appandmobile: terminateAppanswer an unsupported operation error. To force stop an app, use the Settings recipe.mobile: clearApprunsclear_data, so it works only in device owner mode.