Technical Documentation: openatx/uiautomator2
ℹ️ Provenance: Multi-Tier Ingestion · CodeWiki Reference
Overview (`README.md`)
uiautomator2
A simple, easy-to-use, and stable Android automation library.
- QQ Group: 815453846
- Discord: https://discord.gg/PbJhnZJKDd
Users still on version 2.x.x, please check 2to3 before deciding to upgrade to 3.x.x (Upgrade is highly recommended).
How it Works
This framework mainly consists of two parts:
- Device Side: Runs an HTTP service based on UiAutomator, providing various interfaces for Android automation.
- Python Client: Communicates with the device side via HTTP protocol, invoking UiAutomator's various functions.
Simply put, it exposes Android automation capabilities to Python through HTTP interfaces. This design makes Python-side code writing simpler and more intuitive.
The Device Side run with a jar file which is also open source. see https://github.com/openatx/android-uiautomator-server-jar
Dependencies
- Android version 4.4+
- Python 3.8+
Installation
pip install uiautomator2
# Check if installation was successful, normally it will output the library version
uiautomator2 version
# or: python -m uiautomator2 versionInstall element inspection tool (optional, but highly recommended):
For more detailed usage instructions, refer to: https://github.com/uiautodev/uiautodev QQ:536481989
# Recommended: launch via npx (requires Node.js), it will automatically open the browser
npx uiautodev -open
# If Node.js is not installed, download the standalone binary for your platform
# from https://get.uiauto.dev and run it directly.
# Alternative: install the Python version via pip
pip install uiautodev
uiautodev
# or: python -m uiautodevAlternatives: uiautomatorviewer, Appium Inspector
Quick Start
Prepare an Android phone with Developer options enabled, connect it to the computer, and ensure that adb devices shows the connected device.
Open a Python interactive window. Then, input the following commands into the window.
import uiautomator2 as u2
d = u2.connect() # Specify device serial number if multiple devices are connected
print(d.info)
# Expected output
# {'currentPackageName': 'net.oneplus.launcher', 'displayHeight': 1920, 'displayRotation': 0, 'displaySizeDpX': 411, 'displaySizeDpY': 731, 'displayWidth': 1080, 'productName': 'OnePlus5', 'screenOn': True, 'sdkInt': 27, 'naturalOrientation': True}Example script:
import uiautomator2 as u2
d = u2.connect('Q5S5T19611004599')
d.app_start('tv.danmaku.bili', stop=True) # Start Bilibili
d.wait_activity('.MainActivityV2')
d.sleep(5) # Wait for splash screen ad to disappear
d.xpath('//*[@text="我的"]').click() # Click "My"
# Get fan count
fans_count = d.xpath('//*[@resource-id="tv.danmaku.bili:id/fans_count"]').text
print(f"Fan count: {fans_count}")Documentation
Connecting to Device
Method 1: Connect using device serial number, e.g., Q5S5T19611004599 (seen from adb devices)
import uiautomator2 as u2
d = u2.connect('Q5S5T19611004599') # alias for u2.connect_usb('123456f')
print(d.info)To connect on a non-default port (default is 9008):
d = u2.connect('Q5S5T19611004599', port=9009)Method 2: Serial number can be passed via environment variable ANDROID_SERIAL
# export ANDROID_SERIAL=Q5S5T19611004599
d = u2.connect()Method 3: Specify device via transport_id
$ adb devices -l
Q5S5T19611004599 device 0-1.2.2 product:ELE-AL00 model:ELE_AL00 device:HWELE transport_id:6Here you can see transport_id:6.
You can also get all connected transport_ids via
adbutils.adb.list(extended=True)
Refer to https://github.com/openatx/adbutils
import adbutils # Requires version >=2.9.1
import uiautomator2 as u2
dev = adbutils.device(transport_id=6)
d = u2.connect(dev)Operating Elements with XPath
What is XPath:
XPath is a query language for locating content in XML or HTML documents. It uses simple syntax rules to establish a path from the root node to the desired element.
Basic Syntax:
/- Select from the root node//- Select from any position starting from the current node.- Select the current node..- Select the parent of the current node@- Select attributes[]- Predicate expression, used for filtering conditions
You can quickly generate XPath using UIAutoDev.
Common Usage:
d.xpath('//*[@text="私人FM"]').click() # Click element with text "私人FM"
# Syntactic sugar
d.xpath('@personal-fm') # Equivalent to d.xpath('//*[@resource-id="personal-fm"]')
sl = d.xpath("@com.example:id/home_searchedit") # sl is an XPathSelector object
sl.click()
sl.click(timeout=10) # Specify timeout, throws XPathElementNotFoundError if not found
sl.click_exists() # Click if exists, returns whether click was successful
sl.click_exists(timeout=10) # Wait up to 10s
# Wait for the corresponding element to appear, returns XMLElement
# Default wait time is 10s
el = sl.wait()
el = sl.wait(timeout=15) # Wait 15s, returns None if not found
# Wait for element to disappear
sl.wait_gone()
sl.wait_gone(timeout=15)
# Similar to wait, but throws XPathElementNotFoundError if not found
el = sl.get()
el = sl.get(timeout=15)
sl.get_text() # Get component text
sl.set_text("") # Clear input field
sl.set_text("hello world") # Input "hello world" into input fieldFor more usage, refer to XPath Interface Document
Plugins
- webview: https://github.com/YuYoungG/uiautomator2-webview
- flutter: https://github.com/assassinaj602/u2_flutter - Flutter driver plugin, lets you locate and operate Flutter widgets (e.g.,
flutter.find_by_key("submit_btn").tap()) instead of the empty native view hierarchy. See proposal #1191.
To maintain the project's simplicity and extensibility, future plugins will be integrated as third-party libraries.
Operating Elements with UiAutomator API
Element Wait Timeout
Set element search wait time (default 20s)
d.implicitly_wait(10.0) # Can also be modified via d.settings['wait_timeout'] = 10.0
print("wait timeout", d.implicitly_wait()) # get default implicit wait
# Throws UiObjectNotFoundError if "Settings" does not appear in 10s
d(text="Settings").click()Wait timeout affects the following functions: click, long_click, drag_to, get_text, set_text, clear_text.
Get Device Information
Information obtained via UiAutomator:
d.info
# Output
{'currentPackageName': 'com.android.systemui',
'displayHeight': 1560,
'displayRotation': 0,
'displaySizeDpX': 360,
'displaySizeDpY': 780,
'displayWidth': 720,
'naturalOrientation': True,
'productName': 'ELE-AL00',
'screenOn': True,
'sdkInt': 29}Get device information (based on adb shell getprop command):
print(d.device_info)
# output
{'arch': 'arm64-v8a',
'brand': 'google',
'model': 'sdk_gphone64_arm64',
'sdk': 34,
'serial': 'EMULATOR34X1X19X0',
'version': 14}Get screen physical size (depends on adb shell wm size):
print(d.window_size())
# device upright output example: (1080, 1920)
# device horizontal output example: (1920, 1080)Get current App (depends on adb shell):
print(d.app_current())
# Output example 1: {'activity': '.Client', 'package': 'com.netease.example', 'pid': 23710}
# Output example 2: {'activity': '.Client', 'package': 'com.netease.example'}
# Output example 3: {'activity': None, 'package': None}Wait for Activity (depends on adb shell):
d.wait_activity(".ApiDemos", timeout=10) # default timeout 10.0 seconds
# Output: true or falseGet device serial number:
print(d.serial)
# output example: 74aAEDR428Z9Get device WLAN IP (depends on adb shell):
print(d.wlan_ip)
# output example: 10.0.0.1 or NoneClipboard
Set or get clipboard content.
clipboard/set_clipboard
python# Set clipboard d.clipboard = 'hello-world' # or d.set_clipboard('hello-world', 'label') # Get clipboard # Depends on input method (com.github.uiautomator/.AdbKeyboard) d.set_input_ime() print(d.clipboard)
Key Events
Turn on/off screen
pythond.screen_on() # turn on the screen d.screen_off() # turn off the screenGet current screen status
pythond.info.get('screenOn')Press hard/soft key
pythond.press("home") # press the home key, with key name d.press("back") # press the back key, with key name d.press(0x07, 0x02) # press keycode 0x07('0') with META ALT(0x02)These key names are currently supported:
- home
- back
- left
- right
- up
- down
- center
- menu
- search
- enter
- delete ( or del)
- recent (recent apps)
- volume_up
- volume_down
- volume_mute
- camera
- power
You can find all key code definitions at Android KeyEvent
Unlock screen
pythond.unlock() # This is equivalent to # 1. press("power") # 2. swipe from left-bottom to right-top
Gesture interaction with the device
Click on the screen
pythond.click(x, y)Double click
pythond.double_click(x, y) d.double_click(x, y, 0.1) # default duration between two clicks is 0.1sLong click on the screen
pythond.long_click(x, y) d.long_click(x, y, 0.5) # long click 0.5s (default)Swipe
pythond.swipe(sx, sy, ex, ey) d.swipe(sx, sy, ex, ey, 0.5) # swipe for 0.5s (default)SwipeExt (Extended functionality)
pythond.swipe_ext("right") # Swipe right, 4 options: "left", "right", "up", "down" d.swipe_ext("right", scale=0.9) # Default 0.9, swipe distance is 90% of screen width d.swipe_ext("right", box=(0, 0, 100, 100)) # Swipe within the area (0,0) -> (100, 100) # In practice, starting swipe from the midpoint for up/down swipes has a higher success rate d.swipe_ext("up", scale=0.8) # Can also use Direction as a parameter from uiautomator2 import Direction d.swipe_ext(Direction.FORWARD) # Scroll down page, equivalent to d.swipe_ext("up"), but easier to understand d.swipe_ext(Direction.BACKWARD) # Scroll up page d.swipe_ext(Direction.HORIZ_FORWARD) # Scroll page horizontally right d.swipe_ext(Direction.HORIZ_BACKWARD) # Scroll page horizontally leftDrag
pythond.drag(sx, sy, ex, ey) d.drag(sx, sy, ex, ey, 0.5) # drag for 0.5s (default)Swipe points
python# swipe from point(x0, y0) to point(x1, y1) then to point(x2, y2) # time will be 0.2s between two points d.swipe_points([(x0, y0), (x1, y1), (x2, y2)], 0.2)Often used for pattern unlock, get relative coordinates of each point beforehand (supports percentages).
For more detailed usage, refer to this post Using u2 to implement pattern unlockTouch and drag (Beta)
This is a lower-level raw interface, feels incomplete but usable. Note: percentages are not supported here.
pythond.touch.down(10, 10) # Simulate press down time.sleep(.01) # Delay between down and move, control it yourself d.touch.move(15, 15) # Simulate move d.touch.up(10, 10) # Simulate release
Note: click, swipe, drag operations support percentage position values. Example:
d.long_click(0.5, 0.5) means long click center of screen.
Screen Related APIs
Retrieve/Set device orientation
The possible orientations:
naturalornleftorlrightorrupsidedownoru(cannot be set)
python# retrieve orientation. the output could be "natural" or "left" or "right" or "upsidedown" orientation = d.orientation # WARNING: did not pass testing on my TT-M1 # set orientation and freeze rotation. # notes: setting "upsidedown" requires Android>=4.3. d.set_orientation('l') # or "left" d.set_orientation("r") # or "right" d.set_orientation("n") # or "natural"Freeze/Un-freeze rotation
python# freeze rotation d.freeze_rotation() # un-freeze rotation d.freeze_rotation(False)Take screenshot
python# take screenshot and save to a file on the computer, requires Android>=4.2. d.screenshot("home.jpg") # get PIL.Image formatted images. Naturally, you need Pillow installed first image = d.screenshot() # default format="pillow" image.save("home.jpg") # or home.png. Currently, only png and jpg are supported # get OpenCV formatted images. Naturally, you need numpy and cv2 installed first import cv2 image = d.screenshot(format='opencv') cv2.imwrite('home.jpg', image) # get raw jpeg data imagebin = d.screenshot(format='raw') with open("some.jpg", "wb") as f: f.write(imagebin)Dump UI hierarchy
python# get the UI hierarchy dump content xml = d.dump_hierarchy() # compressed=True: include non-important nodes (default False) # pretty: format xml (default False) # max_depth: limit xml depth, default 50 xml = d.dump_hierarchy(compressed=False, pretty=True, max_depth=30) # root_in_active=True: dump only the active window root xml = d.dump_hierarchy(root_in_active=True)Open notification or quick settings
pythond.open_notification() d.open_quick_settings()Show touch trace on device screen
python# show touch trace on device screen d.show_touch_trace() # hide touch trace d.show_touch_trace(pointer_location=False, show_touches=False)
Selector
Selector is a handy mechanism to identify a specific UI object in the current window.
# Select the object with text 'Clock' and its className is 'android.widget.TextView'
d(text='Clock', className='android.widget.TextView')Selector supports the below parameters. Refer to UiSelector Java doc for detailed information.
text,textContains,textMatches,textStartsWithclassName,classNameMatchesdescription,descriptionContains,descriptionMatches,descriptionStartsWithcheckable,checked,clickable,longClickablescrollable,enabled,focusable,focused,selectedpackageName,packageNameMatchesresourceId,resourceIdMatchesindex,instance
Children and siblings
children
python# get the children or grandchildren d(className="android.widget.ListView").child(text="Bluetooth")siblings
python# get siblings d(text="Google").sibling(className="android.widget.ImageView")children by text or description or instance
python# get the child matching the condition className="android.widget.LinearLayout" # and also its children or grandchildren with text "Bluetooth" d(className="android.widget.ListView", resourceId="android:id/list") \ .child_by_text("Bluetooth", className="android.widget.LinearLayout") # get children by allowing scroll search d(className="android.widget.ListView", resourceId="android:id/list") \ .child_by_text( "Bluetooth", allow_scroll_search=True, # default False className="android.widget.LinearLayout" )child_by_descriptionis to find children whose grandchildren have
the specified description, other parameters being similar tochild_by_text.child_by_instanceis to find children which have a child UI element anywhere
within its sub-hierarchy that is at the instance specified. It is performed
on visible views without scrolling.
See below links for detailed information:
- UiScrollable,
getChildByDescription,getChildByText,getChildByInstance - UiCollection,
getChildByDescription,getChildByText,getChildByInstance
Above methods support chained invoking, e.g. for the below hierarchy:
xml<node index="0" text="" resource-id="android:id/list" class="android.widget.ListView" ...> <node index="0" text="WIRELESS & NETWORKS" resource-id="" class="android.widget.TextView" .../> <node index="1" text="" resource-id="" class="android.widget.LinearLayout" ...> <node index="1" text="" resource-id="" class="android.widget.RelativeLayout" ...> <node index="0" text="Wi‑Fi" resource-id="android:id/title" class="android.widget.TextView" .../> </node> <node index="2" text="ON" resource-id="com.android.settings:id/switchWidget" class="android.widget.Switch" .../> </node> ... </node>To click the switch widget right to the TextView 'Wi‑Fi', we need to select the switch widget first. However, according to the UI hierarchy, more than one switch widget exists and has almost the same properties. Selecting by className will not work. Alternatively, the below selecting strategy would help:
pythond(className="android.widget.ListView", resourceId="android:id/list") \ .child_by_text("Wi‑Fi", className="android.widget.LinearLayout") \ .child(className="android.widget.Switch") \ .click()relative positioning
Also, we can use the relative positioning methods to get the view:
left,right,up,down.d(A).left(B), selects B on the left side of A.d(A).right(B), selects B on the right side of A.d(A).up(B), selects B above A.d(A).down(B), selects B under A.
So for the above cases, we can alternatively select it with:
python## select "switch" on the right side of "Wi‑Fi" d(text="Wi‑Fi").right(className="android.widget.Switch").click()Multiple instances
Sometimes the screen may contain multiple views with the same properties, e.g. text. Then you will
have to use the "instance" property in the selector to pick one of the qualifying instances, like below:pythond(text="Add new", instance=0) # which means the first instance with text "Add new"In addition, uiautomator2 provides a list-like API (similar to jQuery):
python# get the count of views with text "Add new" on current screen print(d(text="Add new").count) # same as count property print(len(d(text="Add new"))) # get the instance via index obj = d(text="Add new")[0] obj = d(text="Add new")[1] # ... # iterator for view in d(text="Add new"): print(view.info) # ...Notes: when using selectors in a code block that walks through the result list, you must ensure that the UI elements on the screen
remain unchanged. Otherwise, an Element-Not-Found error could occur when iterating through the list.
Get the selected UI object status and its information
Check if the specific UI object exists
pythonif d(text="Settings").exists: # True if exists, else False print("Settings button exists") # alias of above property. if d.exists(text="Settings"): print("Settings button exists") # advanced usage if d(text="Settings").exists(timeout=3): # wait for Settings to appear in 3s, same as .wait(3) print("Settings button appeared within 3 seconds")Retrieve the info of the specific UI object
pythoninfo = d(text="Settings").info print(info)Below is a possible output:
json{ "contentDescription": "", "checked": false, "scrollable": false, "text": "Settings", "packageName": "com.android.launcher", "selected": false, "enabled": true, "bounds": { "top": 385, "right": 360, "bottom": 585, "left": 200 }, "className": "android.widget.TextView", "focused": false, "focusable": true, "clickable": true, "childCount": 0, "longClickable": true, "visibleBounds": { "top": 385, "right": 360, "bottom": 585, "left": 200 }, "checkable": false }Get/Set/Clear text of an editable field (e.g., EditText widgets)
pythontext_content = d(className="android.widget.EditText").get_text() # get widget text d(className="android.widget.EditText").set_text("My text...") # set the text d(className="android.widget.EditText").clear_text() # clear the textGet Widget center point
pythonx, y = d(text="Settings").center() # x, y = d(text="Settings").center(offset=(0, 0)) # left-top x, yTake screenshot of widget
pythonim = d(text="Settings").screenshot() im.save("settings.jpg")
Perform the click action on the selected UI object
Perform click on the specific object
python# click on the center of the specific ui object d(text="Settings").click() # wait for element to appear for at most 10 seconds and then click d(text="Settings").click(timeout=10) # click with offset(x_offset_ratio, y_offset_ratio) from top-left of the element # click_x = x_offset_ratio * width + x_left_top # click_y = y_offset_ratio * height + y_left_top d(text="Settings").click(offset=(0.5, 0.5)) # Default: center d(text="Settings").click(offset=(0, 0)) # click left-top d(text="Settings").click(offset=(1, 1)) # click right-bottom # click if exists within 10s, default timeout 0s clicked = d(text='Skip').click_exists(timeout=10.0) # returns bool # click until element is gone, return bool is_gone = d(text="Skip").click_gone(maxretry=10, interval=1.0) # maxretry default 10, interval default 1.0sPerform long click on the specific UI object
python# long click on the center of the specific UI object d(text="Settings").long_click() # long click with duration d(text="Settings").long_click(duration=1.0) # duration in seconds, default 0.5s
Gesture actions for the specific UI object
Drag the UI object towards another point or another UI object
python# notes : drag cannot be used for Android<4.3. # drag the UI object to a screen point (x, y), in 0.5 seconds d(text="Settings").drag_to(x, y, duration=0.5) # drag the UI object to (the center position of) another UI object, in 0.25 seconds d(text="Settings").drag_to(text="Clock", duration=0.25)Swipe from the center of the UI object to its edge
Swipe supports 4 directions:
leftrightup(Previously 'top')down(Previously 'bottom')
pythond(text="Settings").swipe("right") d(text="Settings").swipe("left", steps=10) # steps control smoothness/speed d(text="Settings").swipe("up", steps=20) # 1 step is about 5ms, so 20 steps is about 0.1s d(text="Settings").swipe("down", steps=20)Two-point gesture from one pair of points to another (for pinch/zoom)
python# ((start_x1, start_y1), (start_x2, start_y2)) are initial touch points # ((end_x1, end_y1), (end_x2, end_y2)) are final touch points # steps is the number of move steps to take d(text="Settings").gesture((sx1, sy1), (sx2, sy2), (ex1, ey1), (ex2, ey2), steps=100)Two-point gesture on the specific UI object (pinch in/out)
Supports two gestures:
in: from edge to center (pinch in)out: from center to edge (pinch out)
python# notes : pinch cannot be set until Android 4.3. # from edge to center. d(text="Settings").pinch_in(percent=100, steps=10) # percent of object size, steps for smoothness # from center to edge d(text="Settings").pinch_out(percent=100, steps=10)Wait until the specific UI appears or disappears
python# wait until the ui object appears appeared = d(text="Settings").wait(timeout=3.0) # return bool if appeared: print("Settings appeared") # wait until the ui object is gone gone = d(text="Settings").wait_gone(timeout=1.0) # return bool if gone: print("Settings disappeared")The default timeout is 20s. See Global Settings for more details.
Perform fling on the specific UI object (scrollable)
Possible properties:
horizontalorvertical(orhoriz,vert)forwardorbackwardortoBeginningortoEnd
python# fling forward(default) vertically(default) d(scrollable=True).fling() # fling forward horizontally d(scrollable=True).fling.horizontal.forward() # fling backward vertically d(scrollable=True).fling.vertical.backward() # fling to beginning horizontally d(scrollable=True).fling.horizontal.toBeginning(max_swipes=1000) # fling to end vertically d(scrollable=True).fling.vertical.toEnd()Perform scroll on the specific UI object (scrollable)
Possible properties:
horizontalorvertical(orhoriz,vert)forwardorbackwardortoBeginningortoEnd, orto(selector)
python# scroll forward(default) vertically(default) d(scrollable=True).scroll(steps=10) # scroll forward horizontally d(scrollable=True).scroll.horizontal.forward(steps=100) # scroll backward vertically d(scrollable=True).scroll.vertical.backward() # scroll to beginning horizontally d(scrollable=True).scroll.horizontal.toBeginning(steps=100, max_swipes=1000) # scroll to end vertically d(scrollable=True).scroll.vertical.toEnd() # scroll forward vertically until specific ui object appears d(scrollable=True).scroll.vertical.to(text="Security")
Input Method (IME)
IME APK: https://github.com/openatx/android-uiautomator-server/releases (Install this for reliable text input)
d.send_keys("Hello123abcEFG") # Send text
d.send_keys("Hello123abcEFG", clear=True) # Clear existing text then send
d.clear_text() # Clear all content in the input field
# Automatically performs Enter, Search, etc., based on input field requirements. Added in version 3.1
d.send_action()
# Can also specify the IME action, e.g., d.send_action("search"). Supports go, search, send, next, done, previous.
d.hide_keyboard() # Hide the soft keyboardWhen send_keys is used, it prioritizes using the clipboard for input. If the clipboard interface is unavailable, it will attempt to install and use an auxiliary IME.
print(d.current_ime()) # Get current IME ID (package/class)For more, refer to: IME_ACTION_CODE
Toast
last_toast_message = d.toast.get_message(wait_timeout=5, default=None) # Get last toast message text within 5s
print(last_toast_message)
d.toast.reset() # Clear last toast message cache
# d.toast.show("Hello", duration=3) # Show a toast (requires special permissions)WatchContext (Deprecated)
Note: This interface is not highly recommended. It's better to check for pop-ups before clicking elements.
The current watch_context uses threading and checks every 2 seconds.
Currently, only click is a trigger operation.
with d.watch_context() as ctx:
# When "Download Now" or "Update Now" and "Cancel" buttons appear simultaneously, click "Cancel"
ctx.when("^(立即下载|立即更新)$").when("取消").click()
ctx.when("同意").click()
ctx.when("确定").click()
# The above three lines execute immediately without waiting.
ctx.wait_stable() # Start pop-up monitoring and wait for the interface to stabilize (stable if no pop-ups in two check cycles)
# Use the call function to trigger a callback
# call supports two parameters, d and el, order doesn't matter, can be omitted. If passed, variable names must be correct.
# e.g., When an element matching "Midsummer Night" appears, click the back button
ctx.when("仲夏之夜").call(lambda d: d.press("back"))
ctx.when("确定").call(lambda el: el.click())
# Other operations
# For convenience, you can also use the default pop-up monitoring logic in the code
# Below is the current built-in default logic. You can join the group and @ the owner to add new logic, or submit a PR directly.
# when("继续使用").click()
# when("移入管控").when("取消").click()
# when("^(立即下载|立即更新)$").when("取消").click()
# when("同意").click()
# when("^(好的|确定)$").click()
with d.watch_context(builtin=True) as ctx:
# Add on top of existing logic
ctx.when("@tb:id/jview_view").when('//*[@content-desc="图片"]').click()
# Other script logicAlternative way:
ctx = d.watch_context()
ctx.when("设置").click()
ctx.wait_stable() # Wait until the interface no longer has pop-ups
ctx.start() # if not using with statement
# ... do something ...
ctx.stop() # or ctx.close()Global Settings
import uiautomator2 as u2
u2.settings['HTTP_TIMEOUT'] = 60 # Default 60s, http default request timeoutOther configurations are mostly centralized in d.settings. Configurations may be added or removed based on future needs.
print(d.settings)
# Output example:
# {'operation_delay': (0, 0), # (before_op_delay, after_op_delay) in seconds
# 'operation_delay_methods': ['click', 'swipe'], # methods to apply delay
# 'wait_timeout': 20.0, # default element wait timeout (native operations, xpath plugin wait time)
# 'xpath_debug': False, # enable xpath debug
# 'xpath_timeout': 10.0 # default xpath wait timeout
# }
# Configure 0.5s delay before click, 1s delay after click
d.settings['operation_delay'] = (0.5, 1)
# Modify methods affected by delay
# double_click, long_click correspond to 'click'
d.settings['operation_delay_methods'] = ['click', 'swipe', 'drag', 'press']
d.settings['wait_timeout'] = 20.0 # Default control wait time
d.settings['max_depth'] = 50 # Default 50, limits dump_hierarchy returned element depthWhen settings are deprecated due to version upgrades, a DeprecatedWarning will be shown, but no exception will be raised.
>>> d.settings['click_before_delay'] = 1
# [W 200514 14:55:59 settings:72] d.settings['click_before_delay'] is deprecated: Use operation_delay insteadUiAutomator timeout settings (hidden methods):
>>> d.jsonrpc.setConfigurator({"waitForIdleTimeout": 100, "waitForSelectorTimeout": 0})
# Check current configurator settings
>>> print(d.jsonrpc.getConfigurator())
# {'actionAcknowledgmentTimeout': 3000,
# 'keyInjectionDelay': 0,
# 'scrollAcknowledgmentTimeout': 200,
# 'waitForIdleTimeout': 100,
# 'waitForSelectorTimeout': 0}To prevent client program timeouts, waitForIdleTimeout and waitForSelectorTimeout are currently set to 0 by default by uiautomator2 itself (not by the underlying uiautomator server).
Refs: Google uiautomator Configurator
Application Management
This part showcases how to perform app management.
Install Application
We only support installing an APK from a URL or local path.
# From URL
d.app_install('http://some-domain.com/some.apk')
# From local path
# d.app_install('/path/to/your/app.apk') # This functionality might depend on adbutils or direct adb calls.
# For local path, usually you'd use adbutils:
# adb = adbutils.AdbClient(host="127.0.0.1", port=5037)
# device = adb.device(serial=d.serial)
# device.install("/path/to/your/app.apk")
# Or ensure atx-agent is installed and use its features if available.
# The simplest way with uiautomator2 if atx-agent is present:
# d.shell(['pm', 'install', '/path/to/app.apk']) # if apk is already on device
# d.push('/local/path/app.apk', '/data/local/tmp/app.apk')
# d.shell(['pm', 'install', '/data/local/tmp/app.apk'])Start Application
# Default method: first parses APK's mainActivity via atx-agent, then calls am start -n $package/$activity
d.app_start("com.example.hello_world")
# Use monkey -p com.example.hello_world -c android.intent.category.LAUNCHER 1 to start
# This method has a side effect: it automatically turns off the phone's rotation lock.
d.app_start("com.example.hello_world", use_monkey=True) # start with package name
# Start app by specifying main activity, equivalent to calling am start -n com.example.hello_world/.MainActivity
d.app_start("com.example.hello_world", ".MainActivity")
# Stop app before starting
d.app_start("com.example.hello_world", stop=True)Stop Application
# equivalent to `am force-stop`, thus you could lose data
d.app_stop("com.example.hello_world")
# equivalent to `pm clear` (clears app data)
d.app_clear('com.example.hello_world')Stop All Applications
# stop all
d.app_stop_all()
# stop all apps except for com.examples.demo
d.app_stop_all(excludes=['com.examples.demo'])Get Application Information
info = d.app_info("com.example.demo")
print(info)
# expect output
# {
# "mainActivity": "com.github.uiautomator.MainActivity",
# "label": "ATX",
# "versionName": "1.1.7",
# "versionCode": 1001007,
# "size": 1760809 # size in bytes
# }
# save app icon
img = d.app_icon("com.example.demo") # Returns a PIL.Image object
if img:
img.save("icon.png")List All Running Applications
running_apps = d.app_list_running()
print(running_apps)
# expect output
# ["com.xxxx.xxxx", "com.github.uiautomator", "xxxx"]Wait for Application to Run
pid = d.app_wait("com.example.android") # Wait for app to run, returns pid (int) or 0 if timeout
if not pid:
print("com.example.android is not running")
else:
print(f"com.example.android pid is {pid}")
# Wait for app to be in the foreground
pid = d.app_wait("com.example.android", front=True)
if pid:
print("com.example.android is in foreground")
# Set custom timeout (default 20.0 seconds)
pid = d.app_wait("com.example.android", timeout=10.0)Push and Pull Files
Push a file to the device
python# push to a folder (src can be local path or BytesIO) d.push("foo.txt", "/sdcard/") # Pushes foo.txt to /sdcard/foo.txt # push and rename d.push("foo.txt", "/sdcard/bar.txt") # push fileobj import io with io.BytesIO(b"file content") as f: d.push(f, "/sdcard/from_io.txt") # push and change file access mode (mode is int, e.g., 0o755) d.push("foo.sh", "/data/local/tmp/", mode=0o755) # Pushes to /data/local/tmp/foo.shPull a file from the device
python# pull /sdcard/tmp.txt to local file tmp.txt d.pull("/sdcard/tmp.txt", "tmp.txt") # FileNotFoundError will raise if the file is not found on the device try: d.pull("/sdcard/some-file-not-exists.txt", "tmp.txt") except FileNotFoundError: print("File not found on device") # Pull file content as bytes # content_bytes = d.pull("/sdcard/tmp.txt") # This is not a standard feature, use sync.read_bytes for this # For reading content directly, use the sync object: # content = d.sync.read_bytes("/sdcard/tmp.txt")
Other Application Operations
# Grant all runtime permissions (requires Android 6.0+ and atx-agent)
# d.app_auto_grant_permissions("io.appium.android.apis") # This might be an older or specific helper
# A more common way to grant permissions is via adb shell:
# d.shell(['pm', 'grant', 'io.appium.android.apis', 'android.permission.READ_CONTACTS'])
# Open URL scheme
d.open_url("appname://appnamehost")
# same as
# adb shell am start -a android.intent.action.VIEW -d "appname://appnamehost"Session (Beta)
Session represents an app lifecycle. Can be used to start app, detect app crash.
Launch and close app
pythonsess = d.session("com.netease.cloudmusic") # Starts NetEase Cloud Music # ... perform operations within the session context ... # sess(text="Play").click() sess.close() # Stops NetEase Cloud Music # sess.restart() # Cold starts NetEase Cloud Music (stops then starts)Use python
withto launch and close apppythonwith d.session("com.netease.cloudmusic") as sess: # sess(text="Play").click() # App will be closed automatically when exiting the 'with' block passAttach to the running app
python# Launch app if not running, skip launch if already running sess = d.session("com.netease.cloudmusic", attach=True)Detect app crash
python# When app is still running # sess(text="Music").click() # operation goes normal # If app crashes or quits # sess(text="Music").click() # raises SessionBrokenError # other function calls under session will raise SessionBrokenError toopython# check if session is ok. # Warning: function name may change in the future if sess.running(): # True or False print("Session is active") else: print("Session is not active (app might have crashed or closed)")
Other APIs
Stop Background HTTP Service
Normally, when the Python program exits, the UiAutomator service on the device also exits.
However, you can also stop the service via an API call.
d.uiautomator.stop() # Stops the uiautomator service on the device
# or d.service("uiautomator").stop()Enable Debugging
Print out the HTTP request information behind the code.
>>> d.debug = True # This enables logging for uiautomator2 library
>>> print(d.info)
# Example output showing HTTP request/response
# 12:32:47.182 $ curl -X POST -d '{"jsonrpc": "2.0", "id": "...", "method": "deviceInfo"}' 'http://127.0.0.1:PORT/jsonrpc/0'
# 12:32:47.225 Response >>>
# {"jsonrpc":"2.0","id":"...","result":{...}}
# <<< ENDFor more structured logging:
import logging
from uiautomator2 import set_log_level
set_log_level(logging.DEBUG) # or logging.INFO
# Or configure manually
# logger = logging.getLogger("uiautomator2")
# logger.setLevel(logging.DEBUG)
# # setup handler, formatter etc.Command Line Functions
$device_ip represents the device's IP address.
To specify a device, pass --serial, e.g., python -m uiautomator2 --serial bff1234 <SubCommand>. SubCommand can be screenshot, current, etc.
To use a non-default uiautomator2 server port, pass -p/--port after the subcommand, e.g., uiautomator2 screenshot -p 9009 screenshot.jpg. The default port is 9008.
1.0.3 Added:
python -m uiautomator2is equivalent touiautomator2
screenshot: Take a screenshotbashuiautomator2 screenshot screenshot.jpg # With specific device uiautomator2 --serial <YOUR_DEVICE_SERIAL> screenshot screenshot.jpgcopy-assets: Copy assets/ to current directoryUsed for pyinstaller、nuitka
current: Get current package name and activitybashuiautomator2 current # Output example: # { # "package": "com.android.settings", # "activity": ".Settings", # "pid": 12345 # }uninstall: Uninstall appbashuiautomator2 uninstall <package-name> # Uninstall one package uiautomator2 uninstall <package-name-1> <package-name-2> # Uninstall multiple packages # uiautomator2 uninstall --all # Uninstall all third-party apps (Be careful!)stop: Stop appbashuiautomator2 stop com.example.app # Stop one app # uiautomator2 stop --all # Stop all apps (Be careful!)doctor: Check uiautomator2 environmentbashuiautomator2 doctor # Example output: # [I 2024-04-25 19:53:36,288 __main__:101 pid:15596] uiautomator2 is OKinstall: Install APK from URL or local pathbashuiautomator2 install http://example.com/app.apk uiautomator2 install /path/to/local/app.apkclear: Clear app databashuiautomator2 clear <package-name>start: Start appbashuiautomator2 start <package-name> uiautomator2 start <package-name>/<activity-name>version: Show uiautomator2 versionbashuiautomator2 version
Agent CLI
u2cli is a lightweight command-line wrapper around common device operations, useful for agent-driven workflows that need to inspect and control a device without writing Python code.
Skill
This repository also ships a skill for Android device automation. After installing uiautomator2, you can add the bundled uiautomator2 skill with either npx or gh:
npx skills add https://github.com/openatx/uiautomator2 --skill uiautomator2
# or
gh skill install https://github.com/openatx/uiautomator2 skills/uiautomator2The installed skill uses u2cli under the hood, so make sure pip install uiautomator2 is already available in your environment.
u2cli device-info
u2cli screenshot screen.png
u2cli dump-hierarchy
u2cli click --text SettingsThe CLI talks to a local u2cli server. Normal commands start the server automatically when needed, and you can also manage it manually:
u2cli start-server
u2cli server-status
u2cli kill-serverThe server keeps a device registry and reuses existing device connections by serial and port. This is especially useful for repeated CLI calls, because the device is connected once and then handled through the server instead of reconnecting on every command.
Differences between Google UiAutomator 2.0 and 1.x
Reference: https://www.cnblogs.com/insist8089/p/6898181.html (Chinese)
- New APIs: UiObject2, Until, By, BySelector (in UiAutomator 2.x Java library)
- Import Style: In 2.0,
com.android.uiautomator.core.*is deprecated. Useandroid.support.test.uiautomator.*(nowandroidx.test.uiautomator.*). - Build System: Maven and/or Ant (1.x); Gradle (2.0).
- Test Package Format: From zip/jar (1.x) to APK (2.0).
- Running Tests via ADB:
- 1.x:
adb shell uiautomator runtest UiTest.jar -c package.name.ClassName - 2.0:
adb shell am instrument -w -r -e debug false -e class package.name.ClassName#methodName package.name.test/androidx.test.runner.AndroidJUnitRunner
- 1.x:
- Access to Android Services/APIs: 1.x: No; 2.0: Yes.
- Log Output: 1.x:
System.out.printechoes to the execution terminal; 2.0: Output to Logcat. - Execution: 2.0: Test cases do not need to inherit from any parent class, method names are not restricted, uses Annotations; 1.x: Needs to inherit
UiAutomatorTestCase, test methods must start withtest.
(Note: uiautomator2 Python library abstracts away many of these Java-level differences, but understanding the underlying UiAutomator evolution can be helpful.)
Dependent Projects
- uiautomator-jsonrpc-server: https://github.com/openatx/android-uiautomator-server/ (The core server running on the Android device)
- adbutils: https://github.com/openatx/adbutils (For ADB communication)
Contributors
Other Excellent Projects
- https://github.com/ecnusse/Kea2: Fusing automated UI testing with scripts for effectively fuzzing Android apps.
- https://github.com/atinfo/awesome-test-automation: A collection of excellent test automation frameworks.
- google/mobly: Google's internal test framework.
- https://github.com/zhangzhao4444/Maxim: A monkey test tool based on UiAutomator.
- http://www.sikulix.com/: A well-established image-based automation framework.
- http://airtest.netease.com/: The predecessor of this project, later taken over and optimized by NetEase Guangzhou team. Features a good IDE. (archived)
(Order matters, additions welcome)
LICENSE
Overview (`uibox/README.md`)
uibox
Add new command
cobra-init add nohupOverview (`_archived/aircv/README.md`)
前言
这是一个 uiautimator2 的一个插件,使得 uiautimator2 可以支持通过图像识别来对手机进行操作
代码集成了开源库: aircv
注意
- 只能支持常宽比为 16:9 的手机
- 截图是以 atx-agent 传过来的图像为基准,图片大小为 800*450
因为分辨率小了,会有失真,所以匹配阈值可适当减小(下面有说明) - 因为基准图为 800*450 分辨率,area 区域范围不能大于该分辨率(下面有说明)
- 有时候确实有但是查找不到,或者查找错误,可适当截图截的大一点
环境
opencv3.x
- 安装opencv3 支持py2和py3(测试环境 python2.7.15 和 python3.7.0)bash
pip install opencv_python - 安装 websocketbash
pip install websocket - 安装 numpybash
pip install numpy
设置
# 启用支持网络下载图片选项
Aircv.support_network = True # 默认 False,不启用
# 设置 host,支持 http
Aircv.host = "127.0.0.1:8000"
# 请求路径,固定
Aircv.path = "/image_service/download/"
# 示例,图片请求地址
img_url = "http://127.0.0.1:8000/image_service/download/@img1"
# 全局设置操作的超时时间,大于该值时间没有找到图像,会报异常
# timeout 可以在每个函数调用时单独设置
Aircv.timeout = 30
# 全局设置操作的等待时间,该值为在查找到图像后,等待多久再操作(等待UI元素渲染完成)
# 例如点击操作,查找到图像后,等待 1秒,然后才点击
Aircv.wait_before_operation = 1
# 全局设置读取图像的频率,间隔几秒读取一张图像,默认为 2秒
# 手机端的服务会 atx-agent 会以较高频率不断发送 800*450 的图像过来,设置该值限制频率
Aircv.rcv_interval = 2
# 图像查找采用模板匹配的方式
# 该设置定义阈值,大于该阈值,则认为图像相同,即找到图像
# 一般来说大于0.999认为图像一样,阈值默认值为0.95
CVHandler.template_threshold = 0.95图像传输
一般来说,图像传输会在连接上设备开始传输,程序结束会自动关闭传输
如果需要主动关闭,开启图像传输的话,可参考如下
import uiautomator2 as u2
from aircv import Aircv
u2.plugin_register('aircv', Aircv)
d = u2.connect()
# 关闭图像传输
d.ext_aircv.stop_get_scren()
# 开启图像传输
d.ext_aircv.start_get_screen()示例
import uiautomator2 as u2
from aircv import Aircv
u2.plugin_register('aircv', Aircv)
d = u2.connect()
# 判断是否存在
d.ext_aircv.exists('tmp.jpg')
d.ext_aircv.exists('tmp.jpg', timeout=60) # 设置超时时间
# 点击
d.ext_aircv.click('tmp.jpg')
d.ext_aircv.click('tmp.jpg', timeout=60) # 设置超时时间
# 原图像中指定查找范围,安卓以左上角为原点即(0,0)
# 参数传入左上角坐标和右下角坐标(x1, y1, x2, y2)
d.ext_aircv.click('tmp.jpg', area=(100, 100, 300, 200))
# 长按
d.ext_aircv.long_click('tmp.jpg')
d.ext_aircv.long_click('tmp.jpg', duration=5) # 设置长按时间
d.ext_aircv.long_click('tmp.jpg', timeout=60) # 设置超时时间
d.ext_aircv.long_click('tmp.jpg', area=(100, 100, 300, 200)) # 设置查找范围
# 滑动
d.ext_aircv.swipe('tmp1.jpg', 'tmp2.jpg')
#设置持续时间,0.1 表示持续 1秒, 默认 1秒
d.ext_aircv.swipe('tmp1.jpg', 'tmp2.jpg', duration=0.1)
d.ext_aircv.swipe('tmp1.jpg', 'tmp2.jpg', timeout=60) # 设置超时时间
d.ext_aircv.swipe('tmp1.jpg', 'tmp2.jpg', area=(100, 100, 300, 200)) # 设置查找范围
# 多点滑动
# duration 的值, 0.1 表示持续 1秒, 默认 1秒
img_list = ['tmp1.jpg', 'tmp2.jpg', 'tmp3.jpg']
d.ext_aircv.swipe_points(img_list, duration=0.5, timeout=60)
# 拖动(按住一会再滑动)
d.ext_aircv.drag('tmp1.jpg', 'tmp2.jpg', duration=0.1, timeout=60)
d.ext_aircv.drag('tmp1.jpg', 'tmp2.jpg', area=(100, 100, 300, 200)) # 设置查找范围
# 获取坐标(x, y)(返回查找到图像的中心坐标)
d.ext_aircv.get_point('tmp1.jpg', timeout=60, area=(100, 100, 300, 200))Overview (`_archived/ocr/README.md`)
使用百度OCR选取文字元素
前提条件
1.需要有百度云账号,百度云注册账号: https://cloud.baidu.com/?from=console
2.创建一个文字识别的应用: https://console.bce.baidu.com/ai/#/ai/ocr/overview/index
记住三个值 AppID 、API_Key、Secret_Key
3.需要安装百度OCR Python SDK:pip install baidu-aip
百度OCR具体应用见百度文档:https://cloud.baidu.com/doc/OCR/s/ejwvxzls6
示例
import uiautomator2 as u2
import uiautomator2.ext.ocr.baiduOCR as ocr
APP_ID = '创建应用的APP_ID'
API_KEY = '创建应用的API_KEY'
SECRECT_KEY = '创建应用的SECRECT_KEY'
# options = {"templateSign": ''} # iOCR财会票据识别模板id
u2.plugin_add("ocr", ocr.OCR, APP_ID, API_KEY, SECRECT_KEY)
# u2.plugin_add("ocrCustom", ocr.OCRCustom, APP_ID, API_KEY, SECRECT_KEY, options)
d = u2.connect()
d.ext_ocr("对战模式").click()
# d.ext_ocrCustom("对战模式").click()Overview (`examples/adbkit-init/README.md`)
adbkit-init
run python -m uiautomator2 init once android device plugin.
Installation
Install nodejs
Install dependencies by npm
bashnpm install
Usage
node main.js --server $SERVER_ADDRHow it works.
Use adbkit to trace device. And the following command will call when device plugin
python -m uiautomator2 init --server $SERVER_ADDRLICENSE
MIT
Overview (`examples/batteryweb/README.md`)
batteryweb
Easy watch device battery
Install
pip install -r requirements.txtUsage
export FLASK_APP="main.py"
export FLASK_DEBUG=1
flask runDemo
Overview (`uiautomator2/ext/htmlreport/README.md`)
HTMLReport for uiautomator2
Demo code
# coding: utf-8
import uiautomator2 as u2
import uiautomator2.ext.htmlreport as htmlreport
u = u2.connect()
hrp = htmlreport.HTMLReport(u)
# take screenshot before each click
hrp.patch_click()
u.click(0.4, 0.6)
u.click(0.4, 0.5)
u(text="Github").click() # will also recordScreenshot
LICENSE
MIT
Overview (`uiautomator2/ext/perf/README.md`)
Performance 性能采集
自动记录测试过程中的CPU,PSS, NET
使用方法
import uiautomator2 as u2
import uiautomator2.ext.perf as perf
package_name = "com.netease.cloudmusic"
u2.plugin_register('perf', perf.Perf)
def main():
d = u2.connect()
d.ext_perf.package_name = package_name
d.ext_perf.csv_output = "perf.csv" # 保存数据到perf.csv
# d.debug = True # 采集到数据就输出,默认关闭
# d.interval = 1.0 # 数据采集间隔,默认1.0s,尽量不要小于0.5s,因为采集内存比较费时间
d.ext_perf.start()
# run ... tests code here ...
d.ext_perf.stop() # 最好结束的时候调用下,虽然不调用也没多大关系
# generate images from csv
# 需要安装 matplotlib, pandas, numpy, humanize
d.ext_perf.csv2images()
if __name__ == '__main__':
main()保存的csv文件内容格式为
time,package,pss,cpu,systemCpu,rxBytes,txBytes,fps
2018-09-11 20:35:29.016,com.tencent.tmgp.sgame,456.71,13.3,15.8,0,0,12.8
2018-09-11 20:35:29.733,com.tencent.tmgp.sgame,456.75,11.0,20.6,108,160,30.7
2018-09-11 20:35:30.756,com.tencent.tmgp.sgame,456.83,12.2,18.9,548,2021,31.3
2018-09-11 20:35:31.730,com.tencent.tmgp.sgame,457.05,11.6,19.1,160,1199,29.8
2018-09-11 20:35:32.759,com.tencent.tmgp.sgame,457.05,11.7,19.5,108,160,31.1
2018-09-11 20:35:33.821,com.tencent.tmgp.sgame,456.86,11.6,17.7,0,0,29.2生成的图片为
csv2images函数更多的用法
d.ext_perf.csv2images("perf.csv", target_dir="./")数据项说明
- PSS直接通过
dumpsys meminfo获取 - CPU直接读取的
/proc/下的文件计算出来的,多核的情况,数据是有可能超过100%的 - rxBytes, txBytes 目前只有wlan的流量,tcp和udp的流量总和
- fps 通过解析
dumpsys SurfaceFlinger --list和dumpsys SurfaceFlinger --latency <VIEW>计算出来
参考资料
- Python CSV读写方法
- android屏幕刷新显示机制
- Android FPS计算方法
- Github项目@leekinwa-androidTestTools_performance_FPS
- 官方proc文件格式资料
- Chromium有关FPS的计算方法
- FPS 计算方法的比较 by fenfenzhong
- 安卓性能测试之cpu占用率统计方法总结
- Android 性能测试实践 (四) 流量
2to3 (`docs/2to3.md`)
2.x到3.x升级说明
变更内容简介
- 移除atx-agent,常驻服务移除,改为运行时启动手机内的uiautomator服务
- 直接通过atx-agent地址连接的方法不再支持,connect函数目前只支持本地的USB设备或adb connect状态的设备
- 环境变量 ANDROID_DEVICE_IP 不再支持,如果需要使用环境变量传递序列号可以使用 ANDROID_SERIAL
- 版本管理从pbr改为poetry,同时降低依赖库的数量
- Python依赖调整为最低3.8
- 增加更多的单测
- 日志使用标准库logging, 默认不输出任何内容,除非手动开启
- minicap,minitouch不再默认安装
atx-agent[armeabi]直接打包到lib里面去,避免外网下载依赖
New
- Add function enable_pretty_logging
- Add class AdbShellError, HierarchyEmptyError, HTTPError
- Add d.xpath.get_page_source() -> PageSource
Breaking changes (移除的功能)
Lib removes
- Remove logzero
- Remove filelocks
- Remove progress
- Remove packaging
- Remove Pillow
Module removes
- Remove module uiautomator2.ext.xpath
Class removes
- Remove class AdbUI
- Remove class GatewayError
- Remove class ServerError, UiautomatorQuitError, RequestError, UiaError, JsonRpcError, NullObjectExceptionError, NullPointerExceptionError, StaleObjectExceptionError
Property removes
- Remove property u2.logger
- Remove property u2.xpath.XPath.logger
- Remove property d.watcher.debug
- Remove property: address, 原来是用于获取atx-agent的URL地址
- Remove property: alive, 原来用于检测atx-agent的存活状态
- Remove property: uiautomator, 原来用法是d.uiautomator.stop()
- Remove property: widget, 原来也不怎么用
- Remove property: http, 原来是这样用的d.http.get("/device_info")
- Remove d.settings["xpath_debug"]
Function removes
- Remove function current_app, use app_current instead
- Remove function d.xpath.apply_watch_from_yaml
- Remove function healcheck, 原来是未来恢复uiautomator服务用的
- Remove function service(name: str) -> Service, 原本是用于做atx-agent的服务管理
- Remove function app_icon() -> Image, 该函数依赖atx-agent
- Remove function connect_wifi() -> Device, 该函数依赖atx-agent
- Remove function connect_adb_wifi(str) -> Device, 直接用connect就行了
- Remove function set_new_command_timeout(timeout: int), 用不着了
- Remove function open_identify(), 打开一个比较明显的界面,这个函数出了点毛病,先下掉了
- Remove function toast.show(text, duration), 用的不多而且稳定性不好
XPath (d.xpath) methods
- remove dump_hierarchy
- remove get_last_hierarchy
- remove add_event_listener
- remove send_click, send_longclick, send_swipe, send_text, take_screenshot
- remove when, run_watchers, watch_background, watch_stop, watch_clear, sleep_watch
- remove position method, usage like d.xpath(...).position(0.2, 0.2)
InputMethod
- deprecated wait_fastinput_ime
- deprecated set_fastinput_ime use set_input_ime instead
Command remove
- Remove "uiautomator2 healthcheck"
- Remove "uiautomator2 identify"
Function changes
connect_usb
- 2.x connect_usb(serial, init: bool)
- 3.x connect_usb(serial)
shell
- 2.x shell(cmdargs, stream: bool, timeout)
- 3.x shell(cmdargs, timeout)
push
- 2.x push(src, dst, mode, show_process: bool)
- 3.x push(src, dst, mode)
xpath
- 2.x
d.xpath("...").wait() -> XMLElement|None - 3.x
d.xpath("...").wait() -> bool
reset_uiautomator
- 2.x reset_uiautomator(self, reason="unknown", depth=0)
- 3.x reset_uiautomator()
app_info
2.x Response
{
"mainActivity": "com.github.uiautomator.MainActivity",
"label": "ATX",
"versionName": "1.1.7",
"versionCode": 1001007,
"size":1760809
}3.x Response
{
"versionName": "1.1.7",
"versionCode": 1001007,
}session
- 2.x sess = d.session("com.netease.cloudmusic", attach=True, strict=True)
- 3.x sess = d.session("com.netease.cloudmusic", attach=True)
It seems the strict is useless, so I delete it.
push
- 2.x push(src, dst, mode, show_process:bool=False)
- 3.x push(src, dst, mode)
device_info
print(d.device_info)
2.x prints
{'udid': '08a3d291-26:17:84:b6:cb:a0-DT1901A',
'version': '10',
'serial': '08a3d291',
'brand': 'SMARTISAN',
'model': 'DT1901A',
'hwaddr': '26:17:84:b6:cb:a0',
'sdk': 29,
'agentVersion': '0.10.0',
'display': {'width': 1080, 'height': 2340},
'battery': {'acPowered': False,
'usbPowered': True,
'wirelessPowered': False,
'status': 5,
'health': 2,
'present': True,
'level': 100,
'scale': 100,
'voltage': 4356,
'temperature': 292,
'technology': 'Li-poly'},
'memory': {'total': 7665272, 'around': '7 GB'},
'cpu': {'cores': 8, 'hardware': 'Qualcomm Technologies, Inc SM8150'},
'arch': '',
'owner': None,
'presenceChangedAt': '0001-01-01T00:00:00Z',
'usingBeganAt': '0001-01-01T00:00:00Z',
'product': None,
'provider': None}3.x prints
{'serial': 'VVY0223208008426',
'sdk': 31,
'brand': 'HUAWEI',
'model': 'JAD-AL80',
'arch': 'arm64-v8a',
'version': 12}app_current
- 2.x raise
OSErrorif couldn't get focused app - 3.x raise
DeviceErrorif couldn't get focused app
current_ime
- 2.x return (ime_method_name, bool), e.g. ("com.github.uiautomator/.FastInputIME", True)
- 3.x return ime_method_name, e.g. "com.github.uiautomator/.FastInputIME"
toast
2.x d.toast.get_message(5.0, default="")
3.x d.last_toast (property)
2.x d.toast.reset()
3.x d.clear_toast()
Api (`docs/api.rst`)
API
.. autoclass:: uiautomator2.Device
:members:
.. autoclass:: uiautomator2.Session
:members:
.. autoclass:: uiautomator2.xpath.XPath
:members:
Index (`docs/index.rst`)
.. uiautomator2 documentation master file, created by
sphinx-quickstart on Tue Feb 18 15:32:23 2020.
You can adapt this file completely to your liking, but it should at least
contain the root toctree directive.
Welcome to uiautomator2's documentation!
.. toctree::
:maxdepth: 2
:caption: 常用接口
api
.. toctree::
:maxdepth: 2
:caption: 全部接口
uiautomator2
Indices and tables
- :ref:
genindex - :ref:
modindex - :ref:
search
--- METRICS ---
- Files Extracted: 11
- Estimated Token Budget: ~14283 tokens
- Canonical Reference: https://codewiki.google/github.com/openatx/uiautomator2