要求:
Android版本 4.4+
Python版本3.6+
连接手机:
- 开启开发者选项,
- 开启usb调试,
- 连接电脑,adb devices可以看到设备号
- 安装uiautomator2:python -m pip install -U uiautomator2
- 验证是否连接成功:
import uiautomator2 as u2 driver = u2.connect() # connect to device print(driver.info)
- 执行以上代码有正确输出
安装:
1、Install uiautomator2
python -m pip install --upgrade --pre uiautomator2
测试是否安装成功 uiautomator2 --help
2、Install weditor (UI Inspector)
python -m pip install -U weditor
在命令行运行weditor --help 确认是否安装成功
3、Install daemons to a device (Optional)
电脑连接上一个手机或多个手机, 确保adb已经添加到环境变量中,执行下面的命令会自动安装本库所需要的设备端程序
python -m uiautomator2 init
安装提示success即可
4、【可选】AppetizerIO 所见即所得脚本编辑器
AppetizerIO 提供了对uiautomator2的深度集成,可以图形化管理ATX设备,还有所见即所得脚本编辑器
到网站下载直接打开,首次使用需要注册账号
设备管理 界面里可以检查设备是否正常init,起停atx-agent,抓取atx-agent.log文件
测试脚本调出脚本助手,实时界面同步,点击界面直接插入各种代码,同时支持uiautomator和Appium
连接设备:
-
通过wifi,需确保同一网络
import uiautomator2 as u2 driver = u2.connect('10.0.0.1') # alias for u2.connect_wifi('10.0.0.1') print(driver.info)
-
通过usb线
import uiautomator2 as u2 d = u2.connect('M9N7N15930001618') # 括号内为adb devices 获取的设备号 print(d.info)
打开weditor UI查看器
cmd命令:python -m weditor
- 浏览器打开
- 输入设备号点击连接
- 成功连接设备后刷新获取最新页面
- 鼠标点击要找的元素,进行定位
uiautomator2 命令操作
-
截图
uiautomator2 screenshot screenshot.jpg 截图保存至当前文件夹,命名为screenshot.jpg
uiautomator2 screenshot E:\liang\tools\screenshot.jpg 截图保存至指定文件夹,命名为screenshot.jpg -
获取当前包名和activity
uiautomator2 current
{
“package”: “com.android.browser”,
“activity”: “com.uc.browser.InnerUCMobile”,
“pid”: 28478
} -
卸载
uiautomator2 uninstall # 卸载一个包
uiautomator2 uninstall # 卸载多个包
uiautomator2 uninstall --all # 全部卸载 -
stop: 停止应用
uiautomator2 stop com.example.app # 停止一个app
uiautomator2 stop --all # 停止所有的app -
连接手机 运行/关闭app
import uiautomator2 as u2
d = u2.connect(‘M9N7N15930001618’) #括号内为adb devices获取的设备号
d.app_start(“com.tencent.wework”) #括号内为要启动的APP包名
d.app_stop(‘包名’) #停止
d.app_clear(‘包名’) #清除
扩展:不知道怎么获取包名的可以打开一个app,doc下输入uiautomator2 current获取
{
“package”: “com.android.browser”,
“activity”: “com.uc.browser.InnerUCMobile”,
“pid”: 28478
}
常用操作
-
手势交互
点击屏幕(坐标,支持相对坐标) d.click(x, y) d.click(0.5, 0.5) #其中(0.235, 0.456) 代表 X(50%) Y(50%) 双击 d.double_click(x, y) #默认两次点击相隔0.1秒 d.double_click(x, y, 1) #两次点击相隔1秒 长按 d.long_click(x, y) #默认按0.5秒 d.long_click(x, y, 5) # l长按5秒 滑动(滑动屏幕) d.swipe(sx, sy, ex, ey) 从坐标(sx,sy)滑动至坐标(ex,ey),支持相对坐标 d.swipe(sx, sy, ex, ey, 0.5) # 默认滑动时间0.5秒,可修改 SwipeExt 扩展功能 d.swipe_ext("right") # 屏幕右滑,4选1 "left", "right", "up", "down" d.swipe_ext("right", scale=0.9) # 默认0.9, 滑动距离为屏幕宽度的90% d.swipe_ext("right", box=(0, 0, 100, 100)) # 在 (0,0) -> (100, 100) 这个区域做滑动 拖动(例:拖动某个APP) d.drag(sx, sy, ex, ey) #支持相对坐标 d.drag(sx, sy, ex, ey, 0.5) # 默认滑动时间0.5秒,可修改 多个点连续滑动(典型案例:9宫格解锁) 从point(x0, y0) 滑动到point(x1, y1)然后滑动到point(x2, y2)...等等 d.swipe_points([(x0, y0), (x1, y1), (x2, y2)], 0.2)) #0.2为每次两点之间滑动的时间,坐标可以是绝对坐标,也可以是相对坐标 d.swipe_points([(0.235, 0.456), (0.503, 0.449), (0.509, 0.601), (0.777, 0.603), (0.771, 0.763), (0.222, 0.75)], 0.2) #其中(0.235, 0.456) 代表 X(23.5%) Y(45.6%) 模拟按下-等待-移动-松开 d.touch.down(10, 10) # 模拟按下 time.sleep(0.01) # down 和 move 之间的延迟,自己控制 d.touch.move(15, 15) # 模拟移动 d.touch.up() # 模拟抬起 截图 d.screenshot("E:\liang\tools\screenshot.jpg")传入路径和名称
-
元素选择器
#定位text为'Clock'并且className为'android.widget.TextView'的元素 d(text='Clock', className='android.widget.TextView') 后代元素 #定位className为"android.widget.ListView"的元素下面text为"Bluetooth"的元素 d(className="android.widget.ListView").child(text="Bluetooth") 兄弟姐妹,同级元素 #定位text为"Google"的元素的同级元素中className="android.widget.ImageView"的元素 d(text="Google").sibling(className="android.widget.ImageView") 通过多个层级多个属性定位 #className="android.widget.ListView"且resourceId="android:id/list"的元素下面text为"Bluetooth"且className="android.widget.LinearLayout"的元素 d(className="android.widget.ListView", resourceId="android:id/list").child_by_text("Bluetooth", className="android.widget.LinearLayout") 可以由多个层级往下定位,例: d(className="###", resourceId="###").child_by_text("Wi-Fi", className="¥¥¥¥").child(className="****").click() 相对定位 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="Add new", instance=0) #instance=0表示选择第一个,依次类推 其他操作: d(text="Add new").count #返回当前屏幕某个属性元素的个数 len(d(text="Add new")) #返回当前屏幕某个属性元素列表的长度 d(text="Add new")[0] #用下标选择指定元素,同d(text="Add new", instance=0) 迭代: for view in d(text="Add new"): view.info 获取所选ui对象的状态及其信息 1、检查元素是否存在 d(text="Settings").exists # 返回布尔值,True if exists, else False d(text="Settings").exists(timeout=3) #增加等待时间为3秒 2、输出指定元素的信息 d(text="Settings").info 3、获取、输入、清除输入框文本 d(text="Settings").get_text() # 获取文本内容 d(text="Settings").set_text("My text...") #输入文本内容 d(text="Settings").clear_text() # 清除文本内容 4、获取指定元素中心点坐标 x, y = d(text="Settings").center() x, y = d(text="Settings").center(offset=(0, 0)) # offset为指定元素的相对坐标,(0,0)表示元素左上角,(0.5,0.5)表示元素中心,(1,1)表示元素右下角
-
指定元素点击操作
d(text="Settings").click() #点击指定元素中心位置 d(text="Settings").click(timeout=10) #等待元素出现(最多等待10秒)后点击 d(text="Settings").click(offset=(0.5, 0.5)) # offset为指定元素的相对坐标,(0,0)表示元素左上角,(0.5,0.5)表示元素中心,(1,1)表示元素右下角 clicked = d(text='Skip').click_exists(timeout=10.0) #如果10秒内元素存在,则点击,默认等待10秒 is_gone = d(text="Skip").click_gone(maxretry=10, interval=1.0) #等待元素消失后点击,返回布尔值,默认轮询次数10次,每次间隔时间1秒 d(text="Settings").long_click() #长按指定元素
-
特定元素的手势操作
1、将元素拖向另一个点或另一个元素 备注:安卓4.3以下不能用 d(text="Settings").drag_to(x, y, duration=0.5) #将指定元素在0.5秒的时间内拖动至指定坐标 d(text="Settings").drag_to(text="Clock", duration=0.25) #将指定元素在0.25秒的时间内拖动至指定元素的中心位置 2、等待元素出现或消失 d(text="Settings").wait(timeout=3.0) #等待元素出现,等待时间最长3秒,返回布尔值,默认等待时间20秒 d(text="Settings").wait_gone(timeout=1.0) #等待元素消失,等待时间最长1秒,返回布尔值,默认等待时间20秒 3、滚动屏幕 scroll()里面的参数steps默认是滑动一个屏幕的距离 a.向上滚动:d(scrollable=True).scroll(steps=10) b.向下滑动:d(scrollable=True).scroll.vert.backward() c.水平向右滚动:d(scrollable=True).scroll.horiz.forward(steps=50) d.水平向左滚动:d(scrollable=True).scroll.horiz.backward(steps=50) e.水平滑动到最左边:d(scrollable=True).scroll.horiz.toBeginning(steps=100, max_swipes=1000) f.水平滑动到最右边:d(scrollable=True).scroll.horiz.toEnd(steps=100, max_swipes=1000) g.竖直滑动到结尾:d(scrollable=True).scroll.toEnd() h.竖直滑动到开头:d(scrollable=True).scroll.toBeginning(steps=50) i.滑动到指定位置:d(scrollable=True).scroll.to(text="Security")
-
全局设置
# 设置每次UI点击后1.5秒的延迟 d.click_post_delay = 1.5 # default no delay # 设置默认元素等待超时(秒) d.wait_timeout = 30.0 # default 20.0 设置元素查找等待时间(默认20s) d.implicitly_wait(10.0)
-
输入内容
d.send_keys('str')
-
关于Toast
1、显示toast d.toast.show("Hello world", 1.0) # show for 1.0s, default 1.0s 2、获取toast # 5.0: 最大等待时间 # 10.0: toast出现后的缓存时间. 默认 10.0 # "default message": 返回的toast值. Default None 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()
-
关于xpath
description -> content-desc(xpath) resourceId -> resource-id(xpath) # 等待元素存在(等待10秒) d.xpath("//android.widget.TextView").wait(10.0) # return bool # 找到元素并点击 d.xpath("//*[@content-desc='分享']").click() # 检查是否存在 if d.xpath("//android.widget.TextView[contains(@text, 'Se')]").exists: print("exists") # 获取所有输入框的text,属性和中心点坐标 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())
感谢原作者:“小啊小狼” 原笔记来源:https://www.jianshu.com/p/28c298abfd15