文章目錄
- 一、簡介
- 二、支持平臺及語言
- 三、作業原理
- 四、安裝
- 4.1 環境要求
- 4.2 安裝adb
- 4.3 安裝uiautomator2
- 4.4 設備安裝atx-agent
- 4.5 安裝weditor
- 4.6 應用及操作
- 4.6.1 呼叫uiautomator2的程序
- 4.6.2 設備連接方法
- 4.6.3 檢查并維持設備端守護行程處于運行狀態
- 4.6.4 打開除錯開關
- 4.6.5 安裝應用
- 4.6.6 啟動應用
- 4.6.7 停止應用
- 4.6.8 停止所有正在運行的應用程式
- 4.6.9 跳過彈窗,禁止彈窗
- 4.6.10 Session
- 4.6.11 獲取設備資訊
- 4.6.12 獲取應用資訊
- 4.6.13 推拉檔案
- 4.6.14 關鍵事件
- 4.6.15 手勢與設備的互動
- 4.6.16 螢屏相關的
- 4.6.17 選擇Ui物件
- 4.6.18 UI物件的六種定位方式
- 4.6.19 Toast
- 4.6.20 XPath
- 五、Google uiautomator與uiautomator2的區別
一、簡介
??uiautomator2是一個python庫,用于Android的UI自動化測驗,其底層基于Google uiautomator,Google提供的uiautomator庫可以獲取螢屏上任意一個APP的任意一個控制元件屬性,并對其進行任意操作,
GitHub地址:https://github.com/openatx/uiautomator2
二、支持平臺及語言
??python-uiautomator2封裝了谷歌自帶的uiautomator2測驗框架,提供便利的python介面,他允許測驗人員直接在PC上撰寫Python的測驗代碼,操作手機應用,完成自動化,大大提高了自動化代碼撰寫的效率,
三、作業原理

如圖所示,python-uiautomator2主要分為兩個部分,python客戶端,移動設備
- python端:運行腳本,并向移動設備發送HTTP請求
- 移動設備:移動設備上運行了封裝了uiautomator2的HTTP服務,決議收到的請求,并轉化成uiautomator2的代碼,
整個程序
- 在移動設備上安裝atx-agent(守護行程),隨后atx-agent啟動uiautomator2服務(默認7912埠)進行監聽
- 在PC上撰寫測驗腳本并執行(相當于發送HTTP請求到移動設備的server端)
- 移動設備通過WIFI或USB接收到PC上發來的HTTP請求,執行制定的操作
四、安裝
4.1 環境要求
- python 3.6+
- android 4.4+
4.2 安裝adb
??如命令列可以執行adb devices,則跳過此步驟
??從谷歌官網下載Android Platform Tools, 解壓,并加包含adb.exe的目錄加入到系統的PATH中,
4.3 安裝uiautomator2
pip install --pre uiautomator2
pip install pillow (如果需要截圖,可安裝pillow)
4.4 設備安裝atx-agent
首先設備連接到PC,并能夠adb devices發現該設備,
執行下面的命令會自動安裝本庫所需要的設備端程式:uiautomator-server,atx-agent,openstf / minicap,openstf / minitouch
# init就是所有USB連接電腦的手機上都安裝uiautomator2
python -m uiautomator2 init
# 指定手機安裝uiautomator2, 用 --mirror
python -m uiautomator2 init --mirror --serial $SERIAL
# 嫌棄慢的話,可以用國內的鏡像
python -m uiautomator2 init --mirror
最后提示success,代表atx-agent初始化成功,
4.5 安裝weditor
有了這個,方便我們快速的識別手機上的元素,方便寫代碼
pip install -U weditor #pip install --pre weditor
安裝好之后,就可以在命令列運行 weditor --help 確認是否安裝成功了,
Windows系統可以使用命令在桌面創建一個快捷方式:
python -m weditor –shortcut
# 或weditor --shortcut
在windows cmd中執行上述命令后,會在桌面上創建一個快捷方式,如下圖:

啟動方法:
- 方法1:命令列直接輸入 weditor 會自動打開瀏覽器,輸入設備的ip或者序列號,點擊Connect即可
- 方法2:桌面上雙擊WEditor快捷方式即可
- 方法3:命令列中執行 python -m weditor
啟動后瀏覽器會自動打開一個網頁,如下圖:

重點說下這個部分

??如何與手機連接起來呢?就是通過手機序列號或IP來實作的,打開cmd,輸入adb devices,查看手機序列號,輸入框中填寫設備的IP或者設備的Serial(序列號),之后點擊Connect,如果一切正常就會出現一個綠色的葉子

??點擊藍色的Reload,就可以在網頁上看到手機的界面了,非常的強大,我們可以看到手機元素的各種定位方法,在UI自動化寫代碼的時候,真的是非常的方面,手機頁面和weditor是實時同步的,
【推薦】AppetizerIO 所見即所得腳本編輯器
AppetizerIO 提供了對uiautomator2的深度集成,可以圖形化管理ATX設備,還有所見即所得腳本編輯器
- 到網站下載直接打開,首次使用需要注冊賬號
- 設備管理 界面里可以檢查設備是否正常init,起停atx-agent,抓取atx-agent.log檔案
- APP測驗->腳本助手調出腳本助手,實時界面同步,點擊界面直接插入各種代碼,同時支持uiautomator和Appium
4.6 應用及操作
4.6.1 呼叫uiautomator2的程序
- 配置手機設備引數,設定具體操作的是哪一臺手機
- 抓取手機上應用的控制元件,制定對應的控制元件來進行操作
- 對抓取到的控制元件進行操作,比如點擊、填寫引數等
4.6.2 設備連接方法
??python-uiautomator2連接手機的方式有兩種,一種是通過WIFI,另外一種是通過USB,兩種方法各有優缺點,
??WIFI最便利的地方就是可以不用連接資料線,USB則可以用在PC和手機網路不在一個網段用不了的情況,
(1)通過WiFi,假設設備IP 192.168.0.1和您的PC在同一網路中
import uiautomator2 as u2
d = u2.connect('192.168.0.1') # WIFI鏈接設備,或者u2.connect_wifi('10.0.0.1')
(2)通過USB, 假設設備序列是123456789F(見adb devices)
import uiautomator2 as u2
d = u2.connect('123456789F') # USB鏈接設備,或者u2.connect_usb('123456f')
#d = u2.connect_usb() #當前只有一個設備時可以用這個
??在沒有引數的情況下呼叫u2.connect(), uiautomator2將從環境變數ANDROID_DEVICE_IP獲取設備IP,如果這個環境變數是空的,uiautomator將回傳connect_usb,您需要確保只有一個設備連接到計算機,
4.6.3 檢查并維持設備端守護行程處于運行狀態
d.healthcheck()
4.6.4 打開除錯開關
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}}
'''
4.6.5 安裝應用
只能從URL安裝
d.app_install('http://some-domain.com/some.apk') #引號內為下載apk地址
4.6.6 啟動應用
d.app_start('包名', '界面名')
4.6.7 停止應用
#相當于'am force-stop'強制停止應用
d.app_stop('com.example.hello_world') #引號內為包名稱
#相當于'pm clear' 清空App資料
d.app_clear('com.example.hello_world')
4.6.8 停止所有正在運行的應用程式
# 停止所有
d.app_stop_all()
# 停止所有應用程式,除了com.examples.demo
d.app_stop_all(excludes=['com.examples.demo'])
4.6.9 跳過彈窗,禁止彈窗
d.disable_popups() #自動跳過彈出視窗
d.disable_popups(False) #禁用自動跳過彈出視窗
4.6.10 Session
Session表示應用程式的生命周期,可用于啟動應用,檢測應用崩潰
- 啟動和關閉應用程式
sess = d.session("com.netease.cloudmusic") # start 網易云音樂
sess.close() # 停止網易云音樂
- 使用python with啟動和關閉應用程式
with d.session("com.netease.cloudmusic") as sess:
sess(text="Play").click()
- 鏈接正在運行的應用
sess = d.session("com.netease.cloudmusic",attach = True)
- 檢測應用崩潰
# App正在運行時
sess(text="Music").click() # 操作是正常的
# App崩潰時
sess(text="Music").click() # 引發會話中斷錯誤SessionBrokenError
# session下的其他函式呼叫也會引發SessionBrokenError錯誤
# 檢查會話是否正確,
# 警告:函式名將來可能會更改
sess.running() # True or False
4.6.11 獲取設備資訊
- 獲取基本資訊
d.info
# 以下是可能輸出結果:
{
u'displayRotation': 0,
u'displaySizeDpY': 640,
u'displaySizeDpX': 360,
u'currentPackageName': u'com.android.launcher',
u'productName': u'takju',
u'displayWidth': 720,
u'sdkInt': 18,
u'displayHeight': 1184,
u'naturalOrientation': True
}
- 獲取視窗大小
d.window_size()
# 設備垂直輸出示例: (1080, 1920)
# 設備水平輸出示例: (1920, 1080)
- 獲取當前應用程式資訊,對于某些android設備,輸出可以為空(參見輸出示例3)
d.current_app()
# 輸出示例 1: {'package': 'com.netease.example', 'activity': '.Client', 'pid': 23710}
# 輸出示例 2: {'package': 'com.ruguoapp.jike', 'activity': 'com.ruguoapp.jike.business.video.ui.activity.videolist.VideoListActivity'}
# 輸出示例 3: {'package': None, 'activity': None}
- 獲取設備序列號
d.serial
# 輸出示例: 74aAEDR428Z9
- 獲取WIFI IP
print(d.wlan_ip)
#輸出示例:10.0.0.1
- 獲取詳細的設備資訊
print(d.device_info)
# 輸出示例:
{'udid': '3578298f-b4:0b:44:e6:1f:90-OD103',
'version': '7.1.1',
'serial': '3578298f',
'brand': 'SMARTISAN',
'model': 'OD103',
'hwaddr': 'b4:0b:44:e6:1f:90',
'port': 7912,
'sdk': 25,
'agentVersion': 'dev',
'display': {'width': 1080, 'height': 1920},
'battery': {'acPowered': False,
'usbPowered': False,
'wirelessPowered': False,
'status': 3,
'health': 0,
'present': True,
'level': 99,
'scale': 100,
'voltage': 4316,
'temperature': 272,
'technology': 'Li-ion'},
'memory': {'total': 3690280, 'around': '4 GB'},
'cpu': {'cores': 8, 'hardware': 'Qualcomm Technologies, Inc MSM8953Pro'},
'presenceChangedAt': '0001-01-01T00:00:00Z',
'usingBeganAt': '0001-01-01T00:00:00Z'}
4.6.12 獲取應用資訊
d.app_info("com.examples.demo")
# 會輸出
#{
# "mainActivity": "com.github.uiautomator.MainActivity",
# "label": "ATX",
# "versionName": "1.1.7",
# "versionCode": 1001007,
# "size":1760809
#}
# 保存應用程式圖示
img = d.app_icon("com.examples.demo")
img.save("icon.png")
4.6.13 推拉檔案
- 將檔案推送到設備
# push檔案夾
d.push("foo.txt", "/sdcard/")
# push和重命名
d.push("foo.txt", "/sdcard/bar.txt")
# push fileobj
with open("foo.txt", 'rb') as f:
d.push(f, "/sdcard/")
# 推動和更改檔案訪問模式
d.push("foo.sh", "/data/local/tmp/", mode=0o755)
- 從設備中拉出一個檔案
d.pull("/sdcard/tmp.txt", "tmp.txt")
# 如果在設備上找不到檔案,FileNotFoundError將引發
d.pull("/sdcard/some-file-not-exists.txt", "tmp.txt")
4.6.14 關鍵事件
- 打開/關閉螢屏
d.screen_on() #打開螢屏
d.screen_off() #關閉螢屏
- 獲取當前螢屏狀態
d.info.get(' screenOn ') #需要 Android >= 4.4
- 硬鍵盤和軟鍵盤操作
d.press("home") # 點擊home鍵
d.press("back") # 點擊back鍵
d.press("left") # 點擊左鍵
d.press("right") # 點擊右鍵
d.press("up") # 點擊上鍵
d.press("down") # 點擊下鍵
d.press("center") # 點擊選中
d.press("menu") # 點擊menu按鍵
d.press("search") # 點擊搜索按鍵
d.press("enter") # 點擊enter鍵
d.press("delete") # 點擊洗掉按鍵
d.press("recent") # 點擊近期活動按鍵
d.press("volume_up") # 音量+
d.press("volume_down") # 音量-
d.press("volume_mute") # 靜音
d.press("camera") # 相機
d.press("power") #電源鍵
- 解鎖螢屏
d.unlock()
# 相當于
# 1. 發射活動:com.github.uiautomator.ACTION_IDENTIFY
# 2. 按home鍵
4.6.15 手勢與設備的互動
# 1、單擊螢屏
d.click(x,y) # x,y為點擊坐標
# 2、雙擊螢屏
d.double_click(x,y)
d.double_click(x,y,0.1) # 默認兩個單擊之間間隔時間為0.1秒
# 3、長按
d.long_click(x,y)
d.long_click(x,y,0.5) # 長按0.5秒(默認)
# 4、滑動
d.swipe(sx, sy, ex, ey)
d.swipe(sx, sy, ex, ey, 0.5) #滑動0.5s(default)
# 5、拖動
d.drag(sx, sy, ex, ey)
d.drag(sx, sy, ex, ey, 0.5)#拖動0.5s(default)
# 6、滑動點 多用于九宮格解鎖,提前獲取到每個點的相對坐標(這里支持百分比)
# 從點(x0, y0)滑到點(x1, y1)再滑到點(x2, y2)
# 兩點之間的滑動速度是0.2秒
d.swipe((x0, y0), (x1, y1), (x2, y2), 0.2)
# 注意:單擊,滑動,拖動操作支持百分比位置值,例:
d.long_click(0.5, 0.5) # 表示長按螢屏中心
4.6.16 螢屏相關的
# 1、檢索方向
d.orientation
# 檢索方向,輸出可以是 "natural" or "left" or "right" or "upsidedown"
# 2、設定方向
d.set_orientation('l') # or "left"
d.set_orientation("l") # or "left"
d.set_orientation("r") # or "right"
d.set_orientation("n") # or "natural"
# 3、凍結/開啟旋轉
d.freeze_rotation()# 凍結旋轉
d.freeze_rotation(False)# 開啟旋轉
# 4、轉儲UI層次結構
# get the UI hierarchy dump content (unicoded).(獲取UI層次結構轉儲內容)
d.dump_hierarchy()
# 5、打開通知或快速設定
d.open_notification()#下拉打開通知欄
d.open_quick_settings()#下拉打開快速設定欄
- 截圖
# 截圖并保存到電腦上的一個檔案中,需要Android>=4.2,
d.screenshot("home.jpg")
# 得到PIL.Image格式的影像. 但你必須先安裝pillow
image = d.screenshot() # default format="pillow"
image.save("home.jpg") # 或'home.png',目前只支持png 和 jpg格式的影像
# 得到OpenCV的格式影像,當然,你需要numpy和cv2安裝第一個
import cv2
image = d.screenshot(format='opencv')
cv2.imwrite('home.jpg', image)
# 獲取原始JPEG資料
imagebin = d.screenshot(format='raw')
open("some.jpg", "wb").write(imagebin)
4.6.17 選擇Ui物件
選擇器是一種方便的機制,用于在當前視窗中標識特定的UI物件,
#選擇帶有文本'Clock'的物件,它的類名是'android.widget.TextView'
d(text='Clock', className='android.widget.TextView')
選擇器支持以下引數,有關詳細資訊,請參閱 UiSelector Java doc for detailed information
- 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
# 1、檢查特定的UI物件是否存在
d(text="Settings").exists # 回傳布林值,如果存在則為True,否則為False
d.exists(text="Settings") # 另一種寫法
# 高級用法
d(text="Settings").exists(timeout=3) # 等待'Settings'在3秒鐘出現
# 2、獲取特定UI物件的資訊
d(text="Settings").info
# 3、獲取/設定/清除可編輯欄位的文本(例如EditText小部件)
d(text="Settings").get_text() #得到文本小部件
d(text="Settings").set_text("My text...") #設定文本
d(text="Settings").clear_text() #清除文本
# 獲取Widget中心點
d(text="Settings").center()
#d(text="Settings").center(offset=(0, 0)) # 基準位置左前
4.6.18 UI物件的六種定位方式
UI物件有六種定位方式,text、resourceId、description、className、xpath、坐標
- 執行單擊UI物件
# 1、text定位單擊
d(text="Settings").click()
d(text="Settings", className="android.widget.TextView").click()
# 2、resourceId定位單擊
d(resourceId="com.ruguoapp.jike:id/tv_title", className="android.widget.TextView").click()
# 3、description定位單擊
d(description="設定").click()
d(description="設定", className="android.widget.TextView").click()
# 4、className定位單擊
d(className="android.widget.TextView").click()
# 5、xpath定位單擊
d.xpath("//android.widget.FrameLayout[@index='0']/android.widget.LinearLayout[@index='0']").click()
# 6、坐標單擊
d.click(182, 1264)
# 等待元素出現(最多10秒),出現后單擊
d(text="Settings").click(timeout=10)
# 在10秒時點擊,默認的超時0
d(text='Skip').click_exists(timeout=10.0)
# 單擊直到元素消失,回傳布爾
d(text="Skip").click_gone(maxretry=10, interval=1.0) # maxretry默認值10,interval默認值1.0
# 點擊基準位置偏移
d(text="Settings").click(offset=(0.5, 0.5)) # 點擊中心位置,同d(text="Settings").click()
d(text="Settings").click(offset=(0, 0)) # 點擊左前位置
d(text="Settings").click(offset=(1, 1)) # 點擊右下
- 執行雙擊UI物件
d(text="設定").double_click() #雙擊特定ui物件的中心
d.double_click(x, y, 0.1)#兩次單擊之間的默認持續時間為0.1秒
- 執行長按UI物件
# 長按特定UI物件的中心
d(text="Settings").long_click()
d.long_click(x, y, 0.5) # 長按坐標位置0.5s默認
- 將UI物件拖向另一個點或另一個UI物件
# Android<4.3不能使用drag.
# 在0.5秒內將UI物件拖到螢屏點(x, y)
d(text="Settings").drag_to(x, y, duration=0.5)
# 將UI物件拖到另一個UI物件的中心位置,時間為0.25秒
d(text="Settings").drag_to(text="Clock", duration=0.25)
- 特定UI物件的手勢操作
支持兩種手勢:從邊緣到中心、從中心到邊緣
#注意:縮放要到安卓4.3才能設定,
#從邊緣到中心
d(text="Settings").pinch_in(percent=100, steps=10)
# 從中心到邊緣
d(text="Settings").pinch_out()
- 等待,直到特定的UI出現或消失
# 等待ui物件出現
d(text="Settings").wait(timeout=3.0) # 回傳布林值
# 等待ui物件的消失
d(text="Settings").wait_gone(timeout=1.0)
# 默認超時為20秒,有關詳細資訊,請參閱全域設定
4.6.19 Toast
- 展示Toast
d.toast.show("Hello world")
d.toast.show("Hello world", 1.0) # 顯示為1.0,默認為1.0
- 獲取 Toast
# [引數]
# 5.0: 最大等待超時,默認的10.0
# 快取時間10.0s,如果最近10s已經出現toast,則回傳快取toast,默認10.0(將來可能會有變化)
# 如果最終沒有toast,回傳"default message",默認沒有
d.toast.get_message(5.0, 10.0, "default message")
# 常見的使用
assert "Short message" in d.toast.get_message(5.0, default="")
#清楚快取toast
d.toast.reset()
# Now d.toast.get_message(0) is None
4.6.20 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
常見用法:
# 等待10s
d.xpath("//android.widget.TextView").wait(10.0)
# 找到并單擊
d.xpath("//*[@content-desc='分享']").click()
# 檢查是否存在
if d.xpath("//android.widget.TextView[contains(@text, 'Se')]").exists:
print("exists")
# 獲取所有文本視圖文本、屬性和中心點
for elem in d.xpath("//android.widget.TextView").all():
print("Text:", elem.text)
#獲取視圖文本
for elem in d.xpath("//android.widget.TextView").all():
print("Attrib:", elem.attrib)
# 獲取屬性和中心點
# 回傳: (100, 200)
for elem in d.xpath("//android.widget.TextView").all():
print("Position:", elem.center())
# 所有元素
//*
# resource-id包含login字符
//*[contains(@resource-id, 'login')]
# 按鈕包含賬號或帳號
//android.widget.Button[contains(@text, '賬號') or contains(@text, '帳號')]
# 所有ImageView中的第二個
(//android.widget.ImageView)[2]
# 所有ImageView中的最后一個
(//android.widget.ImageView)[last()]
# className包含ImageView
//*[contains(name(), "ImageView")]
五、Google uiautomator與uiautomator2的區別
- API相似但是不完全兼容
- uiautomator2是安卓專案,而uiautomator是Java專案
- uiautomator2可以輸入中文,而uiautomator的Java工程需借助utf7輸入法才能輸入中文
- uiautomator2必須明確EditText框才能向里面輸入文字,uiautomator直接指定父類也可以在子類中輸入文字
- uiautomator2獲取控制元件速度比uiautomator快
文章參考:https://vic.kim/2019/05/20/UIAutomator2%E7%9A%84%E4%BD%BF%E7%94%A8/
轉載請註明出處,本文鏈接:https://www.uj5u.com/qita/384207.html
標籤:其他
