文章目录
1. UIAutomator2的GitHub链接
https://github.com/openatx/uiautomator2
2. UIAutomator2的安装和运行
- 先下载adb并安装
http://adbdownload.com/ - 运行pip3 install -U uiautomator2 安装uiautomator2
- 准备一台(不要两台)开启了开发者选项的安卓手机或者雷电模拟器,连接上电脑,确保执行adb devices可以看到连接上的设备(我截图显示的是模拟器,IP:Port的形式显示)。
- 运行python3 -m uiautomator2 init安装包含httprpc服务的apk到手机+atx-agent, minicap, minitouch (在过去的版本中,这一步是必须执行的,但是从1.3.0之后的版本,当运行python代码u2.connect()时就会自动推送这些文件了)
3. UIAutomator2的使用前提
- Android版本 4.4+
- Python 3.6+ (社区反馈3.8.0不支持, 但是3.8.2支持)
4. UIAutomator2的元素定位方法
4.1 问题:
在使用UIAutomator2定位手机上的元素时,发现眼睛能看到的元素,用鼠标点击时,并不能定位到对应的元素。
4.2 解决方法:
-
将模拟器的分辨率改成和电脑的分辨率一致。
-
在WEditor工具的hierarchy分层树上查找对应的元素。
-
如果分层树上找不到对应的元素,需要将鼠标放在页面空白处(没有能点击的元素),然后按下图中的“点击”按钮,再点击“Hierarchy”按钮,就 可以在hierarchy分层树上查找对应的元素。
-
如果手机是竖屏显示的,但是WEditor中的手机镜像是横屏显示的,需要点击connect断开连接,然后把开关改成“静态”,然后再点击connect重新连接手机,然后把开关改成“实时”,这时WEditor中的手机镜像就会和手机同步改成竖屏显示了。
5. UIAutomator2的使用实例
请将username和password修改为自己的qq id和密码后直接执行LeiDianVM.py
5.1 执行效果,需要在720P下观看
python+UIAutomator2完成app自动化操作20210401
5.2 LeiDianVM.py
from time import sleep
import os
import subprocess
from LeiDianApp import LeiDianApp
class LeiDianVM(object):
def __init__(self):
# 启动模拟器
self.start_vm = 'ldconsole.exe launch --name %d'
# 删除模拟器
self.delete_vm = 'ldconsole.exe remove --name %s'
# 新增模拟器
self.add_vm = 'ldconsole.exe add --name %d'
# 关闭所有模拟器
self.close_vm = 'ldconsole.exe quitall'
# 启动模拟器
self.launch_vm = 'ldconsole.exe launch --name %d'
# 关闭adb server
self.close_adb_server = 'adb kill-server'
# 开启adb server
self.start_adb_server = 'adb start-server'
# 修改分辨率,分辨率必须和笔记本电脑的分辨率一样,否则会出现看到按钮的位置,但是鼠标点上去发现定位不到按钮的位置(resourceId/text/xpath等)
self.alter_ratio = 'ldconsole.exe modify --name %d --resolution 1920,1080,280 --cpu 4 --memory 2048 ' \
'--manufacturer HUAWEI --model HMA-AL00 --pnumber 13248888888 --imei 865166020734532 '
# 安装app
self.install_app = 'ldconsole.exe installapp --index %d --filename %s'
# app目录路径
self.app_dir_path = 'E:\\adbd.apk'
# ldconsole.exe路径
self.ld_path = 'E:\\ChangZhi\\dnplayer2'
# 查看模拟器所有信息
self.all_vm_info = 'ldconsole.exe list2'
# 切换目录
def change_dir(self, vm_name):
try:
current_dir = os.getcwd()
print('当前目录为 %s' % current_dir)
# 切换到雷电路径
os.chdir(self.ld_path)
# 查看修改后的工作目录
after_dir = os.getcwd()
print('当前工作目录已修改为 %s' % after_dir)
except Exception as e:
print(e)
else:
self.view_vm(vm_name)
# 查看模拟器
def view_vm(self, vm_name):
data = subprocess.Popen('ldconsole.exe list2', stdout=subprocess.PIPE, universal_newlines=True)
number_list = []
# 通过循环获取已经存在的vm_name
for line in data.stdout:
info = line.strip().split(',')
vm_number = info[1]
number_list.append(vm_number)
print(number_list)
# 判断准备创建的vm_name是否在查询的列表里,如果存在就执行删除vm的的函数,如果不存在就启动新建vm的函数
if str(vm_name) in number_list:
# 查询vm_name出现了多少次
count_vm_name = number_list.count(str(vm_name))
print('该模拟器已经存在 %d 次' % count_vm_name)
print('正在关闭模拟器')
subprocess.call(self.close_vm)
print('关闭模拟器完成')
# for i in range(1, count_vm_name + 1):
# print('开始删除%s' % vm_name)
# self.del_vm(vm_name)
# print('删除 %s 完成' % vm_name)
for j in number_list:
print('j= %s' % j)
if j == '雷电模拟器':
pass
else:
self.del_vm(j)
print('删除%s完成' % j)
print('restarting adb_server')
subprocess.call(self.close_adb_server)
subprocess.call(self.start_adb_server)
sleep(2)
print('finish restart adb_server')
result = 'deleted_vm'
print('view_vm= %s !!!' % result)
LeiDianVM().view_vm(1)
else:
print('start creating %s' % vm_name)
self.make_vm(vm_name=vm_name)
result = 'made_vm'
print('view_vm= %s !!!' % result)
print('finish LeiDianVM,start LeiDianApp')
LeiDianApp().vm_status('QQ')
# 删除模拟器
def del_vm(self, vm_name):
subprocess.call(self.delete_vm % vm_name)
# 创建模拟器
def make_vm(self, vm_name):
subprocess.call(self.add_vm % vm_name)
print('创建模拟器{}成功!'.format(vm_name))
subprocess.call(self.alter_ratio % vm_name)
print('修改模拟器{}分辨率成功!'.format(vm_name))
subprocess.call(self.start_vm % vm_name)
print('启动模拟器%s' % vm_name)
print('开始安装%s!' % self.app_dir_path)
code = subprocess.call(self.install_app % (vm_name, self.app_dir_path))
print(code)
if code == 0:
sleep(60)
print('install %s successfully!' % self.app_dir_path)
print('start install uiautomator2!')
data2 = subprocess.Popen('python -m uiautomator2 init', stdout=subprocess.PIPE, universal_newlines=True)
number_list2 = []
for line in data2.stdout:
info2 = line.strip().split(' ')
status = info2[0]
number_list2.append(status)
print(number_list2)
if 'Successfully' in number_list2:
print('install uiautomator2 successfully')
else:
print('install uiautomator2 unsuccessfully')
# 启动模拟器
def launch_vm(self, vm_name):
subprocess.call(self.launch_vm % vm_name)
print('finish launch_vm')
if __name__ == '__main__':
LeiDianVM().change_dir(1)
5.3 LeiDianApp.py
import uiautomator2 as u2
import uiautomator2.ext.htmlreport as htmlreport
from LeiDianAppOpt import LeiDianAppOpt
class LeiDianApp(object):
def __init__(self):
try:
self.d = u2.connect_usb()
except Exception as e:
print(e)
# 每个ui点击后休眠1s
self.d.click_post_delay = 1
# 连接测试
def vm_status(self, app_name):
if self.d.info['naturalOrientation']:
print('设备连接正常!')
print('finish vm_status')
self.search_app(app_name)
else:
print('设备连接出现问题!请检查后重试!')
# 搜索app
def search_app(self, app_name):
self.d.press('home')
self.d(resourceId="com.android.launcher3:id/search_text_view").click()
self.d(focused=True).set_text(app_name)
self.d(resourceId="com.android.launcher3:id/searchImageView").click()
try:
self.d(resourceId="com.android.flysilkworm:id/gameName", text=app_name).click()
print('finish search_app')
self.install_app()
except Exception as e:
print(e)
# 安装app
def install_app(self):
try:
self.d(resourceId="com.android.flysilkworm:id/download_btn_layout").wait(exists=True, timeout=20)
self.d(resourceId="com.android.flysilkworm:id/download_btn_layout").click()
self.d(resourceId="com.android.flysilkworm:id/open_btn").wait(exists=True, timeout=30)
self.d(resourceId="com.android.flysilkworm:id/open_btn").click()
self.d(resourceId="com.tencent.mobileqq:id/dialogRightBtn").wait(exists=True, timeout=20)
self.d(resourceId="com.tencent.mobileqq:id/dialogRightBtn").click()
print('finish install_app')
self.login_app()
except Exception as e:
print(e)
# 登录app
def login_app(self):
self.d(resourceId="com.tencent.mobileqq:id/btn_login").click()
self.d(resourceId="com.tencent.mobileqq:id/input").click()
self.d.send_keys("username", clear=True)
self.d(resourceId='com.tencent.mobileqq:id/password').set_text('password')
try:
self.d(resourceId="com.tencent.mobileqq:id/login").click()
self.d(resourceId="com.tencent.mobileqq:id/dialogLeftBtn").wait(exists=True, timeout=120)
self.d(resourceId="com.tencent.mobileqq:id/dialogLeftBtn").click()
print('正在登录,请稍后')
print('finish LeiDianApp,start LeiDianAppOpt')
LeiDianAppOpt().app_opt()
except Exception as e:
print(e)
if __name__ == '__main__':
print('this is inside LeiDianApp __name__')
5.4 LeiDianAppOpt.py
import uiautomator2 as u2
class LeiDianAppOpt(object):
def __init__(self):
try:
self.d = u2.connect_usb()
except Exception as e:
print(e)
def app_opt(self):
self.d.xpath('//*[@resource-id="com.tencent.mobileqq:id/recent_chat_list"]/android.widget.LinearLayout['
'6]/android.widget.RelativeLayout[1]/android.widget.RelativeLayout[1]/android.view.View['
'2]').click()
self.d(resourceId="com.tencent.mobileqq:id/fun_btn")
self.d(resourceId="com.tencent.mobileqq:id/input").wait(exists=True, timeout=60)
self.d(resourceId="com.tencent.mobileqq:id/input").set_text('test')
self.d(resourceId="com.tencent.mobileqq:id/fun_btn").click_exists(15)
self.d(resourceId="com.tencent.mobileqq:id/chat_item_content_layout", text="test").long_click(1)
if self.d(resourceId="com.tencent.mobileqq:id/name", text="撤回").exists:
self.d(resourceId="com.tencent.mobileqq:id/name", text="撤回").click()
self.d(resourceId="com.tencent.mobileqq:id/dialogRightBtn").click()
else:
self.d(resourceId="com.tencent.mobileqq:id/name", text="删除").click()
self.d(resourceId="com.tencent.mobileqq:id/action_sheet_button").click()
print('finish app_opt')
if __name__ == '__main__':
print('this is inside LeiDianAppOpt __name__')