WatchContext
目前的这个watch_context是用threading启动的,每2s检查一次 目前还只有click这一种触发操作
with d.watch_context() as ctx: ctx.when("^立即(下载|更新)").when("取消").click() # 当同时出现 (立即安装 或 立即取消)和 取消 按钮的时候,点击取消 ctx.when("同意").click() ctx.when("确定").click() # 上面三行代码是立即执行完的,不会有什么等待 ctx.wait_stable() # 开启弹窗监控,并等待界面稳定(两个弹窗检查周期内没有弹窗代表稳定) # 使用call函数来触发函数回调 # call 支持两个参数,d和el,不区分参数位置,可以不传参,如果传参变量名不能写错 # eg: 当有元素匹配仲夏之夜,点击返回按钮 ctx.when("仲夏之夜").call(lambda d: d.press("back")) ctx.when("确定").call(lambda el: el.click()) # 其他操作 # 为了方便也可以使用代码中默认的弹窗监控逻辑 # 下面是目前内置的默认逻辑,可以加群at群主,增加新的逻辑,或者直接提pr # when("继续使用").click() # when("移入管控").when("取消").click() # when("^立即(下载|更新)").when("取消").click() # when("同意").click() # when("^(好的|确定)").click() with d.watch_context(builtin=True) as ctx: # 在已有的基础上增加 ctx.when("@tb:id/jview_view").when('//*[@content-desc="图片"]').click() # 其他脚本逻辑
另外一种写法
ctx = d.watch_context() ctx.when("设置").click() ctx.wait_stable() # 等待界面不在有弹窗了 ctx.close()
Watcher
更推荐用WatchContext 写法更简洁一些
You can register watchers to perform some actions when a selector does not find a match.
2.0.0之前使用的是 uiautomator-jar库中提供的[Watcher]((http://developer.android.com/tools/help/uiautomator/UiWatcher.html)方法,但在实践中发现一旦uiautomator所有的watcher配置都是丢失,这肯定是无法接受的。 所以目前采用了后台运行了一个线程的方法(依赖threading库),然后每隔一段时间dump一次hierarchy,匹配到元素之后执行相应的操作。
用法举例
注册监控
# 常用写法,注册匿名监控 d.watcher.when("安装").click() # 注册名为ANR的监控,当出现ANR和Force Close时,点击Force Close d.watcher("ANR").when(xpath="ANR").when("Force Close").click() # 其他回调例子 d.watcher.when("抢红包").press("back") d.watcher.when("//*[@text = 'Out of memory']").call(lambda d: d.shell('am force-stop com.im.qq')) # 回调说明 def click_callback(d: u2.Device): d.xpath("确定").click() # 在回调中调用不会再次触发watcher d.xpath("继续").click() # 使用d.xpath检查元素的时候,会触发watcher(目前最多触发5次)
监控操作
# 移除ANR的监控 d.watcher.remove("ANR") # 移除所有的监控 d.watcher.remove() # 开始后台监控 d.watcher.start() d.watcher.start(2.0) # 默认监控间隔2.0s # 强制运行所有监控 d.watcher.run() # 停止监控 d.watcher.stop() # 停止并移除所有的监控,常用于初始化 d.watcher.reset()
另外文档还是有很多没有写,推荐直接去看源码watcher.py
Global settings
d.HTTP_TIMEOUT = 60 # 默认值60s, http默认请求超时时间 # 当设备掉线时,等待设备在线时长,仅当TMQ=true时有效,支持通过环境变量 WAIT_FOR_DEVICE_TIMEOUT 设置 d.WAIT_FOR_DEVICE_TIMEOUT = 70
其他的配置,目前已大部分集中到 d.settings
中,根据后期的需求配置可能会有增减。
print(d.settings) {'operation_delay': (0, 0), 'operation_delay_methods': ['click', 'swipe'], 'wait_timeout': 20.0, 'xpath_debug': False} # 配置点击前延时0.5s,点击后延时1s d.settings['operation_delay'] = (.5, 1) # 修改延迟生效的方法 # 其中 double_click, long_click 都对应click d.settings['operation_delay_methods'] = ['click', 'swipe', 'drag', 'press'] d.settings['xpath_debug'] = True # 开启xpath插件的调试日志 d.settings['wait_timeout'] = 20.0 # 默认控件等待时间(原生操作,xpath插件的等待时间)
对于随着版本升级,设置过期的配置时,会提示Deprecated,但是不会抛异常。
>>> d.settings['click_before_delay'] = 1 [W 200514 14:55:59 settings:72] d.settings[click_before_delay] deprecated: Use operation_delay instead
uiautomator恢复方式设置
细心的你可能发现,实际上手机安装了两个APK,一个在前台可见(小黄车)。一个包名为com.github.uiautomator.test在后台不可见。这两个apk使用同一个证书签名的。 不可见的应用实际上是一个测试包,包含有所有的测试代码,核心的测试服务也是通过其启动的。 但是运行的时候,系统却需要那个小黄车一直在运行(在后台运行也可以)。一旦小黄车应用被杀,后台运行的测试服务也很快的会被杀掉。就算什么也不做,应用应用在后台,也会很快被系统回收掉。(这里希望高手指点一下,如何才能不依赖小黄车应用,感觉理论上是可以的,但是目前我还不会)。
让小黄车在后台运行有两种方式,一种启动应用后,放到后台(默认)。另外通过am startservice启动一个后台服务也行。
通过 d.settings["uiautomator_runtest_app_background"] = True 可以调整该行为。True代表启动应用,False代表启动服务。
UiAutomator中的超时设置(隐藏方法)
>> d.jsonrpc.getConfigurator() {'actionAcknowledgmentTimeout': 500, 'keyInjectionDelay': 0, 'scrollAcknowledgmentTimeout': 200, 'waitForIdleTimeout': 0, 'waitForSelectorTimeout': 0} >> d.jsonrpc.setConfigurator({"waitForIdleTimeout": 100}) {'actionAcknowledgmentTimeout': 500, 'keyInjectionDelay': 0, 'scrollAcknowledgmentTimeout': 200, 'waitForIdleTimeout': 100, 'waitForSelectorTimeout': 0}
为了防止客户端程序响应超时,waitForIdleTimeout和waitForSelectorTimeout目前已改为0
Refs: Google uiautomator Configurator
Input method
这种方法通常用于不知道控件的情况下的输入。第一步需要切换输入法,然后发送adb广播命令,具体使用方法如下
d.set_fastinput_ime(True) # 切换成FastInputIME输入法 d.send_keys("你好123abcEFG") # adb广播输入 d.clear_text() # 清除输入框所有内容(Require android-uiautomator.apk version >= 1.0.7) d.set_fastinput_ime(False) # 切换成正常的输入法 d.send_action("search") # 模拟输入法的搜索
send_action 说明
该函数可以使用的参数有 go search send next done previous
什么时候该使用这个函数呢?
有些时候在EditText中输入完内容之后,调用press("search") or press("enter")发现并没有什么反应。 这个时候就需要send_action函数了,这里用到了只有输入法才能用的IME_ACTION_CODE。 send_action先broadcast命令发送给输入法操作IME_ACTION_CODE,由输入法完成后续跟EditText的通信。(原理我不太清楚,有了解的,提issue告诉我)
Toast (2.2版本之后有添加回来)
Show Toast
d.toast.show("Hello world") d.toast.show("Hello world", 1.0) # show for 1.0s, default 1.0s
Get Toast
# [Args] # 5.0: max wait timeout. Default 10.0 # 10.0: cache time. return cache toast if already toast already show up in recent 10 seconds. Default 10.0 (Maybe change in the furture) # "default message": return if no toast finally get. Default None d.toast.get_message(5.0, 10.0, "default message") # common usage assert "Short message" in d.toast.get_message(5.0, default="") # clear cached toast d.toast.reset() # Now d.toast.get_message(0) is None
XPath
Java uiautoamtor中默认是不支持xpath的,所以这里属于扩展的一个功能。速度不是这么的快。
For example: 其中一个节点的内容
<android.widget.TextView index="2" text="05:19" resource-id="com.netease.cloudmusic:id/qf" package="com.netease.cloudmusic" content-desc="" checkable="false" checked="false" clickable="false" enabled="true" focusable="false" focused="false" scrollable="false" long-clickable="false" password="false" selected="false" visible-to-user="true" bounds="[957,1602][1020,1636]" />
xpath定位和使用方法
有些属性的名字有修改需要注意
description -> content-desc resourceId -> resource-id
常见用法
# wait exists 10s d.xpath("//android.widget.TextView").wait(10.0) # find and click d.xpath("//*[@content-desc='分享']").click() # check exists if d.xpath("//android.widget.TextView[contains(@text, 'Se')]").exists: print("exists") # get all text-view text, attrib and center point for elem in d.xpath("//android.widget.TextView").all(): print("Text:", elem.text) # Dictionary eg: # {'index': '1', 'text': '999+', 'resource-id': 'com.netease.cloudmusic:id/qb', 'package': 'com.netease.cloudmusic', 'content-desc': '', 'checkable': 'false', 'checked': 'false', 'clickable': 'false', 'enabled': 'true', 'focusable': 'false', 'focused': 'false','scrollable': 'false', 'long-clickable': 'false', 'password': 'false', 'selected': 'false', 'visible-to-user': 'true', 'bounds': '[661,1444][718,1478]'} print("Attrib:", elem.attrib) # Coordinate eg: (100, 200) print("Position:", elem.center())
点击查看其他XPath常见用法
Screenrecord
视频录制
这里没有使用手机中自带的screenrecord命令,是通过获取手机图片合成视频的方法,所以需要安装一些其他的依赖,如imageio, imageio-ffmpeg, numpy等 因为有些依赖比较大,推荐使用镜像安装。直接运行下面的命令即可。
pip3 install -U "uiautomator2[image]" -i https://pypi.doubanio.com/simple
使用方法
d.screenrecord('output.mp4') time.sleep(10) # or do something else d.screenrecord.stop() # 停止录制后,output.mp4文件才能打开
录制的时候也可以指定fps(当前是20),这个值是率低于minicap输出图片的速度,感觉已经很好了,不建议你修改。
Image match
图像匹配,在使用这个功能之前你需要先把依赖安装上
pip3 install -U "uiautomator2[image]" -i https://pypi.doubanio.com/simple
目前开放两个接口
imdata = "target.png" # 也可以是URL, PIL.Image或OpenCV打开的图像 d.image.match(imdata) # 匹配待查找的图片,立刻返回一个结果 # 返回一个dict, eg: {"similarity": 0.9, "point": [200, 300]} d.image.click(imdata, timeout=20.0) # 在20s的时间内调用match轮询查找图片,当similarity>0.9时,执行点击操作
该功能还在完善中,图片需要手机的原图裁剪后的图才可以。
常见问题
很多没写在这个地方的,都放到了这里 Common Issues
Stop UiAutomator
停止UiAutomator守护服务
https://github.com/openatx/uiautomator2/wiki/Common-issues
因为有atx-agent的存在,Uiautomator会被一直守护着,如果退出了就会被重新启动起来。但是Uiautomator又是霸道的,一旦它在运行,手机上的辅助功能、电脑上的uiautomatorviewer 就都不能用了,除非关掉该框架本身的uiautomator。下面就说下两种关闭方法
方法1:
直接打开uiautomator app(init成功后,就会安装上的),点击关闭UIAutomator
方法2:
d.service("uiautomator").stop() # d.service("uiautomator").start() # 启动 # d.service("uiautomator").running() # 是否在运行
ATX与Maxim共存AccessibilityService的方法
Article Recommended
优秀文章推荐
termux里如何部署uiautomator2简介 by 成都-测试只会一点点
项目历史
项目重构自 https://github.com/xiaocong/uiautomator
Google UiAutomator 2.0和1.x的区别
https://www.cnblogs.com/insist8089/p/6898181.html
- 新增接口:UiObject2、Until、By、BySelector
- 引入方式:2.0中,com.android.uiautomator.core.* 引入方式被废弃。改为android.support.test.uiautomator
- 构建系统:Maven 和/或 Ant(1.x);Gradle(2.0)
- 产生的测试包的形式:从zip /jar(1.x) 到 apk(2.0)
- 在本地环境以adb命令运行UIAutomator测试,启动方式的差别:
adb shell uiautomator runtest UiTest.jar -c package.name.ClassName(1.x) adb shell am instrument -e class com.example.app.MyTest com.example.app.test/android.support.test.runner.AndroidJUnitRunner(2.0)
- 能否使用Android服务及接口? 1.x不能;2.0能。
- og输出? 使用System.out.print输出流回显至执行端(1.x); 输出至Logcat(2.0)
- 执行?测试用例无需继承于任何父类,方法名不限,使用注解 Annotation进行(2.0); 需要继承UiAutomatorTestCase,测试方法需要以test开头(1.x)
CHANGELOG (generated by pbr)
重大更新
- 1.0.0
- 移除 d.watchers.watched (会拖慢自动化的执行速度并且还会降低稳定性)
依赖项目
uiautomator守护程序 https://github.com/openatx/atx-agent
uiautomator jsonrpc serverhttps://github.com/openatx/android-uiautomator-server/