uiautomator简介
UiAutomator是Google提供的用来做安卓自动化测试的一个Java库,基于Accessibility服务。功能很强,可以对第三方App进行测试,获取屏幕上任意一个APP的任意一个控件属性,并对其进行任意操作,但有两个缺点:1. 测试脚本只能使用Java语言 2. 测试脚本要打包成jar或者apk包上传到设备上才能运行
uiautomator2是对uiautomator的改进,使其能够用Python编写,能够在电脑上运行的时候就控制手机,原理是在手机上运行了一个http rpc服务,将uiautomator中的功能开放出来,然后再将这些http接口封装成Python库
github地址:https://github.com/openatx/uiautomator2
uiautomator2 除了对原有的库的bug进行了修复,还增加了很多新的Feature。主要有以下部分:
设备和开发机可以脱离数据线,通过WiFi互联(基于atx-agent)
集成了openstf/minicap达到实时屏幕投频,以及实时截图
集成了openstf/minitouch达到精确实时控制设备
修复了xiaocong/uiautomator经常性退出的问题
代码进行了重构和精简,方便维护
实现了一个设备管理平台(也支持iOS) atxserver2
扩充了toast获取和展示的功能
安装
安装 uiautomator2
pip install --upgrade --pre uiautomator2 # 因为uiautomator2仍在开发中,你必须添加——pre来安装开发版本
测试是否安装成功 uiautomator2 --help
安装 weditor (UI Inspector)
因为uiautomator是独占资源,所以当atx运行的时候uiautomatorviewer是不能用的,为了减少atx频繁的启停,可以使用基于浏览器技术的weditor UI查看器。https://github.com/openatx/weditor
安装方法(备注: 目前最新的稳定版为 0.1.0)
安装 daemons to a device (Optional)
电脑连接上一个手机或多个手机, 确保adb已经添加到环境变量中,执行下面的命令会自动安装本库所需要的设备端程序:uiautomator-server
、atx-agent
、openstf/minicap
、openstf/minitouch
# 初始化 所有的已经连接到电脑的设备
python -m uiautomator2 init
安装提示success
即可
UiAutomator 常用命令
命令行常用命令
screenshot: 截图
uiautomator2 screenshot screenshot.jpg
current: 获取当前包名和activity
$ uiautomator2 current
{
"package" : "com.xueqiu.android" ,
"activity" : "com.xueqiu.android.stockmodule.stockdetail.StockDetailActivity"
}
install: 安装apk
$ uiautomator2 install com.android.chrome_81.0 .4044 .117 _404411700.apk
[D 221119 15 :09 :15 __init__:1295 ] pm install - rt / data/ local / tmp/ _tmp.apk
Installed None
官方说不能使用,但我实测可以安装成功
uninstall: 卸载
$ uiautomator2 uninstall <package -name>
$ uiautomator2 uninstall <package -name-1 > <package -name-2 >
$ uiautomator2 uninstall --all
$ uiautomator2 uninstall com.android.chrome
Uninstall "com.android.chrome" OK
stop: 停止应用
$ uiautomator2 stop com.example.app
$ uiautomator2 stop --all
$ uiautomator2 stop com.xueqiu.android
am force-stop "com.xueqiu.android"
设置超时时间
在假设客户端退出并结束uiautomator服务之前,等待来自客户端的新命令的时间(秒)(默认为3分钟)
d.set_new_command_timeout(300)
开启debug模式
d.debug = True
print (d.info)
15 :37 :11.340 $ curl -X POST -d '{"jsonrpc" : "2.0" , "id" : "c78bf70891f2fb0b99b082a40faa6e76" , "method" : "deviceInfo" , "params" : {}}' ' http:
15 :37 :11.483 Response (142 ms) >>>
{"jsonrpc" :"2.0" ,"id" :"c78bf70891f2fb0b99b082a40faa6e76" ,"result" :{"currentPackageName" :"com.xueqiu.android" ,"displayHeight" :1872 ,"displayRotation" :0 ,"displaySizeDpX" :450 ,"displaySizeDpY" :720 ,"displayWidth" :1170 ,"productName" :"cancro_x64" ,"screenOn" :true ,"sdkInt" :23 ,"naturalOrientation" :true }}
<<< END
{'currentPackageName ': 'com .xueqiu.android', 'displayHeight ': 1872 , 'displayRotation ': 0 , 'displaySizeDpX ': 450 , 'displaySizeDpY ': 720 , 'displayWidth ': 1170 , 'productName ': 'cancro_x64 ', 'screenOn ': True, 'sdkInt ': 23 , 'naturalOrientation ': True}
设置Implicit wait
设置元素查找等待时间(默认20s)
d.implicitly_wait(10.0)
d(text="Settings" ).click()
print ("wait timeout" , d.implicitly_wait())
app 管理
安装APP
d.app_install('https://down11.zol.com.cn/liaotian/huoshan15.2.0w.apk' )
启动APP
d.app_start("com.xueqiu.android" )
d.app_start("com.xueqiu.android" ,stop=True )
d.app_start("com.example.hello_world" , use_monkey=True )
d.app_start("com.example.hello_world" , ".MainActivity" )
关闭APP
d.app_stop("com.xueqiu.android" )
d.app_clear("com.xueqiu.android" )
关闭所有正则运行的APP
d.app_stop_all()
d.app_stop_all(excludes=['com.xueqiu.android' ])
获取APP信息
print (d.app_info('com.xueqiu.android' ))
# {'packageName' : 'com.xueqiu.android' , 'mainActivity' : 'com.xueqiu.android.common.splash.SplashActivity' , 'label' : '雪球股票' , 'versionName' : '12.18.1' , 'versionCode' : 280 , 'size' : 88270498 }
img = d.app_icon("com.xueqiu.android" ) # 获取APP图标
img.save('xueqiu.jpg' )
获取正在运行的APP
print (d.app_list_running())
# ['com.github.uiautomator' , 'com.android.systemui' , 'com.xueqiu.android' ]
等待应用程序运行
pid = d.app_wait('com.xueqiu.android' )
if pid:
print (f"com.xueqiu.android 运行成功" )
else :
print ("运行失败" )
d.app_wait("com.example.android" , front=True)
d.app_wait("com.example.android" , timeout =20.0)
上传下载文件
上传文件
d.push('xueqiu.jpg' ,'/sdcard/' )
d.push('xueqiu.jpg' ,'/sdcard/xue.jpg' )
with open ("xueqiu.jpg" , 'rb' ) as f:
d.push(f, "/sdcard/" )
d.push("xueqiu.jpg" , "/data/local/tmp/" , mode=0o755 )
下载文件
d.pull('/data/local/tmp/xueqiu.mp4' ,'xueqiu.mp4' )
指定APP打开链接
d.open_url("https://www.baidu.com" )
d.open_url("taobao://taobao.com" )
d.open_url("appname://appnamehost" )
执行shell命令
d.shell("pwd" , timeout=60 )
print (d.shell("pwd" ))
output = d.shell("pwd" ).output
exit_code = d.shell("pwd" ).exit_code
d.shell(["ls" , "-l" ])
当执行需要长期输出的命令时,需加stream=True
,否则代码讲一直在执行,直到超时失败
import time
r = d.shell("logcat" ,stream=True )
deadline = time.time() + 10
try :
for line in r.iter_lines():
if time.time() > deadline:
break
print (line.decode("utf-8" ))
finally :
r.close()
Session
Session 代表一个应用程序生命周期。可以用来启动应用程序,检测应用程序崩溃
目前session方法以停用,预计在3.0版本中再启用
启动和关闭APP
sess = d.session('com.xueqiu.android' )
sess.app_stop('com.xueqiu.android' )
基本操作
基本命令
print (d.info)
print (d.window_size())
print (d.app_current())
print (d.wait_activity("com.xueqiu.android.main.view.MainActivity" , timeout=10 ))
print (d.serial)
print (d.wlan_ip)
print (d.device_info)
d.set_clipboard('text' , 'label' )
print (d.clipboard)
屏幕操作
d.screen_on()
d.screen_off()
print (d.info.get('screenOn' ))
d.press("home" )
d.press("back" )
d.press(0x07 , 0x02 )
d.unlock()
d.click(100 , 200 )
d.double_click(100 ,200 ,0.1 )
d.long_click(100 , 200 , 0.5 )
d.long_click(0.5 , 0.5 )
d.swipe(580 , 1400 , 580 , 400 , 0.5 )
SwipeExt 扩展功能
d.swipe_ext("right" )
d.swipe_ext("right" , scale=0.9 )
d.swipe_ext("right" , box=(0 , 0 , 100 , 100 ))
d.swipe_ext("up" , scale=0.8 )
from uiautomator2 import Direction
d.swipe_ext(Direction.FORWARD)
d.swipe_ext(Direction.BACKWARD)
d.swipe_ext(Direction.HORIZ_FORWARD)
d.swipe_ext(Direction.HORIZ_BACKWARD)
d.drag(288 , 880 , 800 , 880 )
d.drag(288 , 880 , 800 , 880 , 0.5 )
d.swipe_points([(300 , 700 ), (700 , 700 ), (700 , 1400 )], 0.2 )
d.touch.down(10 , 10 )
time.sleep(.01 )
d.touch.move(15 , 15 )
d.touch.up()
屏幕设置
d.set_orientation('l' )
d.set_orientation("u" )
d.set_orientation("r" )
d.set_orientation("n" )
d.freeze_rotation()
d.freeze_rotation(False )
d.screenshot("home.jpg" )
image = d.screenshot()
image.save("home.jpg" )
import cv2
image = d.screenshot(format ='opencv' )
cv2.imwrite('home.jpg' , image)
imagebin = d.screenshot(format ='raw' )
open ("some.jpg" , "wb" ).write(imagebin)
xml = d.dump_hierarchy()
print (xml)
d.open_notification()
d.open_quick_settings()
选择器
选择器是一种方便的机制,可以在当前窗口中标识特定的UI对象。
演示用例:
<?xml version='1.0' encoding='UTF-8' standalone='yes' ?>
<hierarchy rotation ="0" >
<node index ="0" text ="" resource-id ="" class ="android.widget.FrameLayout" package ="com.android.systemui" 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 ="[0,0][1170,62]" >
<node index ="1" text ="" resource-id ="com.android.systemui:id/status_bar" class ="android.widget.FrameLayout" package ="com.android.systemui" 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 ="[0,0][1170,62]" >
<node index ="0" text ="" resource-id ="com.android.systemui:id/status_bar_contents" class ="android.widget.LinearLayout" package ="com.android.systemui" 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 ="[0,0][1170,62]" >
<node index ="0" text ="" resource-id ="com.android.systemui:id/notification_icon_area" class ="android.widget.FrameLayout" package ="com.android.systemui" 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 ="[16,0][1007,62]" >
<node index ="0" text ="" resource-id ="com.android.systemui:id/notification_icon_area_inner" class ="android.widget.LinearLayout" package ="com.android.systemui" 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 ="[16,0][1007,62]" >
<node index ="0" text ="" resource-id ="com.android.systemui:id/notificationIcons" class ="android.widget.LinearLayout" package ="com.android.systemui" 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 ="[16,0][946,62]" >
<node index ="0" text ="" resource-id ="" class ="android.widget.ImageView" package ="com.android.systemui" content-desc ="UIAutomator service started" 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 ="[25,9][69,53]" />
</node >
</node >
</node >
<node index ="1" text ="" resource-id ="com.android.systemui:id/system_icon_area" class ="android.widget.LinearLayout" package ="com.android.systemui" 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 ="[1007,0][1149,62]" >
<node index ="0" text ="" resource-id ="com.android.systemui:id/system_icons" class ="android.widget.LinearLayout" package ="com.android.systemui" 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 ="[1007,0][1079,62]" >
<node index ="1" text ="" resource-id ="com.android.systemui:id/signal_cluster" class ="android.widget.LinearLayout" package ="com.android.systemui" 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 ="[1014,9][1079,53]" >
<node index ="0" text ="" resource-id ="com.android.systemui:id/wifi_combo" class ="android.widget.FrameLayout" package ="com.android.systemui" content-desc ="WLAN 信号满格。" 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 ="[1014,9][1061,53]" >
<node index ="1" text ="" resource-id ="com.android.systemui:id/wifi_signal" class ="android.widget.ImageView" package ="com.android.systemui" 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 ="[1014,9][1061,53]" />
</node >
</node >
</node >
<node index ="1" text ="1:31" resource-id ="com.android.systemui:id/clock" class ="android.widget.TextView" package ="com.android.systemui" 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 ="[1079,0][1149,62]" />
</node >
</node >
</node >
<node index ="2" text ="" resource-id ="com.android.systemui:id/scrim_behind" class ="android.view.View" package ="com.android.systemui" 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 ="[0,0][1170,62]" />
<node index ="3" text ="" resource-id ="com.android.systemui:id/panel_holder" class ="android.widget.FrameLayout" package ="com.android.systemui" 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 ="[0,0][1170,62]" />
<node index ="4" text ="" resource-id ="com.android.systemui:id/scrim_in_front" class ="android.view.View" package ="com.android.systemui" 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 ="[0,0][1170,62]" />
</node >
<node index ="0" text ="" resource-id ="" class ="android.widget.FrameLayout" package ="com.android.settings" 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 ="[0,0][1170,1872]" >
<node index ="0" text ="" resource-id ="android:id/decor_content_parent" class ="android.view.ViewGroup" package ="com.android.settings" 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 ="[0,0][1170,1872]" >
<node index ="0" text ="" resource-id ="android:id/action_bar_container" class ="android.widget.FrameLayout" package ="com.android.settings" 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 ="[0,62][1170,208]" >
<node index ="0" text ="" resource-id ="android:id/action_bar" class ="android.view.ViewGroup" package ="com.android.settings" 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 ="[0,62][1170,208]" >
<node index ="0" text ="设置" resource-id ="" class ="android.widget.TextView" package ="com.android.settings" 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 ="[41,100][145,170]" />
<node index ="1" text ="" resource-id ="" class ="android.widget.LinearLayout" package ="com.android.settings" 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 ="[1045,62][1170,208]" >
<node index ="0" text ="" resource-id ="com.android.settings:id/search" class ="android.widget.TextView" package ="com.android.settings" content-desc ="搜索设置" checkable ="false" checked ="false" clickable ="true" enabled ="true" focusable ="true" focused ="false" scrollable ="false" long-clickable ="true" password ="false" selected ="false" visible-to-user ="true" bounds ="[1045,72][1170,197]" />
</node >
</node >
</node >
<node index ="1" text ="" resource-id ="android:id/content" class ="android.widget.FrameLayout" package ="com.android.settings" 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 ="[0,208][1170,1872]" >
<node index ="0" text ="" resource-id ="com.android.settings:id/main_content" class ="android.widget.FrameLayout" package ="com.android.settings" 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 ="[0,208][1170,1872]" >
<node index ="0" text ="" resource-id ="com.android.settings:id/dashboard" class ="android.widget.ScrollView" package ="com.android.settings" content-desc ="" checkable ="false" checked ="false" clickable ="false" enabled ="true" focusable ="true" focused ="false" scrollable ="true" long-clickable ="false" password ="false" selected ="false" visible-to-user ="true" bounds ="[0,208][1170,1872]" >
<node index ="0" text ="" resource-id ="com.android.settings:id/dashboard_container" class ="android.widget.LinearLayout" package ="com.android.settings" 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 ="[0,208][1170,1872]" >
<node index ="0" text ="" resource-id ="com.android.settings:id/category" class ="android.widget.LinearLayout" package ="com.android.settings" 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 ="[0,208][1170,894]" >
<node index ="0" text ="无线和网络" resource-id ="com.android.settings:id/category_title" class ="android.widget.TextView" package ="com.android.settings" 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 ="[0,208][1170,333]" />
<node index ="1" text ="" resource-id ="com.android.settings:id/category_content" class ="android.view.ViewGroup" package ="com.android.settings" 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 ="[0,333][1170,894]" >
<node index ="0" text ="" resource-id ="" class ="android.widget.FrameLayout" package ="com.android.settings" content-desc ="" checkable ="false" checked ="false" clickable ="true" enabled ="true" focusable ="true" focused ="false" scrollable ="false" long-clickable ="false" password ="false" selected ="false" visible-to-user ="true" bounds ="[0,333][1170,520]" >
<node index ="0" text ="" resource-id ="" class ="android.widget.LinearLayout" package ="com.android.settings" 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 ="[0,333][1170,520]" >
<node index ="0" text ="" resource-id ="com.android.settings:id/icon" class ="android.widget.ImageView" package ="com.android.settings" 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 ="[42,395][104,457]" />
<node index ="1" text ="" resource-id ="" class ="android.widget.LinearLayout" package ="com.android.settings" 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 ="[187,333][1170,520]" >
<node index ="0" text ="" resource-id ="" class ="android.widget.LinearLayout" package ="com.android.settings" 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 ="[187,333][1170,517]" >
<node index ="0" text ="" resource-id ="" class ="android.widget.RelativeLayout" package ="com.android.settings" 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 ="[187,396][304,453]" >
<node index ="0" text ="WLAN" resource-id ="com.android.settings:id/title" class ="android.widget.TextView" package ="com.android.settings" 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 ="[187,396][304,453]" />
</node >
</node >
<node index ="1" text ="" resource-id ="com.android.settings:id/tile_divider" class ="android.view.View" package ="com.android.settings" 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 ="[187,517][1170,520]" />
</node >
</node >
</node >
<node index ="1" text ="" resource-id ="" class ="android.widget.FrameLayout" package ="com.android.settings" content-desc ="" checkable ="false" checked ="false" clickable ="true" enabled ="true" focusable ="true" focused ="false" scrollable ="false" long-clickable ="false" password ="false" selected ="false" visible-to-user ="true" bounds ="[0,520][1170,707]" >
<node index ="0" text ="" resource-id ="" class ="android.widget.LinearLayout" package ="com.android.settings" 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 ="[0,520][1170,707]" >
<node index ="0" text ="" resource-id ="com.android.settings:id/icon" class ="android.widget.ImageView" package ="com.android.settings" 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 ="[42,582][104,644]" />
<node index ="1" text ="" resource-id ="" class ="android.widget.LinearLayout" package ="com.android.settings" 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 ="[187,520][1170,707]" >
<node index ="0" text ="" resource-id ="" class ="android.widget.LinearLayout" package ="com.android.settings" 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 ="[187,520][1170,704]" >
<node index ="0" text ="" resource-id ="" class ="android.widget.RelativeLayout" package ="com.android.settings" 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 ="[187,583][271,640]" >
<node index ="0" text ="蓝牙" resource-id ="com.android.settings:id/title" class ="android.widget.TextView" package ="com.android.settings" 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 ="[187,583][271,640]" />
</node >
</node >
<node index ="1" text ="" resource-id ="com.android.settings:id/tile_divider" class ="android.view.View" package ="com.android.settings" 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 ="[187,704][1170,707]" />
</node >
</node >
</node >
<node index ="2" text ="" resource-id ="" class ="android.widget.FrameLayout" package ="com.android.settings" content-desc ="" checkable ="false" checked ="false" clickable ="true" enabled ="true" focusable ="true" focused ="false" scrollable ="false" long-clickable ="false" password ="false" selected ="false" visible-to-user ="true" bounds ="[0,707][1170,894]" >
<node index ="0" text ="" resource-id ="" class ="android.widget.LinearLayout" package ="com.android.settings" 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 ="[0,707][1170,894]" >
<node index ="0" text ="" resource-id ="com.android.settings:id/icon" class ="android.widget.ImageView" package ="com.android.settings" 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 ="[42,769][104,831]" />
<node index ="1" text ="" resource-id ="" class ="android.widget.LinearLayout" package ="com.android.settings" 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 ="[187,707][1170,894]" >
<node index ="0" text ="" resource-id ="" class ="android.widget.LinearLayout" package ="com.android.settings" 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 ="[187,707][1170,894]" >
<node index ="0" text ="" resource-id ="" class ="android.widget.RelativeLayout" package ="com.android.settings" 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 ="[187,772][271,829]" >
<node index ="0" text ="更多" resource-id ="com.android.settings:id/title" class ="android.widget.TextView" package ="com.android.settings" 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 ="[187,772][271,829]" />
</node >
</node >
</node >
</node >
</node >
</node >
</node >
<node index ="1" text ="" resource-id ="com.android.settings:id/category" class ="android.widget.LinearLayout" package ="com.android.settings" 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 ="[0,915][1170,1872]" >
<node index ="0" text ="设备" resource-id ="com.android.settings:id/category_title" class ="android.widget.TextView" package ="com.android.settings" 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 ="[0,915][1170,1040]" />
<node index ="1" text ="" resource-id ="com.android.settings:id/category_content" class ="android.view.ViewGroup" package ="com.android.settings" 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 ="[0,1040][1170,1872]" >
<node index ="0" text ="" resource-id ="" class ="android.widget.FrameLayout" package ="com.android.settings" content-desc ="" checkable ="false" checked ="false" clickable ="true" enabled ="true" focusable ="true" focused ="false" scrollable ="false" long-clickable ="false" password ="false" selected ="false" visible-to-user ="true" bounds ="[0,1040][1170,1227]" >
<node index ="0" text ="" resource-id ="" class ="android.widget.LinearLayout" package ="com.android.settings" 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 ="[0,1040][1170,1227]" >
<node index ="0" text ="" resource-id ="com.android.settings:id/icon" class ="android.widget.ImageView" package ="com.android.settings" 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 ="[42,1102][104,1164]" />
<node index ="1" text ="" resource-id ="" class ="android.widget.LinearLayout" package ="com.android.settings" 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 ="[187,1040][1170,1227]" >
<node index ="0" text ="" resource-id ="" class ="android.widget.LinearLayout" package ="com.android.settings" 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 ="[187,1040][1170,1224]" >
<node index ="0" text ="" resource-id ="" class ="android.widget.RelativeLayout" package ="com.android.settings" 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 ="[187,1103][271,1160]" >
<node index ="0" text ="显示" resource-id ="com.android.settings:id/title" class ="android.widget.TextView" package ="com.android.settings" 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 ="[187,1103][271,1160]" />
</node >
</node >
<node index ="1" text ="" resource-id ="com.android.settings:id/tile_divider" class ="android.view.View" package ="com.android.settings" 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 ="[187,1224][1170,1227]" />
</node >
</node >
</node >
<node index ="1" text ="" resource-id ="" class ="android.widget.FrameLayout" package ="com.android.settings" content-desc ="" checkable ="false" checked ="false" clickable ="true" enabled ="true" focusable ="true" focused ="false" scrollable ="false" long-clickable ="false" password ="false" selected ="false" visible-to-user ="true" bounds ="[0,1227][1170,1414]" >
<node index ="0" text ="" resource-id ="" class ="android.widget.LinearLayout" package ="com.android.settings" 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 ="[0,1227][1170,1414]" >
<node index ="0" text ="" resource-id ="com.android.settings:id/icon" class ="android.widget.ImageView" package ="com.android.settings" 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 ="[42,1289][104,1351]" />
<node index ="1" text ="" resource-id ="" class ="android.widget.LinearLayout" package ="com.android.settings" 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 ="[187,1227][1170,1414]" >
<node index ="0" text ="" resource-id ="" class ="android.widget.LinearLayout" package ="com.android.settings" 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 ="[187,1227][1170,1411]" >
<node index ="0" text ="" resource-id ="" class ="android.widget.RelativeLayout" package ="com.android.settings" 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 ="[187,1290][439,1347]" >
<node index ="0" text ="提示音和通知" resource-id ="com.android.settings:id/title" class ="android.widget.TextView" package ="com.android.settings" 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 ="[187,1290][439,1347]" />
</node >
</node >
<node index ="1" text ="" resource-id ="com.android.settings:id/tile_divider" class ="android.view.View" package ="com.android.settings" 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 ="[187,1411][1170,1414]" />
</node >
</node >
</node >
<node index ="2" text ="" resource-id ="" class ="android.widget.FrameLayout" package ="com.android.settings" content-desc ="" checkable ="false" checked ="false" clickable ="true" enabled ="true" focusable ="true" focused ="false" scrollable ="false" long-clickable ="false" password ="false" selected ="false" visible-to-user ="true" bounds ="[0,1414][1170,1601]" >
<node index ="0" text ="" resource-id ="" class ="android.widget.LinearLayout" package ="com.android.settings" 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 ="[0,1414][1170,1601]" >
<node index ="0" text ="" resource-id ="com.android.settings:id/icon" class ="android.widget.ImageView" package ="com.android.settings" 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 ="[42,1476][104,1538]" />
<node index ="1" text ="" resource-id ="" class ="android.widget.LinearLayout" package ="com.android.settings" 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 ="[187,1414][1170,1601]" >
<node index ="0" text ="" resource-id ="" class ="android.widget.LinearLayout" package ="com.android.settings" 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 ="[187,1414][1170,1598]" >
<node index ="0" text ="" resource-id ="" class ="android.widget.RelativeLayout" package ="com.android.settings" 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 ="[187,1477][271,1534]" >
<node index ="0" text ="应用" resource-id ="com.android.settings:id/title" class ="android.widget.TextView" package ="com.android.settings" 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 ="[187,1477][271,1534]" />
</node >
</node >
<node index ="1" text ="" resource-id ="com.android.settings:id/tile_divider" class ="android.view.View" package ="com.android.settings" 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 ="[187,1598][1170,1601]" />
</node >
</node >
</node >
<node index ="3" text ="" resource-id ="" class ="android.widget.FrameLayout" package ="com.android.settings" content-desc ="" checkable ="false" checked ="false" clickable ="true" enabled ="true" focusable ="true" focused ="false" scrollable ="false" long-clickable ="false" password ="false" selected ="false" visible-to-user ="true" bounds ="[0,1601][1170,1788]" >
<node index ="0" text ="" resource-id ="" class ="android.widget.LinearLayout" package ="com.android.settings" 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 ="[0,1601][1170,1788]" >
<node index ="0" text ="" resource-id ="com.android.settings:id/icon" class ="android.widget.ImageView" package ="com.android.settings" 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 ="[42,1663][104,1725]" />
<node index ="1" text ="" resource-id ="" class ="android.widget.LinearLayout" package ="com.android.settings" 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 ="[187,1601][1170,1788]" >
<node index ="0" text ="" resource-id ="" class ="android.widget.LinearLayout" package ="com.android.settings" 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 ="[187,1601][1170,1785]" >
<node index ="0" text ="" resource-id ="" class ="android.widget.RelativeLayout" package ="com.android.settings" 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 ="[187,1664][397,1721]" >
<node index ="0" text ="应用兼容性" resource-id ="com.android.settings:id/title" class ="android.widget.TextView" package ="com.android.settings" 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 ="[187,1664][397,1721]" />
</node >
</node >
<node index ="1" text ="" resource-id ="com.android.settings:id/tile_divider" class ="android.view.View" package ="com.android.settings" 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 ="[187,1785][1170,1788]" />
</node >
</node >
</node >
<node index ="4" text ="" resource-id ="" class ="android.widget.FrameLayout" package ="com.android.settings" content-desc ="" checkable ="false" checked ="false" clickable ="true" enabled ="true" focusable ="true" focused ="false" scrollable ="false" long-clickable ="false" password ="false" selected ="false" visible-to-user ="true" bounds ="[0,1788][1170,1872]" >
<node index ="0" text ="" resource-id ="" class ="android.widget.LinearLayout" package ="com.android.settings" 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 ="[0,1788][1170,1872]" >
<node index ="0" text ="" resource-id ="com.android.settings:id/icon" class ="android.widget.ImageView" package ="com.android.settings" 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 ="[42,1850][104,1872]" />
<node index ="1" text ="" resource-id ="" class ="android.widget.LinearLayout" package ="com.android.settings" 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 ="[187,1788][1170,1872]" >
<node index ="0" text ="" resource-id ="" class ="android.widget.LinearLayout" package ="com.android.settings" 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 ="[187,1788][1170,1872]" >
<node index ="0" text ="" resource-id ="" class ="android.widget.RelativeLayout" package ="com.android.settings" 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 ="[187,1851][485,1872]" >
<node index ="0" text ="存储设备和 USB" resource-id ="com.android.settings:id/title" class ="android.widget.TextView" package ="com.android.settings" 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 ="[187,1851][485,1872]" />
</node >
</node >
</node >
</node >
</node >
</node >
</node >
</node >
</node >
</node >
</node >
</node >
</node >
</hierarchy >
选择器支持以下参数。详细信息请参考 UiSelector 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
父子节点和兄弟节点选择
父子节点
d(className="android.widget.RelativeLayout" ).child(text="蓝牙" )
d(className="android.widget.FrameLayout" , resourceId="android:id/content" ) \
.child_by_text("蓝牙" , className="android.widget.TextView" ).click()
d(className="android.widget.FrameLayout" , resourceId="android:id/content" ) \
.child_by_text(
"安全" ,
allow_scroll_search=True ,
className="android.widget.TextView"
).click()
兄弟节点
d (text="无线和网络" ) .sibling (className="android.view.ViewGroup" )
相对定位
我们还可以使用相对定位方法来获取元素 left
, right
, top
, bottom
.
d(A).left(B)
, 在A的左侧选择B
d(A).right(B)
, 在A的右侧选择B.
d(A).up(B)
, 在A的上部选择B
d(A).down(B)
, 在A是下部选择B
so:
d (text="更多" ) .up (text="蓝牙" ) .click ()
多元素(实例)选择
有时屏幕可能包含多个具有相同属性的元素,例如文本,使用选择器中的“instance”属性来选择一个符合条件的实例
d(resourceId="com.android.settings:id/title" ,instance=0 ).click()
此外,uiautomator2提供了一个类似列表的方法:
print (d(resourceId="com.android.settings:id/title" ).count)
d(resourceId="com.android.settings:id/title" )[0 ].click()
d(resourceId="com.android.settings:id/title" )[1 ].click()
for view in d(resourceId="com.android.settings:id/title" ):
print (view.info)
判断元素状态及信息
判断元素是否存在
print (d(text="蓝牙" ).exists)
print (d.exists(text="蓝牙" ))
print (d(text="蓝牙" ).exists(timeout=3 ))
获取特定UI对象的信息
获取/设置/清除可编辑字段的文本
print (d(text="Settings" ).get_text())
d(text="Settings" ).set_text("My text..." )
d(text="My text..." ).clear_text()
获取元素位置
x, y = d(text="蓝牙" ).center()
print (x, y)
x, y = d(text="蓝牙" ).center(offset=(0 , 0 ))
print (x, y)
im = d(text="蓝牙" ).screenshot()
im.save("蓝牙.jpg" )
扩展选择器 XPath
Java uiautoamtor中默认是不支持xpath的,所以这里属于扩展的一个功能。速度不是这么的快
常见用法
d.xpath("//android.widget.TextView" ).wait(10.0 )
d.xpath("//*[@content-desc='分享']" ).click()
if d.xpath("//android.widget.TextView[contains(@text, 'Se')]" ).exists:
print ("exists" )
for elem in d.xpath("//android.widget.TextView" ).all ():
print ("Text:" , elem.text)
print ("Attrib:" , elem.attrib)
print ("Position:" , elem.center())
元素操作
d(text="蓝牙" ).click()
d(text="蓝牙" ).click(timeout=10 )
d(text="蓝牙" ).click(offset=(0.5 , 0.5 ))
d(text="蓝牙" ).click(offset=(0 , 0 ))
d(text="蓝牙" ).click(offset=(1 , 1 ))
clicked = d(text='蓝牙' ).click_exists(timeout=10.0 )
print (clicked)
is_gone = d(text="蓝牙" ).click_gone(maxretry=10 , interval=1.0 )
d(text="蓝牙" ).long_click()
针对特定UI对象的手势操作
元素拖拽
元素移动
元素等待
在特定的ui对象上滑动页面
在特定的ui对象上滚动页面
上下文监控
with d.watch_context() as ctx:
ctx.when("^立即(下载|更新)" ).when("取消" ).click()
ctx.when("同意" ).click()
ctx.when("确定" ).click()
ctx.wait_stable()
ctx.when("仲夏之夜" ).call(lambda d: d.press("back" ))
ctx.when("确定" ).call(lambda el: el.click())
with d.watch_context(builtin=True ) as ctx:
ctx.when("@tb:id/jview_view" ).when('//*[@content-desc="图片"]' ).click()
全局设置
d.HTTP_TIMEOUT = 60
d.WAIT_FOR_DEVICE_TIMEOUT = 70
其他的配置,目前已大部分集中到 d.settings 中
d.settings['operation_delay' ] = (.5 , 1 )
d.settings['operation_delay_methods' ] = ['click' , 'swipe' , 'drag' , 'press' ]
d.settings['xpath_debug' ] = True
d.settings['wait_timeout' ] = 20.0
print (d.settings)
UiAutomator中的超时设置(隐藏方法)
print ( d.jsonrpc.getConfigurator() )
d.jsonrpc .setConfigurator ({"waitForIdleTimeout": 100 })
Input method (没测试过)
这种方法通常用于不知道控件的情况下的输入。第一步需要切换输入法,然后发送adb广播命令,具体使用方法如下
d.set_fastinput_ime(True )
d.send_keys("你好123abcEFG" )
d.clear_text()
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的通信。
消息提示框toast
显示toast
print (d.toast.show("Hello world" ))
d.toast.show("Hello world" , 5.0)
获取 toast
print (d.toast.get_message(5.0, 10.0, "default message" ))
一般用法
assert "Short message" in d.toast.get_message(5.0 , default ="" )
清除缓存的toast
d.toast .reset () # Now d.toast .get_message (0 ) is None
视频录制
这里没有使用手机中自带的screenrecord命令,是通过获取手机图片合成视频的方法,所以需要安装一些其他的依赖,如imageio, imageio-ffmpeg, numpy等 因为有些依赖比较大,推荐使用镜像安装。直接运行下面的命令即可。
pip3 install -U "uiautomator2[image]" -i https://pypi.doubanio.com/simple
使用方法:
d.screenrecord('output.mp4' )
time.sleep(10 )
d.screenrecord.stop()
图像匹配
安装依赖:
pip3 install -U "uiautomator2[image]" -i https://pypi.doubanio.com/simple
imdata = "home.jpg"
d.image.match(imdata)
d.image.click(imdata, timeout=20.0 )
Stop UiAutomator
停止UiAutomator守护服务
因为有atx-agent的存在,Uiautomator会被一直守护着,如果退出了就会被重新启动起来。但是Uiautomator又是霸道的,一旦它在运行,手机上的辅助功能、电脑上的uiautomatorviewer 就都不能用了,除非关掉该框架本身的uiautomator。下面就说下两种关闭方法
d.uiautomator.stop()
print (d.uiautomator.running())
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 地球OL攻略 —— 某应届生求职总结
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 提示词工程——AI应用必不可少的技术
· .NET周刊【3月第1期 2025-03-02】