Группа связи QQ: 815453846 Дискорд: https://discord.gg/PbJhnZJKDd
Я не поддерживал этот проект какое-то время (возможно, два года), но недавно мне нужно было заново изучить нативную автоматизацию Android. Конечно, я также исследовал Appium. После сравнения я обнаружил, что скорость работы проекта uiautomator2. действительно хорош. Он быстрый, от обнаружения элементов до нажатия, все занимает миллисекунды, и код легче понять. Я действительно не ожидал, что раньше написал такой волшебный проект. Как мог такой хороший проект позволить ему пылиться? Его нужно реорганизовать и почистить от ненужного кода. Итак, версия проекта была обновлена с 2.xx до 3.xx.
Пользователи, которые все еще используют версию 2.xx, могут сначала взглянуть на 2to3, прежде чем решить, стоит ли обновляться до 3.xx (лично я настоятельно рекомендую выполнить обновление)
В конце концов, версия со 2 по 3 является основным обновлением версии, и многие функции были удалены. Первое, что нужно удалить, это atx-agent, а во-вторых, есть куча функций, связанных с atx-агентом. Устаревшие функции, такие как init.
Номера версий различных зависимых библиотек
UiAutomator — это библиотека Java, предоставляемая Google для тестирования автоматизации Android на основе службы специальных возможностей. Он очень мощный и может тестировать сторонние приложения, получать любые атрибуты управления любого приложения на экране и выполнять с ним любую операцию, но у него есть два недостатка: 1. Тестовый сценарий может использовать только язык Java. 2. Тест. скрипт. Его необходимо упаковать в пакет jar или apk и загрузить на устройство для запуска.
Мы надеемся, что логика теста может быть написана на Python и сможет управлять мобильным телефоном во время работы на компьютере. Я хотел бы выразить огромную благодарность Xiaocong He (@xiaocong) за реализацию этой идеи (см. xiaocong/uiautomator). Принцип состоит в том, чтобы запустить службу http rpc на мобильном телефоне, открыть функции в uiautomator и затем использовать эти http. Интерфейс инкапсулирован в библиотеку Python. Потому что библиотека xiaocong/uiautomator
давно не обновлялась. Поэтому мы напрямую создали версию. Чтобы ее было легче отличить, мы добавили 2 openatx/uiautomator2 в конце. Я также создал копию соответствующего исходного кода пакета Android, openatx/android-uiautomator-server.
Помимо исправления ошибок в исходной библиотеке, также было добавлено множество новых функций. В основном он включает в себя следующие части:
Позвольте мне сначала объяснить, потому что многие люди часто спрашивают, что openatx/uiautomator2 не поддерживает тестирование iOS. Если вам нужно автоматическое тестирование iOS, вы можете перейти к этой библиотеке openatx/facebook-wda.
PS: Эта библиотека
https://github.com/NeteaseGame/ATXОн больше не находится на обслуживании, пожалуйста, замените его как можно скорее.
Вот краткое справочное руководство, подходящее для тех, кто уже начал. Комментарии приветствуются.
Сначала подготовьте один (а не два) телефон Android с включенными开发者选项
, подключите его к компьютеру и убедитесь, что вы видите подключенное устройство, запустив adb devices
.
Запустите pip3 install -U uiautomator2
чтобы установить uiautomator2.
Запустите python
из командной строки, чтобы открыть интерактивное окно Python. Затем введите следующую команду в окно.
import uiautomator2 as u2
d = u2 . connect () # connect to device
print ( d . info )
Когда вы увидите результат, аналогичный приведенному ниже, вы можете официально начать использовать нашу библиотеку. Поскольку в этой библиотеке слишком много функций и за ней стоит много контента, читать ее нужно медленно....
{'currentPackageName': 'net.oneplus.launcher', 'displayHeight': 1920, 'displayRotation': 0, 'displaySizeDpX': 411, 'displaySizeDpY': 731, 'displayWidth': 1080, 'productName': 'OnePlus5', '
screenOn': True, 'sdkInt': 27, 'naturalOrientation': True}
Кроме того, чтобы сохранить стабильность, вам необходимо включить разрешение плавающего окна小黄车
. Справочная статья py-uiautomator2 делает сервисы доступными в течение длительного времени через плавающие окна.
Обычно это удается, но могут быть и сюрпризы. Вы можете присоединиться к группе QQ, чтобы сообщать о проблемах (номер группы вверху). В группе много крутых ребят, которые могут помочь вам решить проблемы.
Спасибо всем нашим спонсорам!
Пустой
Рекомендация отличных статей (добро пожаловать ко мне в группу QQ для обратной связи)
成都-测试只会一点点
Установка
Подключиться к устройству
Командная строка
Глобальные настройки
Управление приложениями
автоматизация пользовательского интерфейса
Авторы
ЛИЦЕНЗИЯ
Установите uiautomator2
pip install -U uiautomator2
Проверьте, прошла ли установка успешно uiautomator2 --help
Инспектор пользовательского интерфейса
pip install uiautodev
# 启动
uiauto.dev
Откройте https://uiauto.dev в браузере, чтобы просмотреть структуру интерфейса текущего устройства.
uiauto.dev
uiauto.dev — это проект, независимый от uiautomator2, используемый для просмотра структуры слоев. Это реконструированная версия старого редактора проекта, за которую позже может взиматься плата (цена определенно стоит своих денег) для поддержки дальнейшего обслуживания текущего проекта. Если вам интересно, вы можете присоединиться к группе для обсуждения (включая запросы) Группа QQ 536481989
используйте серийный номер для подключения устройства, например 123456f
(видно на adb devices
)
import uiautomator2 as u2
d = u2 . connect ( '123456f' ) # alias for u2.connect_usb('123456f')
print ( d . info )
Серийный номер можно передать через env-var ANDROID_SERIAL
# export ANDROID_SERIAL=123456f
d = u2 . connect ()
$device_ip
представляет IP-адрес устройства.
Если вам нужно указать устройство, вам нужно передать --serial
, например python3 -m uiautomator2 --serial bff1234 <SubCommand>
, SubCommand — это подкоманда (скриншот, текущий и т. д.)
1.0.3 Добавлено:
python3 -m uiautomator2
равноuiautomator2
скриншот: скриншот
$ uiautomator2 screenshot screenshot.jpg
current: получить текущее имя и активность пакета.
$ uiautomator2 current
{
" package " : " com.android.browser " ,
" activity " : " com.uc.browser.InnerUCMobile " ,
" pid " : 28478
}
удалить: удалить приложение
$ uiautomator2 uninstall < package-name > # 卸载一个包
$ uiautomator2 uninstall < package-name- 1> < package-name- 2> # 卸载多个包
$ uiautomator2 uninstall --all # 全部卸载
стоп: остановить приложение
$ uiautomator2 stop com.example.app # 停止一个app
$ uiautomator2 stop --all # 停止所有的app
доктор:
$ uiautomator2 doctor
[I 2024-04-25 19:53:36,288 __main__:101 pid:15596] uiautomator2 is OK
Когда Python завершает работу, служба UiAutomation также завершает работу.
Распечатайте информацию HTTP-запроса, стоящую за кодом.
> >> d . debug = True
> >> d . info
12 : 32 : 47.182 $ curl - X POST - d '{"jsonrpc": "2.0", "id": "b80d3a488580be1f3e9cb3e926175310", "method": "deviceInfo", "params": {}}' 'http://127.0.0.1:54179/jsonrpc/0'
12 : 32 : 47.225 Response >> >
{ "jsonrpc" : "2.0" , "id" : "b80d3a488580be1f3e9cb3e926175310" , "result" :{ "currentPackageName" : "com.android.mms" , "displayHeight" : 1920 , "displayRotation" : 0 , "displaySizeDpX" : 360 , "displaySizeDpY" : 640 , "displayWidth" : 1080 , "productName"
: "odin" , "screenOn" : true , "sdkInt" : 25 , "naturalOrientation" : true }}
< << END
Установить время ожидания поиска элемента (по умолчанию 20 с)
d . implicitly_wait ( 10.0 ) # 也可以通过d.settings['wait_timeout'] = 10.0 修改
d ( text = "Settings" ). click () # if Settings button not show in 10s, UiObjectNotFoundError will raised
print ( "wait timeout" , d . implicitly_wait ()) # get default implicit wait
Эта функция будет влиять на click
, long_click
, drag_to
, get_text
, set_text
, clear_text
и т. д.
В этой части показано, как управлять приложениями.
Мы поддерживаем установку APK только по URL-адресу.
d . app_install ( 'http://some-domain.com/some.apk' )
# 默认的这种方法是先通过atx-agent解析apk包的mainActivity,然后调用am start -n $package/$activity启动
d . app_start ( "com.example.hello_world" )
# 使用 monkey -p com.example.hello_world -c android.intent.category.LAUNCHER 1 启动
# 这种方法有个副作用,它自动会将手机的旋转锁定给关掉
d . app_start ( "com.example.hello_world" , use_monkey = True ) # start with package name
# 通过指定main activity的方式启动应用,等价于调用am start -n com.example.hello_world/.MainActivity
d . app_start ( "com.example.hello_world" , ".MainActivity" )
# equivalent to `am force-stop`, thus you could lose data
d . app_stop ( "com.example.hello_world" )
# equivalent to `pm clear`
d . app_clear ( 'com.example.hello_world' )
# stop all
d . app_stop_all ()
# stop all app except for com.examples.demo
d . app_stop_all ( excludes = [ 'com.examples.demo' ])
d . app_info ( "com.examples.demo" )
# expect output
#{
# "mainActivity": "com.github.uiautomator.MainActivity",
# "label": "ATX",
# "versionName": "1.1.7",
# "versionCode": 1001007,
# "size":1760809
#}
# save app icon
img = d . app_icon ( "com.examples.demo" )
img . save ( "icon.png" )
d . app_list_running ()
# expect output
# ["com.xxxx.xxxx", "com.github.uiautomator", "xxxx"]
pid = d . app_wait ( "com.example.android" ) # 等待应用运行, return pid(int)
if not pid :
print ( "com.example.android is not running" )
else :
print ( "com.example.android pid is %d" % pid )
d . app_wait ( "com.example.android" , front = True ) # 等待应用前台运行
d . app_wait ( "com.example.android" , timeout = 20.0 ) # 最长等待时间20s(默认)
Добавлено в версии 1.2.0
отправить файл на устройство
# push to a folder
d . push ( "foo.txt" , "/sdcard/" )
# push and rename
d . push ( "foo.txt" , "/sdcard/bar.txt" )
# push fileobj
with open ( "foo.txt" , 'rb' ) as f :
d . push ( f , "/sdcard/" )
# push and change file access mode
d . push ( "foo.sh" , "/data/local/tmp/" , mode = 0o755 )
вытащить файл с устройства
d . pull ( "/sdcard/tmp.txt" , "tmp.txt" )
# FileNotFoundError will raise if the file is not found on the device
d . pull ( "/sdcard/some-file-not-exists.txt" , "tmp.txt" )
# grant all the permissions
d . app_auto_grant_permissions ( "io.appium.android.apis" )
# open scheme
d . open_url ( "appname://appnamehost" )
# same as
# adb shell am start -a android.intent.action.VIEW -d "appname://appnamehost"
В этой части показано, как выполнять распространенные операции с устройством:
Запустите кратковременную команду оболочки с защитой по тайм-ауту (тайм-аут по умолчанию 60 с).
Примечание. Для поддержки тайм-аута требуется atx-agent >=0.3.3
Функция adb_shell
устарела. Вместо этого используйте shell
.
Простое использование
output , exit_code = d . shell ( "pwd" , timeout = 60 ) # timeout 60s (Default)
# output: "/n", exit_code: 0
# Similar to command: adb shell pwd
# Since `shell` function return type is `namedtuple("ShellResponse", ("output", "exit_code"))`
# so we can do some tricks
output = d . shell ( "pwd" ). output
exit_code = d . shell ( "pwd" ). exit_code
Первым аргументом может быть, например, список.
output , exit_code = d . shell ([ "ls" , "-l" ])
# output: "/....", exit_code: 0
Это возвращает строку для стандартного вывода, объединенную со стандартным выводом stderr. Если команда является блокирующей командой, shell
также будет блокироваться до тех пор, пока команда не будет завершена или не истечет время ожидания. Во время выполнения команды не будет получен частичный вывод. подходит для длительных команд. Данная команда оболочки выполняется в среде, аналогичной adb shell
, которая имеет уровень разрешений Linux adb
или shell
(выше, чем разрешение приложения).
Запуск длительной команды оболочки (удалено)
Сеанс представляет жизненный цикл приложения. Может использоваться для запуска приложения и обнаружения сбоя приложения.
Запустить и закрыть приложение
sess = d . session ( "com.netease.cloudmusic" ) # start 网易云音乐
sess . close () # 停止网易云音乐
sess . restart () # 冷启动网易云音乐
Используйте Python with
запуска и закрытия приложения
with d . session ( "com.netease.cloudmusic" ) as sess :
sess ( text = "Play" ). click ()
Прикрепите к работающему приложению
# launch app if not running, skip launch if already running
sess = d . session ( "com.netease.cloudmusic" , attach = True )
Обнаружение сбоя приложения
# When app is still running
sess ( text = "Music" ). click () # operation goes normal
# If app crash or quit
sess ( text = "Music" ). click () # raise SessionBrokenError
# other function calls under session will raise SessionBrokenError too
# check if session is ok.
# Warning: function name may change in the future
sess . running () # True or False
Получите основную информацию
d . info
Ниже приведен возможный результат:
{'currentPackageName': 'com.android.systemui',
'displayHeight': 1560,
'displayRotation': 0,
'displaySizeDpX': 360,
'displaySizeDpY': 780,
'displayWidth': 720,
'naturalOrientation': True,
'productName': 'ELE-AL00',
'screenOn': True,
'sdkInt': 29}
Получить размер окна
print ( d . window_size ())
# device upright output example: (1080, 1920)
# device horizontal output example: (1920, 1080)
Получить текущую информацию о приложении. Для некоторых устройств Android выходные данные могут быть пустыми (см. Пример вывода 3 ).
print ( d . app_current ())
# Output example 1: {'activity': '.Client', 'package': 'com.netease.example', 'pid': 23710}
# Output example 2: {'activity': '.Client', 'package': 'com.netease.example'}
# Output example 3: {'activity': None, 'package': None}
Ожидание активности
d . wait_activity ( ".ApiDemos" , timeout = 10 ) # default timeout 10.0 seconds
# Output: true of false
Получить серийный номер устройства
print ( d . serial )
# output example: 74aAEDR428Z9
Получить IP-адрес WLAN
print ( d . wlan_ip )
# output example: 10.0.0.1 or None
Получить подробную информацию об устройстве d.device_info
информация_устройства
print ( d . device_info )
Ниже приведен возможный результат:
{'arch': 'arm64-v8a',
'brand': 'google',
'model': 'sdk_gphone64_arm64',
'sdk': 34,
'serial': 'EMULATOR34X1X19X0',
'version': 14}
Получение заданного содержимого буфера обмена
Установите содержимое монтажного стола или получите содержимое
буфер обмена/set_clipboard
d . clipboard = 'hello-world'
# or
d . set_clipboard ( 'hello-world' , 'label' )
Получить содержимое буфера обмена
Для получения буфера обмена требуется вызов IME(com.github.uiautomator/.AdbKeyboard)
d.set_input_ime()
перед его использованием.
```python
# get clipboard content
print(d.clipboard)
```
Включить/выключить экран
d . screen_on () # turn on the screen
d . screen_off () # turn off the screen
Получить текущий статус экрана
d . info . get ( 'screenOn' ) # require Android >= 4.4
Нажмите аппаратную/мягкую клавишу
d . press ( "home" ) # press the home key, with key name
d . press ( "back" ) # press the back key, with key name
d . press ( 0x07 , 0x02 ) # press keycode 0x07('0') with META ALT(0x02)
В настоящее время поддерживаются следующие имена ключей:
Вы можете найти все определения кодов клавиш на Android KeyEvnet.
Разблокировать экран
d . unlock ()
# This is equivalent to
# 1. press("power")
# 2. swipe from left-bottom to right-top
Нажмите на экран
d . click ( x , y )
Двойной щелчок
d . double_click ( x , y )
d . double_click ( x , y , 0.1 ) # default duration between two click is 0.1s
Долгий клик по экрану
d . long_click ( x , y )
d . long_click ( x , y , 0.5 ) # long click 0.5s (default)
Проведите пальцем по экрану
d . swipe ( sx , sy , ex , ey )
d . swipe ( sx , sy , ex , ey , 0.5 ) # swipe for 0.5s(default)
Функция расширения 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) 这个区域做滑动
# 实践发现上滑或下滑的时候,从中点开始滑动成功率会高一些
d . swipe_ext ( "up" , scale = 0.8 ) # 代码会vkk
# 还可以使用Direction作为参数
from uiautomator2 import Direction
d . swipe_ext ( Direction . FORWARD ) # 页面下翻, 等价于 d.swipe_ext("up"), 只是更好理解
d . swipe_ext ( Direction . BACKWARD ) # 页面上翻
d . swipe_ext ( Direction . HORIZ_FORWARD ) # 页面水平右翻
d . swipe_ext ( Direction . HORIZ_BACKWARD ) # 页面水平左翻
Тащить
d . drag ( sx , sy , ex , ey )
d . drag ( sx , sy , ex , ey , 0.5 ) # swipe for 0.5s(default)
Проведите по точкам
# swipe from point(x0, y0) to point(x1, y1) then to point(x2, y2)
# time will speed 0.2s bwtween two points
d . swipe_points ([( x0 , y0 ), ( x1 , y1 ), ( x2 , y2 )], 0.2 ))
В основном он используется для разблокировки шаблона Цзюгун. Относительные координаты каждой точки можно получить заранее (здесь поддерживается процентное соотношение).
Коснитесь и перетащите (бета)
Этот интерфейс представляет собой относительно низкоуровневый примитивный интерфейс. Он не кажется идеальным, но его можно использовать. Примечание. Это место не поддерживает проценты.
d . touch . down ( 10 , 10 ) # 模拟按下
time . sleep ( .01 ) # down 和 move 之间的延迟,自己控制
d . touch . move ( 15 , 15 ) # 模拟移动
d . touch . up ( 10 , 10 ) # 模拟抬起
Примечание. Операции щелчка, пролистывания и перетаскивания поддерживают процентные значения позиции. Пример:
d.long_click(0.5, 0.5)
означает долгое нажатие в центре экрана.
Получить/установить ориентацию устройства
Возможные направления:
natural
или n
left
или l
right
или r
upsidedown
или u
(невозможно установить) # retrieve orientation. the output could be "natural" or "left" or "right" or "upsidedown"
orientation = d . orientation
# WARNING: not pass testing in my TT-M1
# set orientation and freeze rotation.
# notes: setting "upsidedown" requires Android>=4.3.
d . set_orientation ( 'l' ) # or "left"
d . set_orientation ( "l" ) # or "left"
d . set_orientation ( "r" ) # or "right"
d . set_orientation ( "n" ) # or "natural"
Вращение «заморозить/разморозить»
# freeze rotation
d . freeze_rotation ()
# un-freeze rotation
d . freeze_rotation ( False )
Сделать скриншот
# take screenshot and save to a file on the computer, require Android>=4.2.
d . screenshot ( "home.jpg" )
# get PIL.Image formatted images. Naturally, you need pillow installed first
image = d . screenshot () # default format="pillow"
image . save ( "home.jpg" ) # or home.png. Currently, only png and jpg are supported
# get opencv formatted images. Naturally, you need numpy and cv2 installed first
import cv2
image = d . screenshot ( format = 'opencv' )
cv2 . imwrite ( 'home.jpg' , image )
# get raw jpeg data
imagebin = d . screenshot ( format = 'raw' )
open ( "some.jpg" , "wb" ). write ( imagebin )
Дамп иерархии пользовательского интерфейса
# get the UI hierarchy dump content
xml = d . dump_hierarchy ()
# compressed=True: include not import nodes
# pretty: format xml
# max_depth: limit xml depth, default 50
xml = d . dump_hierarchy ( compressed = False , pretty = False , max_depth = 50 )
Открыть уведомление или быстрые настройки
d . open_notification ()
d . open_quick_settings ()
Селектор — удобный механизм для идентификации конкретного объекта пользовательского интерфейса в текущем окне.
# Select the object with text 'Clock' and its className is 'android.widget.TextView'
d ( text = 'Clock' , className = 'android.widget.TextView' )
Selector поддерживает следующие параметры. Подробную информацию можно найти в документе 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
дети
# get the children or grandchildren
d ( className = "android.widget.ListView" ). child ( text = "Bluetooth" )
братья и сестры