Grup komunikasi QQ: 815453846 Perselisihan: https://discord.gg/PbJhnZJKDd
Saya sudah lama tidak menjalankan proyek ini (mungkin dua tahun), tetapi baru-baru ini saya perlu meneliti ulang otomatisasi asli Android. Tentu saja, saya juga menyelidiki Appium. Setelah membandingkannya, saya menemukan bahwa kecepatan berjalan proyek uiautomator2 sangat bagus. Cepat, mulai dari mendeteksi elemen hingga mengklik, semuanya dalam milidetik, dan kodenya lebih mudah dipahami. Saya benar-benar tidak menyangka bahwa saya telah menulis proyek ajaib seperti itu sebelumnya. Bagaimana proyek sebaik itu bisa dibiarkan mengumpulkan debu? Jadi versi proyek telah ditingkatkan dari 2.xx menjadi 3.xx
Pengguna yang masih menggunakan versi 2.xx dapat melihat 2to3 sebelum memutuskan apakah akan mengupgrade ke 3.xx (saya pribadi sangat menyarankan untuk mengupgrade)
Bagaimanapun, versi 2 hingga 3 adalah peningkatan versi utama, dan banyak fungsi telah dihapus. Hal pertama yang harus dihapus adalah atx-agent, dan kedua ada banyak fungsi terkait atx-agent. Fungsi yang tidak digunakan lagi seperti init.
Nomor versi dari berbagai perpustakaan dependen
UiAutomator adalah perpustakaan Java yang disediakan oleh Google untuk pengujian otomatisasi Android, berdasarkan layanan Aksesibilitas. Ini sangat kuat dan dapat menguji aplikasi pihak ketiga, mendapatkan atribut kontrol apa pun dari APP apa pun di layar, dan melakukan operasi apa pun di dalamnya, tetapi memiliki dua kelemahan: 1. Skrip pengujian hanya dapat menggunakan bahasa Java 2. Pengujian skrip Itu harus dikemas ke dalam paket jar atau apk dan diunggah ke perangkat untuk dijalankan.
Kami berharap logika pengujian dapat ditulis dengan Python dan dapat mengontrol ponsel saat dijalankan di komputer. Saya ingin mengucapkan terima kasih banyak kepada Xiaocong He (@xiaocong) karena telah mewujudkan ide ini (lihat xiaocong/uiautomator). Prinsipnya adalah menjalankan layanan http rpc di ponsel, membuka fungsi di uiautomator, dan kemudian menggunakan http ini. Antarmuka dienkapsulasi ke dalam perpustakaan Python. Karena perpustakaan xiaocong/uiautomator
sudah lama tidak diperbarui. Jadi kami langsung mem-fork versinya. Agar lebih mudah membedakannya, kami menambahkan 2 openatx/uiautomator2 di akhir. Saya juga mem-fork salinan kode sumber paket Android yang sesuai, openatx/android-uiautomator-server.
Selain memperbaiki bug di perpustakaan asli, banyak fitur baru juga telah ditambahkan. Ini terutama mencakup bagian-bagian berikut:
Biar saya jelaskan dulu di sini, karena banyak orang sering bertanya bahwa openatx/uiautomator2 tidak mendukung pengujian iOS. Jika Anda memerlukan pengujian otomatis iOS, Anda dapat membuka perpustakaan ini openatx/facebook-wda.
PS: Perpustakaan ini
https://github.com/NeteaseGame/ATXSudah tidak lagi dalam pemeliharaan, harap segera diganti.
Berikut adalah referensi singkat PANDUAN REFERENSI CEPAT yang cocok bagi mereka yang sudah memulai. Komentar dipersilakan.
Pertama siapkan satu (bukan dua) ponsel Android dengan开发者选项
aktif, sambungkan ke komputer, dan pastikan Anda dapat melihat perangkat yang terhubung dengan menjalankan adb devices
.
Jalankan pip3 install -U uiautomator2
untuk menginstal uiautomator2
Jalankan python
dari baris perintah untuk membuka jendela interaktif python. Kemudian masukkan perintah berikut ke dalam jendela.
import uiautomator2 as u2
d = u2 . connect () # connect to device
print ( d . info )
Ketika Anda melihat keluaran yang mirip dengan di bawah ini, Anda dapat secara resmi mulai menggunakan perpustakaan kami. Karena perpustakaan ini memiliki terlalu banyak fungsi dan banyak konten di baliknya, Anda perlu membacanya perlahan-lahan....
{'currentPackageName': 'net.oneplus.launcher', 'displayHeight': 1920, 'displayRotation': 0, 'displaySizeDpX': 411, 'displaySizeDpY': 731, 'displayWidth': 1080, 'productName': 'OnePlus5', '
screenOn': True, 'sdkInt': 27, 'naturalOrientation': True}
Selain itu, untuk menjaga stabilitas, Anda perlu mengaktifkan izin jendela mengambang pada小黄车
. Artikel referensi py-uiautomator2 membuat layanan tersedia untuk waktu yang lama melalui jendela mengambang
Biasanya berhasil, tapi mungkin ada kejutan. Anda dapat bergabung dengan grup QQ untuk melaporkan masalah (nomor grup ada di atas). Ada banyak orang besar di grup yang dapat membantu Anda memecahkan masalah.
Terima kasih kepada semua sponsor kami!
Kosong
Rekomendasi artikel yang bagus (selamat datang di saya di grup QQ untuk mendapatkan umpan balik)
成都-测试只会一点点
Instalasi
Hubungkan ke perangkat
Baris perintah
Pengaturan global
Manajemen aplikasi
Otomatisasi UI
Kontributor
LISENSI
Instal uiautomator2
pip install -U uiautomator2
Uji apakah instalasi berhasil uiautomator2 --help
Inspektur UI
pip install uiautodev
# 启动
uiauto.dev
Buka https://uiauto.dev di browser untuk melihat struktur antarmuka perangkat saat ini.
uiauto.dev
uiauto.dev adalah proyek independen dari uiautomator2, digunakan untuk melihat struktur lapisan. Ini adalah versi rekonstruksi dari editor proyek lama, dan mungkin dikenakan biaya nanti (harganya pasti sepadan dengan uang yang dikeluarkan) untuk mendukung kelanjutan pemeliharaan proyek saat ini. Jika berminat bisa gabung di grup untuk diskusi (termasuk request) Grup QQ 536481989
gunakan serialno untuk menghubungkan perangkat misalnya 123456f
(dilihat dari adb devices
)
import uiautomator2 as u2
d = u2 . connect ( '123456f' ) # alias for u2.connect_usb('123456f')
print ( d . info )
Serial dapat diteruskan melalui env-var ANDROID_SERIAL
# export ANDROID_SERIAL=123456f
d = u2 . connect ()
$device_ip
mewakili alamat IP perangkat
Jika Anda perlu menentukan perangkat, Anda harus memasukkan --serial
seperti python3 -m uiautomator2 --serial bff1234
, SubCommand adalah subperintah (tangkapan layar, saat ini, dll.)
1.0.3 Ditambahkan:
python3 -m uiautomator2
sama denganuiautomator2
tangkapan layar: tangkapan layar
$ uiautomator2 screenshot screenshot.jpg
saat ini: Dapatkan nama paket dan aktivitas saat ini
$ uiautomator2 current
{
" package " : " com.android.browser " ,
" activity " : " com.uc.browser.InnerUCMobile " ,
" pid " : 28478
}
uninstall: Copot pemasangan aplikasi
$ uiautomator2 uninstall < package-name > # 卸载一个包
$ uiautomator2 uninstall < package-name- 1> < package-name- 2> # 卸载多个包
$ uiautomator2 uninstall --all # 全部卸载
berhenti: Hentikan aplikasi
$ uiautomator2 stop com.example.app # 停止一个app
$ uiautomator2 stop --all # 停止所有的app
dokter:
$ uiautomator2 doctor
[I 2024-04-25 19:53:36,288 __main__:101 pid:15596] uiautomator2 is OK
Saat python berhenti, layanan UiAutomation juga berhenti.
Cetak informasi permintaan HTTP di balik kode
> >> 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
Tetapkan waktu tunggu pencarian elemen (default 20 detik)
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
Fungsi ini akan berpengaruh pada click
, long_click
, drag_to
, get_text
, set_text
, clear_text
, dll.
Bagian ini menunjukkan cara melakukan manajemen aplikasi
Kami hanya mendukung pemasangan APK dari 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(默认)
Ditambahkan di versi 1.2.0
mendorong file ke perangkat
# 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 )
tarik file dari perangkat
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"
Bagian ini menunjukkan cara melakukan operasi perangkat umum:
Jalankan perintah shell berumur pendek dengan perlindungan batas waktu (batas waktu default 60 detik)
Catatan: dukungan batas waktu memerlukan atx-agent >=0.3.3
Fungsi adb_shell
tidak digunakan lagi. Gunakan shell
sebagai gantinya.
Penggunaan sederhana
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
Argumen pertama bisa berupa daftar
output , exit_code = d . shell ([ "ls" , "-l" ])
# output: "/....", exit_code: 0
Ini mengembalikan string untuk stdout yang digabungkan dengan stderr. Jika perintahnya adalah perintah pemblokiran, shell
juga akan memblokir sampai perintah selesai atau batas waktu dimulai. Tidak ada keluaran parsial yang akan diterima selama eksekusi perintah cocok untuk perintah yang berjalan lama. Perintah shell yang diberikan berjalan di lingkungan serupa adb shell
, yang memiliki tingkat izin Linux adb
atau shell
(lebih tinggi dari izin aplikasi).
Jalankan perintah shell yang sudah berjalan lama (Dihapus)
Sesi mewakili siklus hidup aplikasi. Dapat digunakan untuk memulai aplikasi, mendeteksi kerusakan aplikasi.
Luncurkan dan tutup aplikasi
sess = d . session ( "com.netease.cloudmusic" ) # start 网易云音乐
sess . close () # 停止网易云音乐
sess . restart () # 冷启动网易云音乐
Gunakan python with
untuk meluncurkan dan menutup aplikasi
with d . session ( "com.netease.cloudmusic" ) as sess :
sess ( text = "Play" ). click ()
Lampirkan ke aplikasi yang sedang berjalan
# launch app if not running, skip launch if already running
sess = d . session ( "com.netease.cloudmusic" , attach = True )
Deteksi kerusakan aplikasi
# 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
Dapatkan informasi dasar
d . info
Di bawah ini adalah kemungkinan keluaran:
{'currentPackageName': 'com.android.systemui',
'displayHeight': 1560,
'displayRotation': 0,
'displaySizeDpX': 360,
'displaySizeDpY': 780,
'displayWidth': 720,
'naturalOrientation': True,
'productName': 'ELE-AL00',
'screenOn': True,
'sdkInt': 29}
Dapatkan ukuran jendela
print ( d . window_size ())
# device upright output example: (1080, 1920)
# device horizontal output example: (1920, 1080)
Dapatkan info aplikasi terkini. Untuk beberapa perangkat Android, outputnya mungkin kosong (lihat contoh Output 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}
Tunggu aktivitas
d . wait_activity ( ".ApiDemos" , timeout = 10 ) # default timeout 10.0 seconds
# Output: true of false
Dapatkan nomor seri perangkat
print ( d . serial )
# output example: 74aAEDR428Z9
Dapatkan ip WLAN
print ( d . wlan_ip )
# output example: 10.0.0.1 or None
Dapatkan info perangkat terperinci d.device_info
perangkat_info
print ( d . device_info )
Di bawah ini adalah kemungkinan keluaran:
{'arch': 'arm64-v8a',
'brand': 'google',
'model': 'sdk_gphone64_arm64',
'sdk': 34,
'serial': 'EMULATOR34X1X19X0',
'version': 14}
Dapatkan konten clipboard yang disetel
Atur konten tempel atau dapatkan konten
papan klip/set_papan klip
d . clipboard = 'hello-world'
# or
d . set_clipboard ( 'hello-world' , 'label' )
Dapatkan konten papan klip
dapatkan clipboard memerlukan panggilan IME(com.github.uiautomator/.AdbKeyboard)
d.set_input_ime()
sebelum menggunakannya.
```python
# get clipboard content
print(d.clipboard)
```
Menghidupkan/mematikan layar
d . screen_on () # turn on the screen
d . screen_off () # turn off the screen
Dapatkan status layar saat ini
d . info . get ( 'screenOn' ) # require Android >= 4.4
Tekan tombol keras/lunak
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)
Nama-nama kunci berikut saat ini didukung:
Anda dapat menemukan semua definisi kode kunci di Android KeyEvnet
Buka kunci layar
d . unlock ()
# This is equivalent to
# 1. press("power")
# 2. swipe from left-bottom to right-top
Klik di layar
d . click ( x , y )
Klik dua kali
d . double_click ( x , y )
d . double_click ( x , y , 0.1 ) # default duration between two click is 0.1s
Klik panjang di layar
d . long_click ( x , y )
d . long_click ( x , y , 0.5 ) # long click 0.5s (default)
Babatan
d . swipe ( sx , sy , ex , ey )
d . swipe ( sx , sy , ex , ey , 0.5 ) # swipe for 0.5s(default)
Fungsi ekstensi 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 ) # 页面水平左翻
Menyeret
d . drag ( sx , sy , ex , ey )
d . drag ( sx , sy , ex , ey , 0.5 ) # swipe for 0.5s(default)
Gesek poin
# 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 ))
Ini sebagian besar digunakan untuk membuka kunci pola Jiugong. Koordinat relatif setiap titik dapat diperoleh terlebih dahulu (persentase didukung di sini). Untuk penggunaan lebih detail, silakan lihat posting ini.
Sentuh dan letakkan (Beta)
Antarmuka ini adalah antarmuka primitif tingkat rendah. Tampaknya tidak sempurna, tetapi dapat digunakan. Catatan: Tempat ini tidak mendukung persentase
d . touch . down ( 10 , 10 ) # 模拟按下
time . sleep ( .01 ) # down 和 move 之间的延迟,自己控制
d . touch . move ( 15 , 15 ) # 模拟移动
d . touch . up ( 10 , 10 ) # 模拟抬起
Catatan: operasi klik, geser, tarik mendukung nilai posisi persentase.
d.long_click(0.5, 0.5)
berarti klik panjang di tengah layar
Ambil/Atur orientasi perangkat
Orientasi yang mungkin:
natural
atau n
left
atau l
right
atau r
upsidedown
atau u
(tidak dapat diatur) # 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"
Rotasi Bekukan/Batalkan Beku
# freeze rotation
d . freeze_rotation ()
# un-freeze rotation
d . freeze_rotation ( False )
Ambil tangkapan layar
# 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 )
Buang hierarki 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 )
Buka notifikasi atau pengaturan cepat
d . open_notification ()
d . open_quick_settings ()
Selector adalah mekanisme praktis untuk mengidentifikasi objek UI tertentu di jendela saat ini.
# Select the object with text 'Clock' and its className is 'android.widget.TextView'
d ( text = 'Clock' , className = 'android.widget.TextView' )
Selector mendukung parameter di bawah ini. Lihat dokumen UiSelector Java untuk informasi detail.
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
anak-anak
# get the children or grandchildren
d ( className = "android.widget.ListView" ). child ( text = "Bluetooth" )
saudara
# get siblings
d ( text = "Google" ). sibling ( className = "android.widget.ImageView" )
anak-anak melalui teks atau deskripsi atau contoh
# 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
adalah untuk menemukan anak yang cucunya memiliki deskripsi yang ditentukan, parameter lainnya serupa dengan child_by_text
.
child_by_instance
adalah untuk menemukan anak-anak dengan elemen UI anak di mana saja dalam sub hierarkinya yang berada pada instance yang ditentukan. Ini dilakukan pada tampilan yang terlihat tanpa menggulir .
Lihat tautan di bawah untuk informasi selengkapnya:
getChildByDescription
, getChildByText
, getChildByInstance
getChildByDescription
, getChildByText
, getChildByInstance
Metode di atas mendukung pemanggilan berantai, misalnya untuk hierarki di bawah
< 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 >
Untuk mengklik widget saklar langsung ke TextView 'Wi‑Fi', kita perlu memilih widget saklar terlebih dahulu. Namun, berdasarkan hierarki UI, tidak akan ada lebih dari satu widget saklar dan memiliki properti yang hampir sama Alternatifnya, strategi pemilihan di bawah ini akan membantu:
d ( className = "android.widget.ListView" , resourceId = "android:id/list" )
. child_by_text ( "Wi‑Fi" , className = "android.widget.LinearLayout" )
. child ( className = "android.widget.Switch" )
. click ()
posisi relatif
Kita juga dapat menggunakan metode penentuan posisi relatif untuk mendapatkan tampilan: left
, right
, top
, bottom
.
d(A).left(B)
, pilih B di sisi kiri A.d(A).right(B)
, pilih B di sisi kanan A.d(A).up(B)
, pilih B di atas A.d(A).down(B)
, pilih B di bawah A.Jadi untuk kasus di atas, alternatifnya kita dapat memilihnya dengan:
## select "switch" on the right side of "Wi‑Fi"
d ( text = "Wi‑Fi" ). right ( className = "android.widget.Switch" ). click ()
Beberapa contoh
Terkadang layar mungkin berisi beberapa tampilan dengan properti yang sama, misalnya teks, maka Anda harus menggunakan properti "instance" di pemilih untuk memilih salah satu instance yang memenuhi syarat, seperti di bawah ini:
d ( text = "Add new" , instance = 0 ) # which means the first instance with text "Add new"
Selain itu, uiautomator2 menyediakan API seperti daftar (mirip dengan 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 # ...
Catatan : saat menggunakan penyeleksi dalam blok kode yang menelusuri daftar hasil, Anda harus memastikan bahwa elemen UI di layar tetap tidak berubah. Jika tidak, kesalahan Elemen Tidak Ditemukan dapat terjadi saat melakukan iterasi melalui daftar.
Periksa apakah objek UI tertentu ada
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)
Ambil info objek UI tertentu
d ( text = "Settings" ). info
Di bawah ini adalah kemungkinan keluaran:
{ 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
}
Dapatkan/Atur/Hapus teks dari bidang yang dapat diedit (misalnya, widget EditTeks)
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
Dapatkan titik pusat Widget
x , y = d ( text = "Settings" ). center ()
# x, y = d(text="Settings").center(offset=(0, 0)) # left-top x, y
Ambil tangkapan layar widget
im = d ( text = "Settings" ). screenshot ()
im . save ( "settings.jpg" )
Lakukan klik pada objek tertentu
# 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
Lakukan klik panjang pada objek UI tertentu
# long click on the center of the specific UI object
d ( text = "Settings" ). long_click ()
Tarik objek UI menuju titik lain atau objek UI lainnya
# 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 )
Gesek dari tengah objek UI ke tepinya
Gesek mendukung 4 arah:
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 )
Gerakan dua titik dari satu titik ke titik lainnya
d ( text = "Settings" ). gesture (( sx1 , sy1 ), ( sx2 , sy2 ), ( ex1 , ey1 ), ( ex2 , ey2 ))
Isyarat dua titik pada objek UI tertentu
Mendukung dua gerakan:
In
, dari ujung ke tengahOut
, dari tengah ke tepi # 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 ()
Tunggu hingga UI tertentu muncul atau hilang
# 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 )
Batas waktu default adalah 20 detik
Lakukan lemparan pada objek ui tertentu (dapat digulir)
Properti yang mungkin:
horiz
atau vert
forward
atau backward
atau toBeginning
atau 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 ()
Lakukan gulir pada objek ui tertentu (dapat digulir)
Properti yang mungkin:
horiz
atau vert
forward
atau backward
atau toBeginning
atau toEnd
, atau 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 saat ini dimulai menggunakan threading dan diperiksa setiap 2 detik. Saat ini, hanya ada satu operasi pemicu: klik.
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 ()
# 其他脚本逻辑
Cara lain untuk menulis
ctx = d . watch_context ()
ctx . when ( "设置" ). click ()
ctx . wait_stable () # 等待界面不在有弹窗了
ctx . close ()
Lebih disarankan menggunakan WatchContext agar penulisannya lebih ringkas.
Anda dapat mendaftarkan pengamat untuk melakukan beberapa tindakan ketika pemilih tidak menemukan kecocokan.
Sebelum 2.0.0, metode [Watcher]((http://developer.android.com/tools/help/uiautomator/UiWatcher.html) yang disediakan di perpustakaan uiautomator-jar digunakan, tetapi dalam praktiknya ditemukan bahwa sekali koneksi uiautomator gagal Setelah memulai ulang, semua konfigurasi pengamat hilang, yang tentunya tidak dapat diterima.
Oleh karena itu, metode saat ini adalah menjalankan thread di latar belakang (mengandalkan pustaka threading), lalu membuang hierarki sesekali, dan melakukan operasi terkait setelah mencocokkan elemen.
Contoh penggunaan
Pemantauan pendaftaran
# 常用写法,注册匿名监控
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 ()
Pantau operasi
# 移除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 ()
Selain itu masih banyak dokumen yang belum ditulis, disarankan untuk langsung menuju source code watcher.py.
u2 . HTTP_TIMEOUT = 60 # 默认值60s, http默认请求超时时间
Sebagian besar konfigurasi lainnya saat ini terkonsentrasi di d.settings
, dan konfigurasi dapat ditambah atau dikurangi sesuai kebutuhan selanjutnya.
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返回的元素层级
Untuk peningkatan versi, saat menyetel konfigurasi yang kedaluwarsa, Deprecated akan diminta, namun tidak ada pengecualian yang akan ditampilkan.
>>> d.settings[ ' click_before_delay ' ] = 1
[W 200514 14:55:59 settings:72] d.settings[click_before_delay] deprecated: Use operation_delay instead
pengaturan mode pemulihan uiautomator
Jika Anda berhati-hati, Anda mungkin menemukan bahwa sebenarnya ada dua APK yang terpasang di ponsel Anda, salah satunya terlihat di latar depan (mobil kuning kecil). Paket bernama com.github.uiautomator.test
tidak terlihat di latar belakang. Kedua apk ini ditandatangani dengan sertifikat yang sama. Aplikasi tak terlihat sebenarnya adalah paket pengujian yang berisi semua kode pengujian, dan layanan pengujian inti juga dimulai melaluinya. Namun saat dijalankan, sistem memerlukan mobil kecil berwarna kuning untuk terus berjalan (dapat juga dijalankan di latar belakang). Setelah aplikasi mobil kuning kecil dimatikan, layanan pengujian yang berjalan di latar belakang juga akan dimatikan dengan cepat. Bahkan jika Anda tidak melakukan apa pun, aplikasi akan dengan cepat didaur ulang oleh sistem jika berada di latar belakang. (Saya berharap para ahli dapat memberi saya beberapa panduan di sini tentang cara menghindari ketergantungan pada aplikasi palsu. Menurut saya secara teoritis hal itu mungkin dilakukan, namun saya belum tahu cara melakukannya).
Ada dua cara untuk membiarkan mobil kuning kecil berjalan di latar belakang. Salah satunya adalah dengan memulai aplikasi dan meletakkannya di latar belakang (default). Selain itu, Anda juga dapat memulai layanan latar belakang melalui am startservice
.
Perilaku ini dapat disesuaikan melalui d.settings["uiautomator_runtest_app_background"] = True
. Benar berarti memulai aplikasi, Salah berarti memulai layanan.
Pengaturan batas waktu di UiAutomator (metode tersembunyi)
>> 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 }
Untuk mencegah program klien merespons batas waktu, waitForIdleTimeout
dan waitForSelectorTimeout
kini telah diubah menjadi 0
Referensi: Konfigurasi uiautomator Google
Metode ini biasanya digunakan untuk input yang kontrolnya tidak diketahui.
# 目前采用从剪贴板粘贴的方式输入
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
Referensi lebih lanjut: IME_ACTION_CODE
print ( d . last_toast ) # get last toast, if not toast return None
d . clear_toast ()
Diperbaiki di versi 3.2.0
Java uiautoamtor tidak mendukung xpath secara default, jadi ini adalah fungsi yang diperluas. Kecepatannya tidak secepat itu.
Misalnya: isi salah satu node
">< 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] " />
pemosisian dan penggunaan xpath
Nama beberapa atribut telah diubah dan perlu diperhatikan.
description -> content-desc
resourceId -> resource-id
Penggunaan umum
# 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 ())
Klik untuk melihat penggunaan umum XPath lainnya
Rekaman video (usang), gunakan scrcpy sebagai gantinya
Perintah screenrecord yang disertakan dengan ponsel tidak digunakan di sini. Ini adalah metode sintesis video dengan memperoleh gambar ponsel, jadi Anda perlu menginstal beberapa dependensi lain, seperti imageio, imageio-ffmpeg, numpy, dll. ketergantungannya relatif besar, disarankan untuk menggunakan instalasi mirror. Jalankan saja perintah berikut.
pip3 install -U " uiautomator2[image] " -i https://pypi.doubanio.com/simple
Cara menggunakan
d.screenrecord('output.mp4')
time.sleep(10)
# or do something else
d.screenrecord.stop() # 停止录制后,output.mp4文件才能打开
Anda juga dapat menentukan fps (saat ini 20) saat merekam. Nilai ini lebih rendah dari kecepatan gambar keluaran minicap. Rasanya sangat bagus.
from uiautomator2 import enable_pretty_logging
enable_pretty_logging ()
Atau
logger = logging.getLogger("uiautomator2")
# setup logger
Saat program Python keluar, UiAutomation keluar. Namun, Anda juga dapat menghentikan layanan melalui metode antarmuka.
d . stop_uiautomator ()
https://www.cnblogs.com/insist8089/p/6898181.html
Kontributor lainnya
Pemeringkatan sudah berurutan, selamat datang untuk menambahkan
MIT