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 mempelajari kembali 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 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. Mungkin ada biaya di masa depan (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>
, 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