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

简介: WatchContext,目前的这个watch_context是用threading启动的,每2s检查一次 目前还只有click这一种触发操作

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/


相关文章
|
6天前
|
Python
手撕Python!模块、包、库,傻傻分不清?一分钟带你弄明白!
手撕Python!模块、包、库,傻傻分不清?一分钟带你弄明白!
19 1
|
6天前
|
安全 程序员 API
几个被淘汰的Python库,请不要再用!
几个被淘汰的Python库,请不要再用!
18 0
|
2天前
|
存储 JSON API
Pydantic:目前最流行的Python数据验证库
在处理来自系统外部的数据,如API、终端用户输入或其他来源时,我们必须牢记开发中的一条基本原则:“永远不要相信用户的输入”。 因此,我们必须对这些数据进行严格的检查和验证,确保它们被适当地格式化和标准化。这样做的目的是为了确保这些数据符合我们的程序所需的输入规范,从而保障项目能够正确且高效地运行。
|
3天前
|
存储 缓存 索引
Python中的NumPy库详解
Python中的NumPy库详解
|
6天前
|
XML 自然语言处理 关系型数据库
CasADi - 最优控制开源 Python/MATLAB 库4
CasADi - 最优控制开源 Python/MATLAB 库
17 4
|
6天前
|
Linux API C++
CasADi - 最优控制开源 Python/MATLAB 库3
CasADi - 最优控制开源 Python/MATLAB 库
20 4
|
6天前
|
算法 数据可视化 机器人
Pinocchio - 开源多刚体动力学 C++、Python库
Pinocchio - 开源多刚体动力学 C++、Python库
9 2
|
5天前
|
存储 网络协议 Python
Python如何用PyModbus库进行Modbus TCP通信
使用python解决工业通信问题是一个非常好的选择,python具有丰富的生态,可以轻松解决工业通信的各种问题。 本篇主要介绍使用pymodbus库进行modbus tcp仿真,实现pc端读取plc或工业设备modbus变量。
|
6天前
|
自然语言处理 算法 API
CasADi - 最优控制开源 Python/MATLAB 库2
CasADi - 最优控制开源 Python/MATLAB 库
11 0
|
6天前
|
存储 API C++
CasADi - 最优控制开源 Python/MATLAB 库1
CasADi - 最优控制开源 Python/MATLAB 库
12 0