基于python+uiautomator2,2020.12月最新库的使用方法,更新watcher使用方法(二)

简介: Basic API UsagesThis part showcases how to perform common device operations

Basic API Usages

This part showcases how to perform common device operations:


Shell commands

Run a short-lived shell command with a timeout protection. (Default timeout 60s)


Note: timeout support require atx-agent >=0.3.3


adb_shell function is deprecated. Use shell instead.


Simple usage

output, exit_code = d.shell("pwd", timeout=60) # timeout 60s (Default)
# output: "/\n", exit_code: 0
# Similar to command: adb shell pwd
# Since `shell` function return type is `namedtuple("ShellResponse", ("output", "exit_code"))`
# so we can do some tricks
output = d.shell("pwd").output
exit_code = d.shell("pwd").exit_code

The first argument can be list. for example

output, exit_code = d.shell(["ls", "-l"])
# output: "/....", exit_code: 0

This returns a string for stdout merged with stderr. If the command is a blocking command, shell will also block until the command is completed or the timeout kicks in. No partial output will be received during the execution of the command. This API is not suitable for long-running commands. The shell command given runs in a similar environment of adb shell, which has a Linux permission level of adb or shell (higher than an app permission).


  • Run a long-running shell command


add stream=True will return requests.models.Response object. More info see requests stream

r = d.shell("logcat", stream=True)
# r: requests.models.Response
deadline = time.time() + 10 # run maxium 10s
try:
    for line in r.iter_lines(): # r.iter_lines(chunk_size=512, decode_unicode=None, delimiter=None)
        if time.time() > deadline:
            break
        print("Read:", line.decode('utf-8'))
finally:
    r.close() # this method must be called

Command will be terminated when r.close() called.

Session

Session represent an app lifecycle. Can be used to start app, detect app crash.

  • Launch and close app
sess = d.session("com.netease.cloudmusic") # start 网易云音乐
sess.close() # 停止网易云音乐
sess.restart() # 冷启动网易云音乐
  • Use python with to launch and close app
with d.session("com.netease.cloudmusic") as sess:
    sess(text="Play").click()
  • Attach to the running app
# launch app if not running, skip launch if already running
sess = d.session("com.netease.cloudmusic", attach=True)
# raise SessionBrokenError if not running
sess = d.session("com.netease.cloudmusic", attach=True, strict=True)
  • Detect app crash
# When app is still running
sess(text="Music").click() # operation goes normal
# If app crash or quit
sess(text="Music").click() # raise SessionBrokenError
# other function calls under session will raise SessionBrokenError too
# check if session is ok.
# Warning: function name may change in the future
sess.running() # True or False

Retrieve the device info

Get basic information

d.info

Below is a possible output:

{ 
    u'displayRotation': 0,
    u'displaySizeDpY': 640,
    u'displaySizeDpX': 360,
    u'currentPackageName': u'com.android.launcher',
    u'productName': u'takju',
    u'displayWidth': 720,
    u'sdkInt': 18,
    u'displayHeight': 1184,
    u'naturalOrientation': True
}

Get window size

print(d.window_size())
# device upright output example: (1080, 1920)
# device horizontal output example: (1920, 1080)

Get current app info. For some android devices, the output could be empty (see Output example 3)

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 activity

d.wait_activity(".ApiDemos", timeout=10) # default timeout 10.0 seconds
# Output: true of false

Get device serial number

print(d.serial)
# output example: 74aAEDR428Z9

Get WLAN ip

print(d.wlan_ip)
# output example: 10.0.0.1

Get detailed device info

print(d.device_info)

Below is a possible output:

{'udid': '3578298f-b4:0b:44:e6:1f:90-OD103',
 'version': '7.1.1',
 'serial': '3578298f',
 'brand': 'SMARTISAN',
 'model': 'OD103',
 'hwaddr': 'b4:0b:44:e6:1f:90',
 'port': 7912,
 'sdk': 25,
 'agentVersion': 'dev',
 'display': {'width': 1080, 'height': 1920},
 'battery': {'acPowered': False,
  'usbPowered': False,
  'wirelessPowered': False,
  'status': 3,
  'health': 0,
  'present': True,
  'level': 99,
  'scale': 100,
  'voltage': 4316,
  'temperature': 272,
  'technology': 'Li-ion'},
 'memory': {'total': 3690280, 'around': '4 GB'},
 'cpu': {'cores': 8, 'hardware': 'Qualcomm Technologies, Inc MSM8953Pro'},
 'presenceChangedAt': '0001-01-01T00:00:00Z',
 'usingBeganAt': '0001-01-01T00:00:00Z'}

Clipboard

Get of set clipboard content

设置粘贴板内容或获取内容 (目前已知问题是9.0之后的后台程序无法获取剪贴板的内容)

  • clipboard/set_clipboard
d.set_clipboard('text', 'label')
print(d.clipboard)

Key Events

  • Turn on/off screen
d.screen_on() # turn on the screen
d.screen_off() # turn off the screen
  • Get current screen status
d.info.get('screenOn') # require Android >= 4.4
  • Press hard/soft key
d.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 KeyEvnet


  • Unlock screen
d.unlock()
# This is equivalent to
# 1. launch activity: com.github.uiautomator.ACTION_IDENTIFY
# 2. press the "home" key

Gesture interaction with the device

  • Click on the screen
d.click(x, y)
  • Double click
d.double_click(x, y)
d.double_click(x, y, 0.1) # default duration between two click is 0.1s
  • Long click on the screen
d.long_click(x, y)
d.long_click(x, y, 0.5) # long click 0.5s (default)
  • Swipe
d.swipe(sx, sy, ex, ey)
d.swipe(sx, sy, ex, ey, 0.5) # swipe for 0.5s(default)
  • SwipeExt 扩展功能
d.swipe_ext("right") # 手指右滑,4选1 "left", "right", "up", "down"
d.swipe_ext("right", scale=0.9) # 默认0.9, 滑动距离为屏幕宽度的90%
d.swipe_ext("right", box=(0, 0, 100, 100)) # 在 (0,0) -> (100, 100) 这个区域做滑动
# 实践发现上滑或下滑的时候,从中点开始滑动成功率会高一些
d.swipe_ext("up", scale=0.8) # 代码会vkk
# 还可以使用Direction作为参数
from uiautomator2 import Direction
d.swipe_ext(Direction.FORWARD) # 页面下翻, 等价于 d.swipe_ext("up"), 只是更好理解
d.swipe_ext(Direction.BACKWARD) # 页面上翻
d.swipe_ext(Direction.HORIZ_FORWARD) # 页面水平右翻
d.swipe_ext(Direction.HORIZ_BACKWARD) # 页面水平左翻
  • Drag
d.drag(sx, sy, ex, ey)
d.drag(sx, sy, ex, ey, 0.5) # swipe for 0.5s(default)
  • Swipe points
# swipe from point(x0, y0) to point(x1, y1) then to point(x2, y2)
# time will speed 0.2s bwtween two points
d.swipe_points([(x0, y0), (x1, y1), (x2, y2)], 0.2))

多用于九宫格解锁,提前获取到每个点的相对坐标(这里支持百分比), 更详细的使用参考这个帖子 使用u2实现九宫图案解锁

  • Touch and drap (Beta)

这个接口属于比较底层的原始接口,感觉并不完善,不过凑合能用。注:这个地方并不支持百分比

d.touch.down(10, 10) # 模拟按下
time.sleep(.01) # down 和 move 之间的延迟,自己控制
d.touch.move(15, 15) # 模拟移动
d.touch.up() # 模拟抬起

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

  • Retrieve/Set device orientation


The possible orientations:


natural or n

left or l

right or r

upsidedown or u (can not be set)

# retrieve orientation. the output could be "natural" or "left" or "right" or "upsidedown"
orientation = d.orientation
# WARNING: not pass testing in my TT-M1
# set orientation and freeze rotation.
# notes: setting "upsidedown" requires Android>=4.3.
d.set_orientation('l') # or "left"
d.set_orientation("l") # or "left"
d.set_orientation("r") # or "right"
d.set_orientation("n") # or "natural"
  • Freeze/Un-freeze rotation
# freeze rotation
d.freeze_rotation()
# un-freeze rotation
d.freeze_rotation(False)
  • Take screenshot
# take screenshot and save to a file on the computer, require 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')
open("some.jpg", "wb").write(imagebin)
  • Dump UI hierarchy
# get the UI hierarchy dump content (unicoded).
xml = d.dump_hierarchy()
  • Open notification or quick settings
d.open_notification()
d.open_quick_settings()

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 below parameters. Refer to UiSelector Java doc for detailed information.

  • text, textContains, textMatches, textStartsWith
  • className, classNameMatches
  • description, descriptionContains, descriptionMatches, descriptionStartsWith
  • checkable, checked, clickable, longClickable
  • scrollable, enabled,focusable, focused, selected
  • packageName, packageNameMatches
  • resourceId, resourceIdMatches
  • index, instance

Children and siblings

  • children
# get the children or grandchildren
d(className="android.widget.ListView").child(text="Bluetooth")
  • siblings
# get siblings
d(text="Google").sibling(className="android.widget.ImageView")
  • children by text or description or instance
# 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,
    className="android.widget.LinearLayout"
  )
  • child_by_description is to find children whose grandchildren have the specified description, other parameters being similar to child_by_text.
  • child_by_instance is to find children with has 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 below hierarchy

<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 widgets first. However, according to the UI hierarchy, more than one switch widgets exist and have almost the same properties. Selecting by className will not work. Alternatively, the below selecting strategy would help:

d(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, top, bottom.


  • 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 above cases, we can alternatively select it with:
## 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 qualifying instances, like below:

d(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):

# get the count of views with text "Add new" on current screen
d(text="Add new").count
# same as count property
len(d(text="Add new"))
# get the instance via index
d(text="Add new")[0]
d(text="Add new")[1]
...
# iterator
for view in d(text="Add new"):
    view.info  # ...

Notes: when using selectors in a code block that walk through the result list, you must ensure that the UI elements on the screen keep unchanged. Otherwise, when 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

d(text="Settings").exists # True if exists, else False
d.exists(text="Settings") # alias of above property.
# advanced usage
d(text="Settings").exists(timeout=3) # wait Settings appear in 3s, same as .wait(3)

Retrieve the info of the specific UI object

d(text="Settings").info

Below is a possible output:

{ u'contentDescription': u'',
u'checked': False,
u'scrollable': False,
u'text': u'Settings',
u'packageName': u'com.android.launcher',
u'selected': False,
u'enabled': True,
u'bounds': {u'top': 385,
            u'right': 360,
            u'bottom': 585,
            u'left': 200},
u'className': u'android.widget.TextView',
u'focused': False,
u'focusable': True,
u'clickable': True,
u'chileCount': 0,
u'longClickable': True,
u'visibleBounds': {u'top': 385,
                    u'right': 360,
                    u'bottom': 585,
                    u'left': 200},
u'checkable': False
}

Get/Set/Clear text of an editable field (e.g., EditText widgets)

d(text="Settings").get_text()  # get widget text
d(text="Settings").set_text("My text...")  # set the text
d(text="Settings").clear_text()  # clear the text

Get Widget center point

x, y = d(text="Settings").center()
# x, y = d(text="Settings").center(offset=(0, 0)) # left-top x, y

Take screenshot of widget

im = d(text="Settings").screenshot()
im.save("settings.jpg")

Perform the click action on the selected UI object

  • Perform click on the specific object
# click on the center of the specific ui object
d(text="Settings").click()
# wait element to appear for at most 10 seconds and then click
d(text="Settings").click(timeout=10)
# click with offset(x_offset, y_offset)
# click_x = x_offset * width + x_left_top
# click_y = y_offset * 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 when exists in 10s, default timeout 0s
clicked = d(text='Skip').click_exists(timeout=10.0)
# click until element gone, return bool
is_gone = d(text="Skip").click_gone(maxretry=10, interval=1.0) # maxretry default 10, interval default 1.0
  • Perform long click on the specific UI object
# long click on the center of the specific UI object
d(text="Settings").long_click()

Gesture actions for the specific UI object

  • Drag the UI object towards another point or another UI object
# notes : drag can not be used for Android<4.3.
# drag the UI object to a screen point (x, y), in 0.5 second
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 second
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:

  • left
  • right
  • top
  • bottom
d(text="Settings").swipe("right")
d(text="Settings").swipe("left", steps=10)
d(text="Settings").swipe("up", steps=20) # 1 steps is about 5ms, so 20 steps is about 0.1s
d(text="Settings").swipe("down", steps=20)
  • Two-point gesture from one point to another
d(text="Settings").gesture((sx1, sy1), (sx2, sy2), (ex1, ey1), (ex2, ey2))
  • Two-point gesture on the specific UI object

Supports two gestures:

  • In, from edge to center
  • Out, from center to edge
# notes : pinch can not be set until Android 4.3.
# from edge to center. here is "In" not "in"
d(text="Settings").pinch_in(percent=100, steps=10)
# from center to edge
d(text="Settings").pinch_out()

The default timeout is 20s. see global settings for more details


  • Perform fling on the specific ui object(scrollable)


Possible properties:


horiz or vert

forward or backward or toBeginning or toEnd

# fling forward(default) vertically(default) 
d(scrollable=True).fling()
# fling forward horizontally
d(scrollable=True).fling.horiz.forward()
# fling backward vertically
d(scrollable=True).fling.vert.backward()
# fling to beginning horizontally
d(scrollable=True).fling.horiz.toBeginning(max_swipes=1000)
# fling to end vertically
d(scrollable=True).fling.toEnd()
  • Perform scroll on the specific ui object(scrollable)

Possible properties:

  • horiz or vert
  • forward or backward or toBeginning or toEnd, or to
# scroll forward(default) vertically(default)
d(scrollable=True).scroll(steps=10)
# scroll forward horizontally
d(scrollable=True).scroll.horiz.forward(steps=100)
# scroll backward vertically
d(scrollable=True).scroll.vert.backward()
# scroll to beginning horizontally
d(scrollable=True).scroll.horiz.toBeginning(steps=100, max_swipes=1000)
# scroll to end vertically
d(scrollable=True).scroll.toEnd()
# scroll forward vertically until specific ui object appears
d(scrollable=True).scroll.to(text="Security")
相关文章
|
17小时前
|
数据可视化 数据处理 Python
Python数据可视化:Matplotlib库的使用与实战
Python数据可视化:Matplotlib库的使用与实战
5 0
|
2天前
|
数据采集 数据挖掘 Serverless
利用Python和Pandas库优化数据清洗流程
在数据分析项目中,数据清洗是至关重要的一步。传统的数据清洗方法往往繁琐且易出错。本文将介绍如何利用Python编程语言中的Pandas库,通过其强大的数据处理能力,实现高效、自动化的数据清洗流程。我们将探讨Pandas库在数据清洗中的应用,包括缺失值处理、重复值识别、数据类型转换等,并通过一个实际案例展示如何利用Pandas优化数据清洗流程,提升数据质量。
|
4天前
|
机器学习/深度学习 数据处理 算法框架/工具
Python标准库与第三方库:强大的编程资源
Python标准库与第三方库:强大的编程资源
|
4天前
|
机器学习/深度学习 数据处理 算法框架/工具
Python库与框架的深入解析
Python中的库和框架扩展了其功能,提高了开发效率。库(如标准库os和第三方库requests)提供预定义的工具,而框架(如Web框架Flask和数据科学框架Scikit-learn)定义了应用结构和交互方式。通过库和框架,开发者能更专注于业务逻辑,快速构建Web应用和执行数据科学任务。
|
6天前
|
Python
python相关库的安装:pandas,numpy,matplotlib,statsmodels
python相关库的安装:pandas,numpy,matplotlib,statsmodels
|
6天前
|
Python
Python之Math库解析
Python之Math库解析
10 0
Python之Math库解析
|
6天前
|
数据采集 Web App开发 测试技术
玩转Python的fake-useragent库
玩转Python的fake-useragent库
|
7天前
|
数据采集 数据可视化 数据处理
利用Python和Pandas库实现高效的数据处理与分析
在大数据和人工智能时代,数据处理与分析已成为不可或缺的一环。Python作为一门强大的编程语言,结合Pandas库,为数据科学家和开发者提供了高效、灵活的数据处理工具。本文将介绍Pandas库的基本功能、优势,并通过实际案例展示如何使用Pandas进行数据清洗、转换、聚合等操作,以及如何利用Pandas进行数据可视化,旨在帮助读者深入理解并掌握Pandas在数据处理与分析中的应用。
|
8天前
|
架构师 数据挖掘 Python
最全pandas库(Python),2024年最新阿里云架构师面试
最全pandas库(Python),2024年最新阿里云架构师面试
最全pandas库(Python),2024年最新阿里云架构师面试
|
8天前
|
机器学习/深度学习 算法 前端开发
2024年8个可以提高数据科学工作效率并节省宝贵时间的Python库,2024年最新记得把每一次面试当做经验积累
2024年8个可以提高数据科学工作效率并节省宝贵时间的Python库,2024年最新记得把每一次面试当做经验积累
2024年8个可以提高数据科学工作效率并节省宝贵时间的Python库,2024年最新记得把每一次面试当做经验积累