Python-uiautomator使用说明文档详细介绍
这个Python库是基于Android自带的uiautomator测试框架的一个python封包。适用于Android 4.1以上版本,需要通过adb连接Android设备。
from uiautomator import device as d d . screen.on() d(text="Clock").click()
安装
pip install uiautomator
前置条件
安装 Android的SDK,设置Android_home环境以正确的路径。
启动 adb 并且通过usb数据线将电脑与Android设备相连接。
设置Android设备,在开发者选项中允许未知来源应用安装。
导入uiautomator
如果仅有一台设备或在 Android_serial 环境变量中配置。
from uiautomator import device as d
通过设备的设备ID来确定设备对象:
from uiautomator import Device d=Device('014E05DE0F02000E')
通过其他计算机的端口及连接运行adb服务
虽然 adb 支持sdk 4.3的 -a选项 ,但是现在它有问题。在所有非本地服务上启动adb监听服务使用
adb -a -P 5037 fork-server server &
from uiautomator import Device d=Device('014E05DE0F02000E', adb_server_host='192.168.1.68', adb_server_port=5037)
注:在下面的例子中,我们使用d代表Android设备对象。
基本API用法
此部分通过一些简单的示例显示设备的正常操作。
检索设备信息
d.info
以下是我这里得到的信息:
{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 }
设备的关键时间操作
打开/关闭屏幕
#在屏幕上打开 d.screen.on() #关闭屏幕 d.screen.off()
替代方法是:
#唤醒设备 d.wakeup() #睡眠设备,一样关闭屏幕。 d.sleep()
按硬/软键
#按home键 d.press.home() #按返回键 d.press.back() #正常的方式按返回键 d.press( “back”) #按下键码0×07(‘0’)与ALT (0X02) d.press(0x07,0X02)
目前下列按键支持上述方法
home
back
left
right
home
back
left
right
up
down
center
menu
search
enter
delete(or del)
recent(recent apps)
volume_up
volume_down
volume_mute
camera
power
down
center
menu
search
enter
delete(or del)
recent(recent apps)
volume_up
volume_down
volume_mute
camera
power
你可以找到所有的键值定义在Android KeyEvent连接如下:
developer.android.com/reference/android/view/KeyEvent.html
在设备上模拟手势交互
#点击屏幕 # click (x, y) on screen d.click(x, y) #长按屏幕 # long click (x, y) on screen d.long_click(x, y)
滑动
# swipe from (sx, sy) to (ex, ey) #从sx,sy坐标滑动至ex,ey坐标 d.swipe(sx, sy, ex, ey) # swipe from (sx, sy) to (ex, ey) with 10 steps d.swipe(sx, sy, ex, ey, steps=10)
拖动
# drag from (sx, sy) to (ex, ey) d.drag(sx, sy, ex, ey) # drag from (sx, sy) to (ex, ey) with 10 steps d.drag(sx, sy, ex, ey, steps=10)
设备屏幕的事件
有以下几种属性:
natural 或者 n 代替
left 或者 l 代替
right 或者 r 代替
upsidedown或 u(不能设定)
#获取orientation(方向),可能是上述中的任意一种 orientation = d.orientation #设置定向和冻结旋转。 #说明:"upsidedown"不能用于Android 4.3 以前的版本 d.orientation="l" d.orientation="r" d.orientation="n"
锁定/解除旋转
#锁定旋转 d.freeze rotation() #解除旋转锁定 d.freeze_rotation(False)
屏幕截图
获取屏幕截图并且将其存本机地址中,命名为home.png,不能被用于Android 4.2以前的版本
d.screenshot("home.png")
将屏幕结构储存(就是使用uiautomatorviewer看到的那个结构,以xml格式保存)
#将当前屏幕结构保存在本机并命名为"heierarchy.xml" d.dump("hierarchy.xml") #或者将存储值作为结果返回 xml=d.dump()
打开通知消息栏或快速设置
#打开通知消息栏,不能用于Android 4.3以前的版本 d.notification() #打开快速设置栏,不能用于Android 4.3以前的版本 d.open.quick_settings()
等待空闲或者窗口刷新
# 等待当前窗口空闲 d.wait.idle() #等待直到窗口发生刷新事件 d.wait.update()
监视器
You can register watcher to perform some actions when a selector can not find a match.
当选择器无法找到匹配时,您可以注册观察器来执行一些操作。
注册监视器
当一个选择器找不到匹配时,uiautomator 会运行全部已经注册的观察者
条件匹配时点击目标
d.watcher("AUTO_FC_WHEN_ANR").when(text="ANR").when(text="Wait") \ .click(text="Force Close") # d.watcher(name) ## creates a new named watcher. (创建一个新的监视器,并将其命名为“name”) # .when(condition) ## the UiSelector condition of the watcher. (为监视器添加添加一个Uiselector条件) # .click(target) ## perform click action on the target UiSelector. (执行对目标Uiselector点击动作)
条件匹配时按下按键
d.watcher("AUTO_FC_WHEN_ANR").when(text="ANR").when(text="Wait") \ .press.back.home() # Alternative way to define it as below (可以将其定义为以下方法) d.watcher("AUTO_FC_WHEN_ANR").when(text="ANR").when(text="Wait") \ .press("back", "home") # d.watcher(name) ## creates a new named watcher. (创建一个监视器) # .when(condition) ## the UiSelector condition of the watcher. (为监视器添加一个Uiselector条件) # .press.......() ## press keys one by one in sequence. (按顺序依次按下key) # Alternative way defining key sequence is press(, ..., ) (定义另一种键值序列的方法是‘press(, ..., )’)
检查监视器是否触发
一个监视器触发意味着这个监视器所有条件都匹配并且监视器运行。
d.watcher("watcher_name").triggered # true in case of the specified watcher triggered, else false (监视器被触发为‘真’,反之则为‘假’)
删除监视器命名
# remove the watcher (删除监视器) d.watcher("watcher_name").remove()
列出所有的监视器
d.watchers # a list of all registered wachers' names (一个已注册的监视器名称列表)
检查是否有监视器被触发
d.watchers.triggered # true in case of any watcher triggered (在任何监视器被触发的情况下)
重置所有已触发的监视器
# reset all triggered watchers, after that, d.watchers.triggered will be false. (重置所有已触发的监视器,并且将d.watchers.triggered 参数变为false) d.watchers.reset()
删除监视器
# remove all registered watchers (删除所有已触发的监视器) d.watchers.remove() # remove the named watcher, same as d.watcher("watcher_name").remove() (删除特定名称的监视器,与d.watcher("watcher_name").remove() 相同) d.watchers.remove("watcher_name")
强制运行所有的监视器
# force to run all registered watchers (强制运行所有已注册的监视器) d.watchers.run()
处理程序
处理程序的功能与Watcher相同,只是它实现了我们的Android uiautomator。
处理程序和观察程序之间最不同的用法是,处理程序可以使用自定义的回调函数
def fc_close(device): if device(text='Force Close').exists: device(text='Force Close').click() return True # return True means to break the loop of handler callback functions. (返回True来中断处理程序回调函数的循环) # turn on the handler callback function(打开处理程序返回函数) d.handlers.on(fc_close) # turn off the handler callback function(关闭处理程序返回函数) d.handlers.off(fc_close)
选择器
选择器是标识当前窗口中的特定UI对象
# To seleted the object ,text is 'Clock' and its className is 'android.widget.TextView' (对于选择器对象,文字是“Clock”和它的类名是“Android.widget.TextView”) d(text='Clock', className='android.widget.TextView')
选择器支持以下参数。请参阅UiSelector DOC java的详细信息。
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
子对象和同级UI对象
子对象
# get the child or grandchild(得到子类或复子类) d(className="android.widget.ListView").child(text="Bluetooth")
同级对象
# get sibling or child of sibling(得到同级对象或子类) d(text="Google").sibling(className="android.widget.ImageView")
子类文本或描述或实例
# get the child match className="android.widget.LinearLayout" (获取子类匹配的 className="android.widget.LinerarLayout") # and also it or its child or grandchild contains text "Bluetooth" (而且它或它的子类和复子类包含文本“Bluetooth”) d(className="android.widget.ListView", resourceId="android:id/list") \ .child_by_text("Bluetooth", className="android.widget.LinearLayout") # allow scroll search to get the child (允许滚动搜索获得子类) d(className="android.widget.ListView", resourceId="android:id/list") \ .child_by_text( "Bluetooth", allow_scroll_search=True, className="android.widget.LinearLayout" )
child_by_description是找到哪个子类或复子类包含与child_by_text相同的描述说明。
child_by_instance查找一个目标类中子类中特定的UI元素,用于没有滚动可见视图上执行。
上面的方法支持链接调用,例如对于下面的层次结构
...
我们要点击文本“Wi-Fi”右侧的开关打开/打开Wi-Fi。因为有几个开关,几乎相同的属性,所以我们不能使用类似d(className=”android.widget.Switch”)选择的UI对象。相反,我们可以使用下面的代码来选择它。
d(className="android.widget.ListView", resourceId="android:id/list") \ .child_by_text("Wi?Fi", className="android.widget.LinearLayout") \ .child(className="android.widget.Switch") \ .click()
相对位置
此外,我们可以用相对位置的方法来获取视图:left,right,top,bottom。
d(A).left(B),意味着在左侧选择B。 d(A).right(B),表示选择A右侧的B. d(A).up(B),表示选择B以上的A. d(A).down(B),表示在A下选择B.
所以对于上面的情况,我们可以写代码:
## select "switch" on the right side of "Wi-Fi" (选择无线网络连接右侧的开关) d(text="Wi?Fi").right(className="android.widget.Switch").click()
多个实例
有时,屏幕可能包含多个视图与相同的例如文本,那么你将不得不使用选择器中的“实例”属性,
如下所示:
d(text="Add new", instance=0) # which means the first instance with text "Add new" (这意味着第一个实例是文字“Add new”)
但是,uiautomator提供了类似的方法来使用它。
# get the count of views with text "Add new" on current screen (统计当前界面中使用文本“Add new”) 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 # ...
注意:当您使用选择器像一个列表时,你必须确保屏幕保持不变,否则你可能会得到UI未找到错误。
获取所选的ui对象状态及其信息
检查特定ui对象是否存在
d(text="Settings").info
以下是可能的结果:
{ 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 }
设置/清除可编辑字段的文本
d(text="Settings").clear_text() # clear the text(清除文本信息) d(text="Settings").set_text("My text...") # set the text(设置文本信息)
对选中的ui对象执行单击操作
点击特定的ui对象
# click on the center of the specific ui object (点击特定的UI对象的中心) d(text="Settings").click0() # click on the bottomright corner of the specific ui object (点击具体的的UI对象的右下角) d(text="Settings").click.bottomright() # click on the topleft corner of the specific ui object (点击具体的UI对象的左上角) d(text="Settings").click.topleft() # click and wait until the new window update (等待更新后点击) d(text="Settings").click.wait()
长时间点击特定的ui对象
# long click on the center of the specific ui object (长按特定的UI对象的中心) d(text="Settings").long_click() # long click on the bottomright corner of the specific ui object (长按特定的UI对象的右下角) d(text="Settings").long_click.bottomright() # long click on the topleft corner of the specific ui object (长按特定UI对象的左上角) d(text="Settings").long_click.topleft()
针对特定UI对象
# notes : drag can not be set until Android 4.3. (注:不能用于Android4.3以下的版本) # drag the ui object to point (x, y) (拖拽UI对象至点X,Y) d(text="Settings").drag.to(x, y, steps=100) # drag the ui object to another ui object(center) (拖拽某一UI对象到另外一个UI对象(中心)) d(text="Settings").drag.to(text="Clock", steps=50)
从UI对象的中心滑动到UI对象的边缘
滑动支持4个方向:
left
right
top
bottom
d(text="Settings").swipe.right() d(text="Settings").swipe.left(steps=10) d(text="Settings").swipe.up(steps=10) d(text="Settings").swipe.down()
模拟两点同时移动
d(text="Settings").gesture((sx1, sy1), (sx2, sy2)) \ .to((ex1, ey1), (ex2, ey2))
模拟两点在特定的UI对象的上的操作
In,从边到中心
Out,从中心到边缘
# notes : pinch can not be set until Android 4.3. (注:不能用于Android4.3以下的版本) # from edge to center. here is "In" not "in" (从边缘到中心这里使用的是“In”不是“in”) d(text="Settings").pinch.In(percent=100, steps=10) # from center to edge (从中心到边缘) d(text="Settings").pinch.Out()
同时三点手势模拟
d().gestureM((sx1, sy1), (sx2, sy2),(sx3, sy3)) \ .to((ex1, ey1), (ex2, ey2),(ex3,ey3)) d().gestureM((100,200),(300,200),(600,200),(100,600),(300,600),(600,900))
等待特定ui对象出现或消失
# wait until the ui object appears (等待UI对象出现) d(text="Settings").wait.exists(timeout=3000) # wait until the ui object gone (等待UI对象消失) d(text="Settings").wait.gone(timeout=1000)
对特定的UI对象执行抛出操作(可滚动)
可能的属性
horiz or vert
(水平或者是垂直的)
forward or backward or toBeginning or toEnd
(向前或向后,去开始位置或者去结束位置)
# fling forward(default) vertically(default) (向前抛出(默认),在垂直方向(默认)) d(scrollable=True).fling() # fling forward horizentally (向前抛出,在水平方向) d(scrollable=True).fling.horiz.forward() # fling backward vertically (向后抛出,在垂直方向) d(scrollable=True).fling.vert.backward() # fling to beginning horizentally (向开始位置抛出,在水平方向) d(scrollable=True).fling.horiz.toBeginning(max_swipes=1000) # fling to end vertically (向结束方向抛出,在垂直方向) d(scrollable=True).fling.toEnd()
在特定的UI对象上滚动(可滚动)
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 horizentally (向前滚动,在水平方向) d(scrollable=True).scroll.horiz.forward(steps=100) # scroll backward vertically (向后滚动,在垂直方向) d(scrollable=True).scroll.vert.backward() # scroll to beginning horizentally (向开始位置滚动,在垂直方向) 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 (向前滚动,直到特定的UI对象出现) d(scrollable=True).scroll.to(text="Security")
Contribution
Fork the repo, and clone to your computer. Checkout a new branch from develop branch Install requirements: pip install -r requirements.txt Make your changes, and update tests. Don't forget adding your name at the end of 'Contributors' section Pass all tests and your code must be covered: tox. Commit your changes and submit pull request to develop branch.
贡献者
Xiaocong He (@xiaocong) Yuanyuan Zou (@yuanyuan) Qian Jin (@QianJin2013) Xu Jingjie (@xiscoxu) Xia Mingyuan (@mingyuan-xia)
问题和讨论
如果您有任何错误报告或问题,烦请提交到 github issues。
备注:
Android的uiautomator适用于Android 4.1及以上版本,所以在使用它之前,请确保你的设备是Android4.1 +。
有些方法仅工作在Android 4.2 / 4.3,所以你最好在使用之前详细阅读uiautomator的Java文档。
该模块采用uiautomator-jsonrpc服务器作为后台程序与设备进行通信。
该模块仅在python2.7 / 3.2 / 3.3 / pypy上测试。
FAQ
1.无法启动JSONRPC服务器: raise IOError(“RPC server not started!”)
它可能是由网络,设备或环境引起的。因此,当您遇到此问题,请按照以下步骤,尝试手动启动JSONRPC服务器。
1)从下载jar文件uiautomator jsonrpc服务器。
2)Adb将下载的jar文件推送到 /data/local/tmp/
3)通过命令启动jsonrpc服务器:
adb shell uiautomator runtest bundle.jar uiautomator-stub.jar -c com.github.uiautomatorstub.Stub
4)adb将本地端口转发到设备端口:
adb forward tcp:9008 tcp:9008
5)检查jsonrpc服务器是否正常
如果你看到类似以下这样的信息:
{“jsonrpc”:”2.0”,”id”:1,”result”:
{“currentPackageName”:”android”,”displayHeight”:1280,”displayRotation”:0,”displaySizeDpX”:0,”displaySizeDpY”:0,
“displayWidth”:720,”productName”:”falcon”,”sdkInt”:17,”naturalOrientation”:true}}
则表示服务器已启动。
如果你可以手动启动jsonrpc服务器,但你的脚本总是提示IOError(“RPC server not started!”),
请提交问题 github issues。
错误 httplib.BadStatusLine: ”
JsonRPC服务器需要访问设备上的临时目录,但在一些底层设备上,它有可能抛出一些错误,在访问临时文件没有连接SD卡。因此,如果您遇到错误,请插入SD卡,然后重试。
上一篇: 在AI生态圈中 创业公司将成为领军者
下一篇: 学习C语言第一天!