QQ 커뮤니케이션 그룹: 815453846 디스코드: https://discord.gg/PbJhnZJKDd
한동안(어쩌면 2년 정도) 이 프로젝트를 유지하지 못했는데, 최근에 안드로이드 네이티브 자동화에 대한 재조사가 필요합니다. 물론 Appium도 조사해 본 결과 uiautomator2 프로젝트의 실행 속도를 발견했습니다. 정말 좋습니다. 요소 감지부터 클릭까지 모두 밀리초 안에 완료되며 코드도 이해하기 쉽습니다. 이전에 이렇게 마법 같은 프로젝트를 작성했다고는 전혀 예상하지 못했습니다. 어떻게 이렇게 좋은 프로젝트에 먼지가 쌓일 수 있었을까요? 재구성이 필요하고 일부 정크 코드를 정리해야 할까요? 그래서 프로젝트 버전이 2.xx에서 3.xx로 업그레이드되었습니다.
아직 2.xx 버전을 사용하고 계신 분들은 2to3을 먼저 보시고 3.xx로 업그레이드할지 결정하시면 됩니다. (개인적으로는 업그레이드를 적극 권장합니다.)
결국 버전 2~3은 메이저 버전 업그레이드로 많은 기능이 삭제됐다. 가장 먼저 삭제해야 할 것은 atx-agent이고, 두 번째로 atx-agent 관련 함수들이 잔뜩 있습니다. init와 같은 더 이상 사용되지 않는 기능입니다.
다양한 종속 라이브러리의 버전 번호
UiAutomator는 접근성 서비스를 기반으로 Android 자동화 테스트를 위해 Google에서 제공하는 Java 라이브러리입니다. 이는 매우 강력하며 타사 앱을 테스트하고, 화면에 있는 모든 앱의 제어 속성을 얻고, 모든 작업을 수행할 수 있지만 두 가지 단점이 있습니다. 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더 이상 유지보수 작업이 진행되지 않으므로 최대한 빨리 교체하시기 바랍니다.
이미 시작한 분들에게 적합한 빠른 참조 빠른 참조 가이드입니다. 의견을 환영합니다.
먼저开发者选项
켜진 안드로이드 폰을 한 대(두 개가 아닌) 준비하고, 컴퓨터에 연결한 후, 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 그룹에 오신 것을 환영합니다)
成都-测试只会一点点
설치
장치에 연결
명령줄
전역 설정
앱 관리
UI 자동화
기여자
특허
uiautomator2 설치
pip install -U uiautomator2
설치 성공 여부 테스트 uiautomator2 --help
UI 검사기
pip install uiautodev
# 启动
uiauto.dev
현재 장치의 인터페이스 구조를 보려면 브라우저에서 https://uiauto.dev를 엽니다.
uiauto.dev
uiauto.dev는 uiautomator2와 독립적인 프로젝트로, 레이어 구조를 보는 데 사용됩니다. 이는 이전 프로젝트 편집기를 재구성한 버전이며, 현재 프로젝트의 지속적인 유지 관리를 지원하기 위해 나중에 요금이 청구될 수 있습니다(가격은 확실히 그만한 가치가 있습니다). 관심이 있으시면 그룹에 가입하여 토론(요청 포함)을 하실 수 있습니다. QQ 그룹 536481989
serialno를 사용하여 장치를 연결합니다(예: 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
등에 영향을 미칩니다.
이 부분에서는 앱 관리를 수행하는 방법을 보여줍니다.
URL을 통한 APK 설치만 지원됩니다.
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과 병합된 stdout에 대한 문자열을 반환합니다. 명령이 차단 명령인 경우 명령이 완료되거나 시간 초과가 시작될 때까지 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
WLAN IP 얻기
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 ))
주로 Jiugong 패턴을 잠금 해제하는 데 사용됩니다. 각 지점의 상대 좌표를 미리 얻을 수 있습니다. (퍼센트는 여기에서 지원됩니다.) 자세한 사용법은 u2를 사용하여 Jiugong 패턴을 잠금 해제하세요.
터치하여 놓기(베타)
이 인터페이스는 상대적으로 낮은 수준의 원시 인터페이스로 완벽하지는 않지만 사용할 수 있습니다. 참고: 이 장소는 백분율을 지원하지 않습니다.
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 )
덤프 UI 계층 구조
# 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 ()
선택기는 현재 창에서 특정 UI 개체를 식별하는 편리한 메커니즘입니다.
# 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" )
형제