目录
1、UIautomator2框架原理
2、UIautomator2使用
2.1、安装
2.2、元素定位工具-weditor
2.3、设备连接
2.4、全局配置
2.4.1、通过settings设置
2.4.2、通过属性设置
2.5、APP相关操作
2.5.1、安装应用
2.5.2、启动应用
2.5.3、等待应用启动
2.5.4、结束应用
2.5.5、卸载应用
2.5.6、获取当前上层APP的信息
2.5.7、获取指定APP的信息
2.6、设备相关操作
2.6.1、获取设备信息
2.6.2、 获取屏幕分辨率
2.6.3、获取设备IP地址
2.6.4、锁屏亮屏
2.6.5、截屏
2.6.6、按键
2.6.7、输入法切换
2.6.8、录屏
2.6.9、文件的上传下载
2.8、元素定位
2.8.1、定位方法
2.8.2、支持的定位方法
2.8.3、 组合定位
2.8.4、子元素定位
2.8.5、兄弟元素定位
2.8.6、多级定位
2.8.9、相对定位
2.8.10、xpath定位
2.9、元素操作
2.9.1、点击
2.9.2、长按
2.9.3、滑动
2.9.4、多点滑动
2.9.5、拖动
2.9.6、放大缩小
2.9.7、滚动
2.9.8、toast操作
2.9.9、文本相关操作
2.9.10、弹窗监测
1、UIautomator2框架原理
如上图所示,python-uiautomator2 主要分为两个部分,python 客户端,移动设备
- python 端: 执行脚本,脚本通过封装成HTTP 请求发送到移动设备
- 移动设备:通过 atx-agent 来接受 HTTP 请求,并将这些请求转换为 uiautomator2可识别的指令来实现自动化操作。atx-agent 充当了一个桥梁的角色。
完整流程
- 在移动设备上安装atx-agent(守护进程), 通过atx-agent启动 uiautomator2 服务 (默认 7912 端口) 进行监听
- 在 PC 上编写测试脚本并执行(相当于发送 HTTP 请求到移动设备的 server 端)
- 移动设备通过 WIFI 或 USB 接收到 PC 上发来的 HTTP 请求,执行指定的操作
2、UIautomator2使用
2.1、安装
pip3 install -U uiautomator2
- 初始化环境,安装包含httprpc服务的apk到手机+atx-agent, minicap, minitouch,注:1.3.0之后的版本,执行python代码u2.connect()时会自动监测和推送这些文件。
python3 -m uiautomator2 init
- 检查是否安装成功
- cmd
uiautomator2 verison
- Python
import uiautomator2 as u2
d = u2.connect() # 连接设备
print(d.info)
2.2、元素定位工具-weditor
1、安装:
- 注意:这里指定安装0.6.4版本的,如果默认安装最新的,很可能会出现安装失败的问题,这里推荐指定安装。
pip install weditor==0.6.4
2、启动weditor
python -m weditor
3、连接Android设备
2.3、设备连接
1、默认一个设备的情况
import uiautomator2 as u2
# 连接设备
d = u2.connect()
# 当PC与设备在同一网段时,可以使用IP地址和端口号通过WIFI连接,无需连接USB线
# 默认使用端口号7912
u2.connect("10.0.0.1:7912")
u2.connect("10.0.0.1")
u2.connect("http://10.0.0.1")
u2.connect("http://10.0.0.1:7912")
等价于
u2.connect_wifi("10.0.0.1:7912")
2、多个设备时指定设备连接
import uiautomator2 as u2
# 连接设备,xxx:表示指定设备的设备号,可使用命令adb devices查看
d = u2.connect("xxx")
等价于
d = u2.connect_usb("xxx")
2.4、全局配置
2.4.1、通过settings设置
1、查看settings默认设置
print(d.settings)
# 默认配置如下:
{
# 截屏失败时返回空白截屏(不抛出异常)
'fallback_to_blank_screenshot': False,
# 操作延迟,(0, 0)表示元素点击前等待0秒,点击后等待0S再执行后续操作
'operation_delay': (0, 0),
# opretion_delay生效的方法,默认为click和swipe
# 还可以设置press,send_keys,long_click等方式
'operation_delay_methods': ['click', 'swipe'],
# 元素默认等待时间(隐式等待)
'wait_timeout': 20.0,
# xpath日志
'xpath_debug': False
}
2、通过settIng修改默认配置
# 修改截图失败后的默认返回
d.settings["fallback_to_blank_screenshot"] = True
# 修改操作延迟时间
d.settings["operation_delay"] = (3, 3)
# 修改操作延迟方法
d.settings["operation_delay_methods"] = ['click', 'swipe', 'press']
# 修改默认等待时间
d.settings["wait_timeout"] = 10
# 查看是否修改成功
print(d.settings)
2.4.2、通过属性设置
1、http默认请求超时时间
# 默认值60s
d.HTTP_TIMEOUT = 60
2、等待设备在线时长
# 默认20s
d.WAIT_FOR_DEVICE_TIMEOUT = 60
3、HTTP debug信息
d.debug = True
2.5、APP相关操作
2.5.1、安装应用
# 本地路径安装
d.app_install('tmp.apk')
# url安装
d.app_install('package_url')
2.5.2、启动应用
# 如果已启动且在后台会被拉起到前台
# 如果已启动且在前台,不做任何操作,结束
# 如果未启动,则冷启动该应用
d.app_start("packageName")
# 先kill掉APP,再冷启动APP
d.app_start("packageName", stop=True)
2.5.3、等待应用启动
# 等待此应用变为当前上层应用,返回pid,超时未启动成功则返回0
# front默认为false表示只要该应用在运行中就会返回pid 为true表示等待app成为当前app
d.app_wait('com.xxx', 30, front=True)
2.5.4、结束应用
# 通过包名结束单个应用
d.app_stop("packageName") # 强制结束应用,但是不清除应用数据
d.app_clear('packageName') # 结束应用,并且清除应用数据
# 默认结束所有第三方应用,保留uiautomator两个依赖服务应用
# excludes参数:排除列表中的应用包名
d.app_stop_all(excludes=['com.xxx.xxx'])
2.5.5、卸载应用
# 卸载成功返回true,没有此包或者卸载失败返回False
d.app_uninstall('com.xxx.xxx')
2.5.6、获取当前上层APP的信息
d.app_current()
2.5.7、获取指定APP的信息
d.app_info("com.XXX")
2.6、设备相关操作
2.6.1、获取设备信息
# 输出设备的详细信息,报告设备号,电池,CPU信息等
d.device_info
2.6.2、 获取屏幕分辨率
# 手机竖屏状态返回 (1080,2400)
# 横屏状态返回 (2400,1080)
d.window_size()
2.6.3、获取设备IP地址
d.wlan_ip
2.6.4、锁屏亮屏
# 锁屏
d.screen_off()
# 亮屏
d.screen_on()
2.6.5、截屏
# 截图,save(fp) 传文件路径地址
d.screenshot().save("./tmp.png")
from PIL import Image
# 旋转90度截屏(逆时针旋转)
d.screenshot().transpose(Image.ROTATE_90).save("./tmp2.png")
2.6.6、按键
# 按音量键"+"
d.press('volume_up')
"""
press key via name or key code. Supported key name includes:
home, back, left, right, up, down, center, menu, search, enter,
delete(or del), recent(recent apps), volume_up, volume_down,
volume_mute, camera, power.
"""
2.6.7、输入法切换
# 切换成uiautomator2的输入法,这里会隐藏掉系统原本的输入法,默认是使用系统输入法
# 当传入False时会使用系统默认输入法,默认为Fasle
d.set_fastinput_ime(True)
# 查看当前输入法
d.current_ime()
2.6.8、录屏
- 需要下载依赖,官方推荐使用镜像下载
pip3 install -U "uiautomator2[image]" -i https://pypi.doubanio.com/simple
# 启动录制,默认帧率为20
d.screenrecord('tmp.mp4')
# 执行其它操作
time.sleep(10)
# 停止录制,录制结束后生成视频
d.screenrecord.stop()
2.6.9、文件的上传下载
# 上传文件(从电脑推送到手机) 如果是目录,这里"/sdcrad/"最后一个斜杠一定要加,否则会报错
d.push("test.txt","/sdcrad/")
d.push("test.txt","/sdcrad/test.txt")
# 下载文件(从手机推送到电脑)
d.pull('/sdcard/test.txt','text.txt')
2.7、等待
说明:等待分为强制等待和隐式等待;
- 强制等待:不管元素是否加载,都强制等待指定时间,实际根据具体情况进行配置,只在使用的地方生效。
- 隐式等待:在规定时间内如果元素可操作,直接进行操作;不必强制等到指定的时间;如果超过规定的时间,元素不可进行操作会抛出异常。一般会在全局配置的时候进行统一配置,配置每一步的操作都会生效。
# 设置强制等待的时间为3秒,等价于:time.sleep(3)
d.sleep(3)
# 设置隐式等待的时间为10
d.implicitly_wait(10)
2.8、元素定位
2.8.1、定位方法
d(定位方法=定位方法的值)
# 示例
#返回一个列表,当没找到元素时,返回一个空列表;存在多个元素时,返回多个列表元素
elements = d(text='Setting')
elements[0].click()
#获取元素个数
print(elements.count)
2.8.2、支持的定位方法
定位方法 | 描述 |
text | text是指定文本的元素 |
textContains | text中包含有指定文本的元素 |
textMatches | text符合指定正则的元素 |
textStartsWith | text以指定文本开头的元素 |
className | className是指定类名的元素 |
classNameMatches | className类名符合指定正则的元素 |
description | description是指定文本的元素 |
descriptionContains | description中包含有指定文本的元素 |
descriptionMatches | description符合指定正则的元素 |
descriptionStartsWith | description以指定文本开头的元素 |
checkable | 可检查的元素,参数为True,False |
checked | 已选中的元素,通常用于复选框,参数为True,False |
clickable | 可点击的元素,参数为True,False |
longClickable | 可长按的元素,参数为True,False |
scrollable | 可滚动的元素,参数为True,False |
enabled | 已激活的元素,参数为True,False |
focusable | 可聚焦的元素,参数为True,False |
focused | 获得了焦点的元素,参数为True,False |
selected | 当前选中的元素,参数为True,False |
packageName | packageName为指定包名的元素 |
packageNameMatches | packageName为符合正则的元素 |
resourceId | resourceId为指定内容的元素 |
resourceIdMatches | resourceId为符合指定正则的元素 |
2.8.3、 组合定位
- 通过不同属性的组合来完成最终元素的定位,注意:该方式定位一定是同一个层级的,如果是不同层级的则会定位失败。
d(className="xxx", text="xxx")
2.8.4、子元素定位
- 该方式定义一定是父子层级的关系才能成功定位,不然会定位失败。
d(className="xxx").child(text="xxx")
2.8.5、兄弟元素定位
- 注意:返回结果包含元素自己本身
d(text="xxx").sibling(className="android.widget.TextView")
2.8.6、多级定位
d(className="xxx", resourceId="xxx").child_by_text("xxx").child(className="xxxx")
2.8.9、相对定位
d(A).left(B),# 选择A左边的B
d(A).right(B),# 选择A右边的B
d(A).up(B), #选择A上边的B
d(A).down(B),# 选择A下边的B
# 示例
d(text='xxx').right(className="android.widget.TextView")
2.8.10、xpath定位
- 注意事项:
- Java uiautoamtor默认是不支持xpath,这是属于uiautoamtor2的扩展功能,速度会比其它定位方式慢
- 在xpath定位中,uiautoamtor2中的description 定位需要替换为content-desc,resourceId 需要替换为resource-id
# 如果找不到元素,则会报XPathElementNotFoundError错误
# 如果找到多个元素,默认返回第1个
d.xpath('//*[@content-desc="xxx"]')
# 如果想要返回的元素有多个,使用all()方法返回列表
# 使用all方法,当未找到元素时,不会报错,会返回一个空列表
d.xpath('//*[@resource-id="xxx"]').all()
2.9、元素操作
2.9.1、点击
d(text='xxx').click()
#单击直到元素消失,超时时间5,点击间隔1
d(text='xxx').click_gone(maxretry=5, interval=1.0)
2.9.2、长按
d(text='xxx').long_click()
2.9.3、滑动
# 根据坐标滑动从(x1,y1)滑动到(x2,y2)
d.swipe(x1,y1,x1,y2)
# 滑动的扩展方法,支持上下左右的滑动
# "left", "right", "up", "down"
d.swipe_ext("up")
2.9.4、多点滑动
- 可用来实现图案解锁
# 按下不放手
touch.down(x,y)
# 停住1S
touch.sleep(1)
# 移动
touch.move(x,y)
# 放开
touch.up(x,y)
#实现长按,同一个点按下休眠10S后抬起
d.touch.down(x1,y1).sleep(10).up(x1,y1)
# 实现多点之间的移动
d.touch.down(x1,y1).move(x2,y2).move(x3,y3).up(x3,y3)
2.9.5、拖动
# 在0.5s内将设置拖动至QQ上,拖动元素的中心位置
# duration默认为0.5,实际拖动的时间会比设置的要高
d(description="设置").drag_to(text="QQ", duration=0.5)
# 拖动设置到屏幕的(500, 500)位置上
d(text="设置").drag_to(500,500, duration=0.5)
# 从点(x1,y1)拖动到点(x2,y2)
d.drag(x1,y1,x2,y2)
2.9.6、放大缩小
# 根据坐标进行放大和缩小
d(className="android.widget.FrameLayout").gesture(start1,start2,end1,end2)
# 缩小
d(className="android.widget.FrameLayout").pinch_in(50, 50)
# 放大
d(className="android.widget.FrameLayout").pinch_out(10, 10)
2.9.7、滚动
- 说明:
- 设置scrollable属性为True
- 滚动类型:horiz 为水平 vert 为垂直
- 所有方法均返回Bool值
- 滚动方向:forward 向前 、backward 向后 、toBeginning 滚动至开始 、toEnd 滚动至最后 、to 滚动直接某个元素出现
# 垂直滚动到页面顶部/横向滚动到最左侧
d(scrollable=True).scroll.toBeginning()
d(scrollable=True).scroll.horiz.toBeginning()
# 垂直滚动到页面最底部/横向滚动到最右侧
d(scrollable=True).scroll.toEnd()
d(scrollable=True).scroll.horiz.toEnd()
# 垂直向后滚动到指定位置/横向向右滚动到指定位置
d(scrollable=True).scroll.to(text="指定位置")
d(scrollable=True).scroll.horiz.to(text="指定位置")
# 垂直向前滚动(横向同理)
d(scrollable=True).scroll.forward()
# 垂直向前滚动到指定位置(横向同理)
d(scrollable=True).scroll.forward.to(text="指定位置")
2.9.8、toast操作
# 获取toast,当没有找到toast消息时,返回default内容
d.toast.get_message(timout=3, default='no toast')
# 清空toast缓存
d.toast.reset()
2.9.9、文本相关操作
# 获取元素文本
d(text="xxx").get_text()
# 设置元素文本
d(text="xxx").set_text()
# 清除元素文本
d(text="xxx").clear_text()
2.9.10、弹窗监测
- 使用wather进行页面监测,可以用来实现弹框的监测处理
- 当启动wather时,会新建一个线程进行监控 ,可以添加多个watcher
# 注册监控,当页面内出现有OK时,点击OK
d.watcher.when('OK').click()
# 移除 allow 的监控
d.watcher.remove("allow")
# 移除所有的监控
d.watcher.remove()
# 开始后台监控
d.watcher.start()
d.watcher.start(2.0) # 默认监控间隔2.0s
# 强制运行所有监控
d.watcher.run()
# 停止监控
d.watcher.stop()
# 停止并移除所有的监控,常用于初始化
d.watcher.reset()