กลุ่มสื่อสาร QQ: 815453846 Discord: https://discord.gg/PbJhnZJKDd
ฉันไม่ได้ดูแลโปรเจ็กต์นี้มาระยะหนึ่งแล้ว (อาจจะสองปี) แต่เมื่อเร็ว ๆ นี้ฉันต้องวิจัยระบบอัตโนมัติแบบเนทีฟของ Android อีกครั้ง แน่นอนว่าฉันได้ตรวจสอบ Appium ด้วย หลังจากเปรียบเทียบแล้ว ฉันพบว่าความเร็วในการทำงานของโปรเจ็กต์ uiautomator2 ดีมากจริงๆ มันรวดเร็วตั้งแต่การตรวจจับองค์ประกอบไปจนถึงการคลิก ทั้งหมดใช้เวลาเป็นมิลลิวินาที และโค้ดก็เข้าใจง่ายกว่า ฉันไม่เคยคาดหวังว่าฉันจะเขียนโปรเจ็กต์มหัศจรรย์เช่นนี้มาก่อนได้อย่างไร โปรเจ็กต์ดีๆ แบบนี้จะปล่อยให้มันสะสมฝุ่นได้อย่างไร มันจำเป็นต้องได้รับการจัดระเบียบใหม่และโค้ดขยะบางส่วนควรได้รับการทำความสะอาด ดังนั้นเวอร์ชันโปรเจ็กต์จึงได้รับการอัปเกรดจาก 2.xx เป็น 3.xx
ผู้ใช้ที่ยังใช้เวอร์ชัน 2.xx สามารถดู 2to3 ก่อนตัดสินใจว่าจะอัพเกรดเป็น 3.xx หรือไม่ (โดยส่วนตัวผมแนะนำให้อัปเกรดเป็นอย่างยิ่ง)
ท้ายที่สุดแล้ว เวอร์ชัน 2 ถึง 3 ถือเป็นการอัพเกรดเวอร์ชันหลัก และฟังก์ชันต่างๆ มากมายได้ถูกลบออกไปแล้ว สิ่งแรกที่ต้องลบคือ atx-agent และอย่างที่สอง มีฟังก์ชันที่เกี่ยวข้องกับ atx-agent มากมาย ฟังก์ชันที่เลิกใช้แล้ว เช่น init
หมายเลขเวอร์ชันของไลบรารีที่ต้องพึ่งพาต่างๆ
UiAutomator เป็นไลบรารี Java ที่จัดทำโดย Google สำหรับการทดสอบอัตโนมัติของ Android โดยอิงตามบริการการเข้าถึง มันมีประสิทธิภาพมากและสามารถทดสอบแอพของบุคคลที่สาม รับคุณสมบัติการควบคุมของแอพใด ๆ บนหน้าจอ และดำเนินการใด ๆ กับมันได้ แต่มีข้อเสียสองประการ: 1. สคริปต์ทดสอบสามารถใช้ได้เฉพาะภาษา Java เท่านั้น 2. การทดสอบ script จะต้องบรรจุลงในแพ็คเกจ 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 สำหรับข้อเสนอแนะ)
成都-测试只会一点点
การติดตั้ง
เชื่อมต่อกับอุปกรณ์
บรรทัดคำสั่ง
การตั้งค่าส่วนกลาง
การจัดการแอป
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 )
Serial สามารถส่งผ่าน env-var ANDROID_SERIAL
# export ANDROID_SERIAL=123456f
d = u2 . connect ()
$device_ip
แสดงถึงที่อยู่ IP ของอุปกรณ์
หากคุณต้องการระบุอุปกรณ์ คุณจะต้องส่งผ่านใน --serial
เช่น python3 -m uiautomator2 --serial bff1234
, SubCommand เป็นคำสั่งย่อย (ภาพหน้าจอ ปัจจุบัน ฯลฯ)
1.0.3 เพิ่มแล้ว:
python3 -m uiautomator2
เท่ากับuiautomator2
ภาพหน้าจอ: ภาพหน้าจอ
$ uiautomator2 screenshot screenshot.jpg
ปัจจุบัน: รับชื่อแพ็คเกจและกิจกรรมปัจจุบัน
$ 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
สิ่งนี้จะส่งคืนสตริงสำหรับ stdout ที่ผสานกับ stderr หากคำสั่งเป็นคำสั่งการบล็อก shell
จะบล็อกจนกว่าคำสั่งจะเสร็จสมบูรณ์หรือหมดเวลาใช้งาน โดยจะไม่ได้รับเอาต์พุตบางส่วนระหว่างการดำเนินการของคำสั่ง adb
adb shell
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
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 สามารถรับพิกัดสัมพัทธ์ของแต่ละจุดล่วงหน้าได้ (รองรับเปอร์เซ็นต์ที่นี่) สำหรับการใช้งานโดยละเอียดเพิ่มเติม โปรดดูที่โพสต์นี้
แตะแล้ววาง (เบต้า)
อินเทอร์เฟซนี้เป็นอินเทอร์เฟซดั้งเดิมระดับต่ำ แม้จะดูไม่สมบูรณ์แบบ แต่ก็ใช้งานได้ หมายเหตุ: สถานที่นี้ไม่รองรับเปอร์เซ็นต์
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' )
ตัวเลือกรองรับพารามิเตอร์ด้านล่าง โปรดดูเอกสาร 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" )
พี่น้อง
# get siblings
d ( text = "Google" ). sibling ( className = "android.widget.ImageView" )
เด็กตามข้อความหรือคำอธิบายหรือตัวอย่าง
# get the child matching the condition className="android.widget.LinearLayout"
# and also its children or grandchildren with text "Bluetooth"
d ( className = "android.widget.ListView" , resourceId = "android:id/list" )
. child_by_text ( "Bluetooth" , className = "android.widget.LinearLayout" )
# get children by allowing scroll search
d ( className = "android.widget.ListView" , resourceId = "android:id/list" )
. child_by_text (
"Bluetooth" ,
allow_scroll_search = True ,
className = "android.widget.LinearLayout"
)
child_by_description
คือการค้นหาเด็กที่มีหลานมีคำอธิบายที่ระบุ พารามิเตอร์อื่นๆ ที่คล้ายกับ child_by_text
child_by_instance
คือการค้นหาลูกที่มีองค์ประกอบ UI ลูกที่ใดก็ได้ภายในลำดับชั้นย่อยที่อยู่ในอินสแตนซ์ที่ระบุ โดยจะดำเนินการในมุมมองที่มองเห็นได้ โดยไม่ต้องเลื่อน
ดูลิงค์ด้านล่างสำหรับข้อมูลโดยละเอียด:
getChildByDescription
, getChildByText
, getChildByInstance
getChildByDescription
, getChildByText
, getChildByInstance
วิธีการข้างต้นรองรับการเรียกใช้แบบลูกโซ่ เช่น สำหรับลำดับชั้นด้านล่าง
< node index = " 0 " text = " " resource-id = " android:id/list " class = " android.widget.ListView " ...>
< node index = " 0 " text = " WIRELESS & NETWORKS " resource-id = " " class = " android.widget.TextView " .../>
< node index = " 1 " text = " " resource-id = " " class = " android.widget.LinearLayout " ...>
< node index = " 1 " text = " " resource-id = " " class = " android.widget.RelativeLayout " ...>
< node index = " 0 " text = " Wi‑Fi " resource-id = " android:id/title " class = " android.widget.TextView " .../>
node >
< node index = " 2 " text = " ON " resource-id = " com.android.settings:id/switchWidget " class = " android.widget.Switch " .../>
node >
...
node >
หากต้องการคลิกวิดเจ็ตสวิตช์ไปที่ TextView 'Wi-Fi' เราจำเป็นต้องเลือกวิดเจ็ตสวิตช์ก่อน อย่างไรก็ตาม ตามลำดับชั้นของ UI มีวิดเจ็ตสวิตช์มากกว่าหนึ่งรายการและมีคุณสมบัติเกือบจะเหมือนกัน กลยุทธ์การเลือกด้านล่างจะช่วยได้:
d ( className = "android.widget.ListView" , resourceId = "android:id/list" )
. child_by_text ( "Wi‑Fi" , className = "android.widget.LinearLayout" )
. child ( className = "android.widget.Switch" )
. click ()
ตำแหน่งสัมพัทธ์
นอกจากนี้เรายังสามารถใช้วิธีการวางตำแหน่งแบบสัมพันธ์เพื่อรับมุมมอง: left
, right
, top
, bottom
.
d(A).left(B)
เลือก B ทางด้านซ้ายของ Ad(A).right(B)
เลือก B ทางด้านขวาของ Ad(A).up(B)
เลือก B เหนือ Ad(A).down(B)
เลือก B ใต้ Aดังนั้นสำหรับกรณีข้างต้น เราสามารถเลือกได้ด้วย:
## select "switch" on the right side of "Wi‑Fi"
d ( text = "Wi‑Fi" ). right ( className = "android.widget.Switch" ). click ()
หลายกรณี
บางครั้งหน้าจออาจมีหลายมุมมองที่มีคุณสมบัติเหมือนกัน เช่น ข้อความ คุณจะต้องใช้คุณสมบัติ "อินสแตนซ์" ในตัวเลือกเพื่อเลือกหนึ่งในอินสแตนซ์ที่มีคุณสมบัติตามที่กำหนด ดังเช่นด้านล่าง:
d ( text = "Add new" , instance = 0 ) # which means the first instance with text "Add new"
นอกจากนี้ uiautomator2 ยังมี API ที่เหมือนรายการ (คล้ายกับ jQuery):
# get the count of views with text "Add new" on current screen
d ( text = "Add new" ). count
# same as count property
len ( d ( text = "Add new" ))
# get the instance via index
d ( text = "Add new" )[ 0 ]
d ( text = "Add new" )[ 1 ]
...
# iterator
for view in d ( text = "Add new" ):
view . info # ...
หมายเหตุ : เมื่อใช้ตัวเลือกในบล็อกโค้ดที่เดินผ่านรายการผลลัพธ์ คุณต้องแน่ใจว่าองค์ประกอบ UI บนหน้าจอไม่มีการเปลี่ยนแปลง มิฉะนั้น เมื่อเกิดข้อผิดพลาดองค์ประกอบไม่พบเมื่อวนซ้ำรายการ
ตรวจสอบว่ามีวัตถุ UI เฉพาะอยู่หรือไม่
d ( text = "Settings" ). exists # True if exists, else False
d . exists ( text = "Settings" ) # alias of above property.
# advanced usage
d ( text = "Settings" ). exists ( timeout = 3 ) # wait Settings appear in 3s, same as .wait(3)
ดึงข้อมูลของวัตถุ UI ที่เฉพาะเจาะจง
d ( text = "Settings" ). info
ด้านล่างนี้เป็นผลลัพธ์ที่เป็นไปได้:
{ u'contentDescription': u'',
u'checked': False,
u'scrollable': False,
u'text': u'Settings',
u'packageName': u'com.android.launcher',
u'selected': False,
u'enabled': True,
u'bounds': {u'top': 385,
u'right': 360,
u'bottom': 585,
u'left': 200},
u'className': u'android.widget.TextView',
u'focused': False,
u'focusable': True,
u'clickable': True,
u'chileCount': 0,
u'longClickable': True,
u'visibleBounds': {u'top': 385,
u'right': 360,
u'bottom': 585,
u'left': 200},
u'checkable': False
}
รับ/ตั้งค่า/ล้างข้อความของฟิลด์ที่แก้ไขได้ (เช่น วิดเจ็ต EditText)
d ( text = "Settings" ). get_text () # get widget text
d ( text = "Settings" ). set_text ( "My text..." ) # set the text
d ( text = "Settings" ). clear_text () # clear the text
รับจุดศูนย์กลางวิดเจ็ต
x , y = d ( text = "Settings" ). center ()
# x, y = d(text="Settings").center(offset=(0, 0)) # left-top x, y
จับภาพหน้าจอของวิดเจ็ต
im = d ( text = "Settings" ). screenshot ()
im . save ( "settings.jpg" )
ทำการคลิกที่วัตถุเฉพาะ
# click on the center of the specific ui object
d ( text = "Settings" ). click ()
# wait element to appear for at most 10 seconds and then click
d ( text = "Settings" ). click ( timeout = 10 )
# click with offset(x_offset, y_offset)
# click_x = x_offset * width + x_left_top
# click_y = y_offset * height + y_left_top
d ( text = "Settings" ). click ( offset = ( 0.5 , 0.5 )) # Default center
d ( text = "Settings" ). click ( offset = ( 0 , 0 )) # click left-top
d ( text = "Settings" ). click ( offset = ( 1 , 1 )) # click right-bottom
# click when exists in 10s, default timeout 0s
clicked = d ( text = 'Skip' ). click_exists ( timeout = 10.0 )
# click until element gone, return bool
is_gone = d ( text = "Skip" ). click_gone ( maxretry = 10 , interval = 1.0 ) # maxretry default 10, interval default 1.0
ทำการคลิกแบบยาวบนวัตถุ UI ที่ระบุ
# long click on the center of the specific UI object
d ( text = "Settings" ). long_click ()
ลากวัตถุ UI ไปยังจุดอื่นหรือวัตถุ UI อื่น
# notes : drag can not be used for Android<4.3.
# drag the UI object to a screen point (x, y), in 0.5 second
d ( text = "Settings" ). drag_to ( x , y , duration = 0.5 )
# drag the UI object to (the center position of) another UI object, in 0.25 second
d ( text = "Settings" ). drag_to ( text = "Clock" , duration = 0.25 )
ปัดจากกึ่งกลางของวัตถุ UI ไปที่ขอบ
การปัดรองรับ 4 ทิศทาง:
d ( text = "Settings" ). swipe ( "right" )
d ( text = "Settings" ). swipe ( "left" , steps = 10 )
d ( text = "Settings" ). swipe ( "up" , steps = 20 ) # 1 steps is about 5ms, so 20 steps is about 0.1s
d ( text = "Settings" ). swipe ( "down" , steps = 20 )
ท่าทางสองจุดจากจุดหนึ่งไปอีกจุดหนึ่ง
d ( text = "Settings" ). gesture (( sx1 , sy1 ), ( sx2 , sy2 ), ( ex1 , ey1 ), ( ex2 , ey2 ))
ท่าทางสองจุดบนวัตถุ UI ที่เฉพาะเจาะจง
รองรับสองท่าทาง:
In
จากขอบถึงกึ่งกลางOut
จากกึ่งกลางถึงขอบ # notes : pinch can not be set until Android 4.3.
# from edge to center. here is "In" not "in"
d ( text = "Settings" ). pinch_in ( percent = 100 , steps = 10 )
# from center to edge
d ( text = "Settings" ). pinch_out ()
รอจนกระทั่ง UI ที่ระบุปรากฏขึ้นหรือหายไป
# wait until the ui object appears
d ( text = "Settings" ). wait ( timeout = 3.0 ) # return bool
# wait until the ui object gone
d ( text = "Settings" ). wait_gone ( timeout = 1.0 )
การหมดเวลาเริ่มต้นคือ 20 วินาที ดู การตั้งค่าส่วนกลาง สำหรับรายละเอียดเพิ่มเติม
ดำเนินการเหวี่ยงบนวัตถุ UI ที่เฉพาะเจาะจง (เลื่อนได้)
คุณสมบัติที่เป็นไปได้:
horiz
หรือ vert
forward
หรือ backward
หรือ toBeginning
หรือ toEnd
# fling forward(default) vertically(default)
d ( scrollable = True ). fling ()
# fling forward horizontally
d ( scrollable = True ). fling . horiz . forward ()
# fling backward vertically
d ( scrollable = True ). fling . vert . backward ()
# fling to beginning horizontally
d ( scrollable = True ). fling . horiz . toBeginning ( max_swipes = 1000 )
# fling to end vertically
d ( scrollable = True ). fling . toEnd ()
ทำการเลื่อนบนวัตถุ UI เฉพาะ (เลื่อนได้)
คุณสมบัติที่เป็นไปได้:
horiz
หรือ vert
forward
หรือ backward
หรือ toBeginning
หรือ toEnd
หรือ to
# scroll forward(default) vertically(default)
d ( scrollable = True ). scroll ( steps = 10 )
# scroll forward horizontally
d ( scrollable = True ). scroll . horiz . forward ( steps = 100 )
# scroll backward vertically
d ( scrollable = True ). scroll . vert . backward ()
# scroll to beginning horizontally
d ( scrollable = True ). scroll . horiz . toBeginning ( steps = 100 , max_swipes = 1000 )
# scroll to end vertically
d ( scrollable = True ). scroll . toEnd ()
# scroll forward vertically until specific ui object appears
d ( scrollable = True ). scroll . to ( text = "Security" )
watch_context ปัจจุบันเริ่มต้นโดยใช้เธรดและมีการตรวจสอบทุกๆ 2 วินาที ขณะนี้มีการดำเนินการทริกเกอร์เพียงรายการเดียวเท่านั้น: คลิก
with d . watch_context () as ctx :
# 当同时出现 (立即下载 或 立即更新)和 取消 按钮的时候,点击取消
ctx . when ( "^立即(下载|更新)" ). when ( "取消" ). click ()
ctx . when ( "同意" ). click ()
ctx . when ( "确定" ). click ()
# 上面三行代码是立即执行完的,不会有什么等待
ctx . wait_stable () # 开启弹窗监控,并等待界面稳定(两个弹窗检查周期内没有弹窗代表稳定)
# 使用call函数来触发函数回调
# call 支持两个参数,d和el,不区分参数位置,可以不传参,如果传参变量名不能写错
# eg: 当有元素匹配仲夏之夜,点击返回按钮
ctx . when ( "仲夏之夜" ). call ( lambda d : d . press ( "back" ))
ctx . when ( "确定" ). call ( lambda el : el . click ())
# 其他操作
# 为了方便也可以使用代码中默认的弹窗监控逻辑
# 下面是目前内置的默认逻辑,可以加群at群主,增加新的逻辑,或者直接提pr
# when("继续使用").click()
# when("移入管控").when("取消").click()
# when("^立即(下载|更新)").when("取消").click()
# when("同意").click()
# when("^(好的|确定)").click()
with d . watch_context ( builtin = True ) as ctx :
# 在已有的基础上增加
ctx . when ( "@tb:id/jview_view" ). when ( '//*[@content-desc="图片"]' ). click ()
# 其他脚本逻辑
อีกวิธีหนึ่งในการเขียน
ctx = d . watch_context ()
ctx . when ( "设置" ). click ()
ctx . wait_stable () # 等待界面不在有弹窗了
ctx . close ()
ขอแนะนำให้ใช้ WatchContext มากกว่า เพื่อเขียนให้กระชับยิ่งขึ้น
คุณสามารถลงทะเบียนผู้ดูเพื่อดำเนินการบางอย่างเมื่อตัวเลือกไม่พบรายการที่ตรงกัน
ก่อนเวอร์ชัน 2.0.0 มีการใช้เมธอด [Watcher]((http://developer.android.com/tools/help/uiautomator/UiWatcher.html) ที่ให้ไว้ในไลบรารี uiautomator-jar แต่ในทางปฏิบัติพบว่าครั้งหนึ่ง การเชื่อมต่อ uiautomator ล้มเหลว หลังจากรีสตาร์ท การกำหนดค่าตัวเฝ้าดูทั้งหมดจะหายไป ซึ่งเป็นที่ยอมรับไม่ได้อย่างแน่นอน
ดังนั้นวิธีการปัจจุบันคือการรันเธรดในพื้นหลัง (อาศัยไลบรารีเธรด) จากนั้นดัมพ์ลำดับชั้นเป็นระยะ ๆ และดำเนินการที่เกี่ยวข้องหลังจากจับคู่องค์ประกอบ
ตัวอย่างการใช้งาน
การตรวจสอบการลงทะเบียน
# 常用写法,注册匿名监控
d . watcher . when ( "安装" ). click ()
# 注册名为ANR的监控,当出现ANR和Force Close时,点击Force Close
d . watcher ( "ANR" ). when ( xpath = "ANR" ). when ( "Force Close" ). click ()
# 其他回调例子
d . watcher . when ( "抢红包" ). press ( "back" )
d . watcher . when ( "//*[@text = 'Out of memory']" ). call ( lambda d : d . shell ( 'am force-stop com.im.qq' ))
# 回调说明
def click_callback ( d : u2 . Device ):
d . xpath ( "确定" ). click () # 在回调中调用不会再次触发watcher
d . xpath ( "继续" ). click () # 使用d.xpath检查元素的时候,会触发watcher(目前最多触发5次)
# 开始后台监控
d . watcher . start ()
ติดตามการดำเนินงาน
# 移除ANR的监控
d . watcher . remove ( "ANR" )
# 移除所有的监控
d . watcher . remove ()
# 开始后台监控
d . watcher . start ()
d . watcher . start ( 2.0 ) # 默认监控间隔2.0s
# 强制运行所有监控
d . watcher . run ()
# 停止监控
d . watcher . stop ()
# 停止并移除所有的监控,常用于初始化
d . watcher . reset ()
นอกจากนี้ ยังมีเอกสารอีกมากมายที่ยังไม่ได้เขียน ขอแนะนำให้ไปที่ซอร์สโค้ด watcher.py โดยตรง
u2 . HTTP_TIMEOUT = 60 # 默认值60s, http默认请求超时时间
ปัจจุบันการกำหนดค่าอื่นๆ ส่วนใหญ่กระจุกตัวอยู่ใน d.settings
และการกำหนดค่าอาจเพิ่มขึ้นหรือลดลงตามความต้องการในภายหลัง
print ( d . settings )
{ 'operation_delay' : ( 0 , 0 ),
'operation_delay_methods' : [ 'click' , 'swipe' ],
'wait_timeout' : 20.0 }
# 配置点击前延时0.5s,点击后延时1s
d . settings [ 'operation_delay' ] = ( .5 , 1 )
# 修改延迟生效的方法
# 其中 double_click, long_click 都对应click
d . settings [ 'operation_delay_methods' ] = [ 'click' , 'swipe' , 'drag' , 'press' ]
d . settings [ 'wait_timeout' ] = 20.0 # 默认控件等待时间(原生操作,xpath插件的等待时间)
d . settings [ 'max_depth' ] = 50 # 默认50,限制dump_hierarchy返回的元素层级
สำหรับการอัปเกรดเวอร์ชัน เมื่อตั้งค่าการกำหนดค่าที่หมดอายุ ระบบจะแจ้งเลิกใช้แล้ว แต่จะไม่มีข้อยกเว้นเกิดขึ้น
>>> d.settings[ ' click_before_delay ' ] = 1
[W 200514 14:55:59 settings:72] d.settings[click_before_delay] deprecated: Use operation_delay instead
การตั้งค่าโหมดการกู้คืน uiautomator
หากคุณระวัง คุณอาจพบว่ามี APK สองตัวติดตั้งอยู่ในโทรศัพท์ของคุณ โดยหนึ่งในนั้นมองเห็นได้ในเบื้องหน้า (รถสีเหลืองเล็กๆ) แพ็คเกจชื่อ com.github.uiautomator.test
จะไม่ปรากฏในพื้นหลัง APK ทั้งสองนี้ลงนามด้วยใบรับรองเดียวกัน แอปพลิเคชันที่มองไม่เห็นจริงๆ แล้วเป็นแพ็คเกจทดสอบที่มีโค้ดทดสอบทั้งหมด และบริการทดสอบหลักก็เริ่มต้นผ่านแอปพลิเคชันนั้นด้วย แต่เมื่อวิ่งระบบต้องการให้รถสีเหลืองคันเล็กวิ่งตลอดเวลา (สามารถวิ่งอยู่เบื้องหลังได้เช่นกัน) เมื่อแอปพลิเคชั่นรถสีเหลืองตัวเล็ก ๆ ถูกฆ่า บริการทดสอบที่ทำงานในพื้นหลังก็จะถูกฆ่าอย่างรวดเร็วเช่นกัน แม้ว่าคุณจะไม่ได้ทำอะไรเลย แอปพลิเคชันจะถูกรีไซเคิลอย่างรวดเร็วโดยระบบหากอยู่ในเบื้องหลัง (ฉันหวังว่าผู้เชี่ยวชาญสามารถให้คำแนะนำแก่ฉันเกี่ยวกับวิธีหลีกเลี่ยงการพึ่งพาแอปปลอม ฉันคิดว่ามันเป็นไปได้ในทางทฤษฎี แต่ฉันยังไม่รู้ว่าต้องทำอย่างไร)
มีสองวิธีในการปล่อยให้รถสีเหลืองวิ่งอยู่ในพื้นหลัง วิธีหนึ่งคือการเริ่มแอปพลิเคชันและวางไว้ในพื้นหลัง (ค่าเริ่มต้น) นอกจากนี้คุณยังสามารถเริ่มบริการพื้นหลังผ่าน am startservice
ได้อีกด้วย
พฤติกรรมนี้สามารถปรับได้ผ่าน d.settings["uiautomator_runtest_app_background"] = True
True หมายถึงการเริ่มแอปพลิเคชัน False หมายถึงการเริ่มบริการ
การตั้งค่าการหมดเวลาใน UiAutomator (วิธีซ่อน)
>> d . jsonrpc . getConfigurator ()
{ 'actionAcknowledgmentTimeout' : 500 ,
'keyInjectionDelay' : 0 ,
'scrollAcknowledgmentTimeout' : 200 ,
'waitForIdleTimeout' : 0 ,
'waitForSelectorTimeout' : 0 }
>> d . jsonrpc . setConfigurator ({ "waitForIdleTimeout" : 100 })
{ 'actionAcknowledgmentTimeout' : 500 ,
'keyInjectionDelay' : 0 ,
'scrollAcknowledgmentTimeout' : 200 ,
'waitForIdleTimeout' : 100 ,
'waitForSelectorTimeout' : 0 }
เพื่อป้องกันไม่ให้โปรแกรมไคลเอ็นต์ตอบสนองต่อการหมดเวลา ขณะนี้ waitForIdleTimeout
และ waitForSelectorTimeout
ได้ถูกเปลี่ยนเป็น 0
อ้างอิง: Google uiautomator Configurator
โดยทั่วไปวิธีนี้จะใช้สำหรับการป้อนข้อมูลโดยที่ไม่ทราบการควบคุม
# 目前采用从剪贴板粘贴的方式输入
d . send_keys ( "你好123abcEFG" )
d . send_keys ( "你好123abcEFG" , clear = True )
d . clear_text () # 清除输入框所有内容
d . send_action () # 根据输入框的需求,自动执行回车、搜索等指令, Added in version 3.1
# 也可以指定发送的输入法action, eg: d.send_action("search") 支持 go, search, send, next, done, previous
print ( d . current_ime ()) # 获取当前输入法ID
ข้อมูลอ้างอิงเพิ่มเติม: IME_ACTION_CODE
print ( d . last_toast ) # get last toast, if not toast return None
d . clear_toast ()
แก้ไขในเวอร์ชัน 3.2.0
Java uiautoamtor ไม่รองรับ xpath ตามค่าเริ่มต้น ดังนั้นนี่คือฟังก์ชันเพิ่มเติม ความเร็วก็ไม่เร็วขนาดนั้น
ตัวอย่างเช่น เนื้อหาของโหนดใดโหนดหนึ่ง
">< android .widget.TextView index = " 2 " text = " 05:19 " resource-id = " com.netease.cloudmusic:id/qf " 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 = " [957,1602][1020,1636] " />
การวางตำแหน่งและการใช้งาน xpath
ชื่อของแอตทริบิวต์บางอย่างได้รับการแก้ไขและจำเป็นต้องจดบันทึกไว้
description -> content-desc
resourceId -> resource-id
การใช้งานทั่วไป
# wait exists 10s
d . xpath ( "//android.widget.TextView" ). wait ( 10.0 )
# find and click
d . xpath ( "//*[@content-desc='分享']" ). click ()
# check exists
if d . xpath ( "//android.widget.TextView[contains(@text, 'Se')]" ). exists :
print ( "exists" )
# get all text-view text, attrib and center point
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 ())
คลิกเพื่อดูการใช้งานทั่วไปอื่นๆ ของ XPath
การบันทึกวิดีโอ (ล้าสมัย) ให้ใช้ scrcpy แทน
ที่นี่ไม่ได้ใช้คำสั่ง screenrecord ที่มาพร้อมกับโทรศัพท์มือถือ มันเป็นวิธีการสังเคราะห์วิดีโอโดยรับรูปภาพจากโทรศัพท์มือถือ ดังนั้นคุณต้องติดตั้งการขึ้นต่อกันอื่น ๆ เช่น imageio, imageio-ffmpeg, numpy เป็นต้น เพราะบางคำสั่ง การขึ้นต่อกันมีขนาดค่อนข้างใหญ่ ขอแนะนำให้ใช้การติดตั้งมิเรอร์ เพียงเรียกใช้คำสั่งต่อไปนี้
pip3 install -U " uiautomator2[image] " -i https://pypi.doubanio.com/simple
วิธีใช้
d.screenrecord('output.mp4')
time.sleep(10)
# or do something else
d.screenrecord.stop() # 停止录制后,output.mp4文件才能打开
คุณยังสามารถระบุ fps ได้ (ปัจจุบันคือ 20) เมื่อบันทึกค่านี้ต่ำกว่าความเร็วของภาพที่ส่งออกแบบมินิแคป
from uiautomator2 import enable_pretty_logging
enable_pretty_logging ()
หรือ
logger = logging.getLogger("uiautomator2")
# setup logger
เมื่อโปรแกรม Python ออก UiAutomation จะออก อย่างไรก็ตาม คุณยังสามารถหยุดบริการโดยใช้วิธีอินเทอร์เฟซได้
d . stop_uiautomator ()
https://www.cnblogs.com/insist8089/p/6898181.html
ผู้มีส่วนร่วมอื่น ๆ
อันดับเป็นไปตามลำดับ ยินดีเพิ่มครับ
เอ็มไอที