
霍格沃茲測試學院是 python-uiautomator2 金牌贊助商,跟着開源項目作者學測試開發實戰。
一、背景簡介
Google 官方提供了一個 Android 自動化測試工具(Java 庫),基于 Accessibility 服務,功能很強,可以對第三方 App 進行測試,擷取螢幕上任意一個 App 的任意一個控件屬性,并對其進行任意操作,但有兩個缺點:
- 測試腳本隻能使用 Java 語言;
- 測試腳本要打包成 jar 或者 apk 包上傳到裝置上才能運作;
實際工作中,我們希望測試邏輯能夠用 Python 編寫,能夠在電腦上運作的時候就控制手機。是以基于這個目的開發了 python-uiautomator2 自動化測試開源工具,其封裝了谷歌自帶的 uiautomator2 測試架構,可以運作在支援 Python 的任一系統上,目前版本為 V2.10.2。
GitHub 開源位址:
https://github.com/openatx/uiautomator2==二、工作原理==
如圖所示,python-uiautomator2 主要分為兩個部分,python 用戶端,移動裝置
- python 端: 運作腳本,并向移動裝置發送 HTTP 請求;
- 移動裝置:移動裝置上運作了封裝了 uiautomator2 的 HTTP 服務,解析收到的請求,并轉化成 uiautomator2 的代碼;
整個過程:
- 在移動裝置上安裝
(守護程序),随後atx-agent
啟動atx-agent
服務(預設 7912 端口)進行監聽;uiautomator2
- 在 PC 上編寫測試腳本并執行(相當于發送 HTTP 請求到移動裝置的 server 端);
- 移動裝置通過 WIFI 或 USB 接收到 PC 上發來的 HTTP 請求,執行制定的操作;
三、安裝與啟動
3.1 安裝 uiautomator2
使用 pip 安裝
pip install -U uiautomator2
安裝完成後,使用如下 python 代碼檢視環境是事配置成功
說明:後文中所有代碼都需要導入 uiautomator2 庫,為了簡化我使用 u2 代替,d 代表 driver
import uiautomator2 as u2
# 連接配接并啟動
d = u2.connect()
print(d.info)
能正确列印出裝置的資訊則表示安裝成功
注意:需要安裝 adb 工具,并配置到系統環境變量,才能操作手機。
安裝有問題可以到 issue 清單查詢:
https://github.com/openatx/uiautomator2/wiki/Common-issues3.2 安裝 weditor
weditor 是一款基于浏覽器的 UI 檢視器,用來幫助我們檢視 UI 元素定位。
因為 uiautomator 是獨占資源,是以當 atx 運作的時候 uiautomatorviewer 是不能用的,為了減少 atx 頻繁的啟停,就需要用到此工具
pip install -U weditor
檢視安裝是否成功
weditor --help
出現如下資訊表示安裝成功
運作 weditor
python -m weditor
#或者直接在指令行運作
weditor
四、元素定位
4.1 使用方法
d(定位方式 = 定位值)
#例:
element = d(text='Phone')
#這裡傳回的是一個清單,當沒找到元素時,不會報錯,隻會傳回一個長度為 0 的清單
#當找到多個元素時,會傳回多個元素的清單,需要加下标再定位
element[0].click()
#擷取元素個數
print(element.count)
4.2 支援的定位方式
ui2 支援 android 中 UiSelector 類中的所有定位方式,詳細可以在這個網址檢視
https://developer.android.com/reference/android/support/test/uiautomator/UiSelector整體内容如下 , 所有的屬性可以通過 weditor 檢視到
名稱 | 描述 |
---|---|
text | text 是指定文本的元素 |
textContains | text 中包含有指定文本的元素 |
textMatches | text 符合指定正則的元素 |
textStartsWith | text 以指定文本開頭的元素 |
className | className 是指定類名的元素 |
classNameMatches | className 類名符合指定正則的元素 |
description | description 是指定文本的元素 |
descriptionContains | description 中包含有指定文本的元素 |
descriptionMatches | description 符合指定正則的元素 |
descriptionStartsWith | description 以指定文本開頭的元素 |
checkable | 可檢查的元素,參數為 True,False |
checked | 已選中的元素,通常用于複選框,參數為 True,False |
clickable | 可點選的元素,參數為 True,False |
longClickable | 可長按的元素,參數為 True,False |
scrollable | 可滾動的元素,參數為 True,False |
enabled | 已激活的元素,參數為 True,False |
focusable | 可聚焦的元素,參數為 True,False |
focused | 獲得了焦點的元素,參數為 True,False |
selected | 目前選中的元素,參數為 True,False |
packageName | packageName 為指定包名的元素 |
packageNameMatches | packageName 為符合正則的元素 |
resourceId | resourceId 為指定内容的元素 |
resourceIdMatches | resourceId 為符合指定正則的元素 |
4.3 子元素和兄弟定位
子元素定位
child()
#查找類名為 android.widget.ListView 下的 Bluetooth 元素
d(className="android.widget.ListView").child(text="Bluetooth")
# 下面這兩種方式定位有點不準确,不建議使用
d(className="android.widget.ListView")\
.child_by_text("Bluetooth",allow_scroll_search=True)
d(className="android.widget.ListView").child_by_description("Bluetooth")
兄弟元素定位
sibling()
#查找與 google 同一級别,類名為 android.widget.ImageView 的元素
d(text="Google").sibling(className="android.widget.ImageView")
鍊式調用
d(className="android.widget.ListView", resourceId="android:id/list") \
.child_by_text("Wi‑Fi", className="android.widget.LinearLayout") \
.child(className="android.widget.Switch") \
.click()
4.4 相對定位
相對定位支援在
left
,
right
top
bottom
, 即在某個元素的前後左右
d(A).left(B),# 選擇 A 左邊的 B
d(A).right(B),# 選擇 A 右邊的 B
d(A).up(B), #選擇 A 上邊的 B
d(A).down(B),# 選擇 A 下邊的 B
#選擇 WIFI 右邊的開關按鈕
d(text='Wi‑Fi').right(resourceId='android:id/widget_frame')
4.5 元素常用 API
表格标注有 @property 裝飾的類屬性方法,均為下方示例方式
d(test="Settings").exists
方法 | 傳回值 | 備注 | |
---|---|---|---|
exists() | 判斷元素是否存在 | True,Flase | @property |
info() | 傳回元素的所有資訊 | 字典 | |
get_text() | 傳回元素文本 字元串 | ||
set_text(text) | 設定元素文本 None | ||
clear_text() | 清空元素文本 None | ||
center() | 傳回元素的中心點位置 | (x,y) | 基于整個螢幕的點 |
exists 其它使用方法:
d.exists(text='Wi‑Fi',timeout=5)
info() 輸出資訊:
{
"bounds": {
"bottom": 407,
"left": 216,
"right": 323,
"top": 342
},
"childCount": 0,
"className": "android.widget.TextView",
"contentDescription": null,
"packageName": "com.android.settings",
"resourceName": "android:id/title",
"text": "Wi‑Fi",
"visibleBounds": {
"bottom": 407,
"left": 216,
"right": 323,
"top": 342
},
"checkable": false,
"checked": false,
"clickable": false,
"enabled": true,
"focusable": false,
"focused": false,
"longClickable": false,
"scrollable": false,
"selected": false
}
可以通過上方資訊分别擷取元素的所有屬性
4.6 XPATH 定位
因為 Java uiautoamtor 中預設是不支援 xpath,這是屬于 ui2 的擴充功能,速度會相比其它定位方式慢一些
在 xpath 定位中,ui2 中的 description 定位需要替換為 content-desc,resourceId 需要替換為 resource-id
使用方法
# 隻會傳回一個元素,如果找不到元素,則會報 XPathElementNotFoundError 錯誤
# 如果找到多個元素,預設會傳回第 0 個
d.xpath('//*[@resource-id="com.android.launcher3:id/icon"]')
# 如果傳回的元素有多個,需要使用 all() 方法傳回清單
# 使用 all 方法,當未找到元素時,不會報錯,會傳回一個空清單
d.xpath('//*[@resource-id="com.android.launcher3:id/icon"]').all()
五、裝置互動
5.1 單擊
d(text='Settings').click()
#單擊直到元素消失 , 逾時時間 10,點選間隔 1
d(text='Settings').click_gone(maxretry=10, interval=1.0)
5.2 長按
d(text='Settings').long_click()
5.3 拖動
Android<4.3 時不能使用拖動
# 在 0.25S 内将 Setting 拖動至 Clock 上,拖動元素的中心位置
# duration 預設為 0.5, 實際拖動的時間會比設定的要高
d(text="Settings").drag_to(text="Clock", duration=0.25)
# 拖動 settings 到螢幕的某個點上
d(text="Settings").drag_to(877,733, duration=0.25)
#兩個點之間的拖動 , 從點 1 拖動至點 2
d.drag(x1,y1,x2,y2)
5.4 滑動
滑動有兩個,一個是在 driver 上操作,一個是在元素上操作
元素上操作
從元素的中心向元素邊緣滑動
# 在 Setings 上向上滑動。steps 預設為 10
# 1 步約為 5 毫秒,是以 20 步約為 0.1 s
d(text="Settings").swipe("up", steps=20)
driver 上操作
即對整個螢幕操作
# 實作下滑操作
x,y = d.window_size()
x1 = x / 2
y1 = y * 0.1
y2 = y * 0.9
d.swipe(x1,y1,x1,y2)
driver 滑動的擴充方法,可以直接實作滑動,不需要再自己封裝定位點
# 支援前後左右的滑動
# "left", "right", "up", "down"
# 下滑操作
d.swipe_ext("down")
5.5 雙指操作
android>4.3
對元素操作
d(text='Settings').gesture(start1,start2,end1,end2,)
# 放大操作
d(text='Settings').gesture((525,960),(613,1121),(135,622),(882,1540))
封裝好的放大縮小操作
# 縮小
d(text="Settings").pinch_in()
# 放大
d(text="Settings").pinch_out()
5.6 等待元素出現或者消失
# 等待元素出現
d(text="Settings").wait(timeout=3.0)
# 等待元素消失,傳回 True False,timout 預設為全局設定的等待時間
d(text='Settings').wait_gone(timeout=20)
5.7 滾動界面
設定 scrollable 屬性為 True;
滾動類型:horiz 為水準,vert 為垂直;
滾動方向:
- forward 向前
- backward 向後
- toBeginning 滾動至開始
- toEnd 滾動至最後
- to 滾動直接某個元素出現
所有方法均傳回 Bool 值;
# 垂直滾動到頁面頂部 / 橫向滾動到最左側
d(scrollable=True).scroll.toBeginning()
d(scrollable=True).scroll.horiz.toBeginning()
# 垂直滾動到頁面最底部 / 橫向滾動到最右側
d(scrollable=True).scroll.toEnd()
d(scrollable=True).scroll.horiz.toEnd()
# 垂直向後滾動到指定位置 / 橫向向右滾動到指定位置
d(scrollable=True).scroll.to(description=" 指定位置 ")
d(scrollable=True).scroll.horiz.to(description=" 指定位置 ")
# 垂直向前滾動(橫向同理)
d(scrollable=True).scroll.forward()
# 垂直向前滾動到指定位置(橫向同理)
d(scrollable=True).scroll.forward.to(description=" 指定位置 ")
# 滾動直到 System 元素出現
d(scrollable=True).scroll.to(text="System")
Take screenshot of widget
im = d(text="Settings").screenshot()
im.save("settings.jpg")
5.8 輸入
5.8.1 輸入自定義文本
# 使用 adb 廣播的方式輸入
d.send_keys('hello')
# 清空輸入框
d.clear_text()
5.8.2 輸入按鍵
兩種方法
# 發送回車
d.press('enter')
# 第二種
d.keyevent('enter')
目前 press 支援的按鍵如下
"""
press key via name or key code. Supported key name includes:
home, back, left, right, up, down, center, menu, search, enter,
delete(or del), recent(recent apps), volume_up, volume_down,
volume_mute, camera, power.
"""
keyevent 是通過 “adb shell input keyevent” 方式輸入,支援按鍵更加豐富
更多詳細的按鍵資訊
https://developer.android.com/reference/android/view/KeyEvent.html5.8.3 輸入法切換
# 切換成 ui2 的輸入法,這裡會隐藏掉系統原本的輸入法 , 預設是使用系統輸入法
# 當傳入 False 時會使用系統預設輸入法,預設為 Fasle
d.set_fastinput_ime(True)
# 檢視目前輸入法
d.current_ime()
#傳回值
('com.github.uiautomator/.FastInputIME', True)
5.8.4 模拟輸入法功能
可以模拟的功能有 go ,search ,send ,next, done ,previous。
如果使用 press 輸入按鍵無效,可以嘗試使用此方法輸入
# 搜尋功能
d.send_action("search")
5.9 toast 操作
# 擷取 toast, 當沒有找到 toast 消息時,傳回 default 内容
d.toast.get_message(timout=5,default='no toast')
# 清空 toast 緩存
d.toast.reset()
5.10 監控界面
使用 wather 進行界面的監控,可以用來實作跳過測試過程中的彈框
當啟動 wather 時,會建立一個線程進行監控
可以添加多個 watcher
用法
# 注冊監控 , 當界面内出現有 allow 字樣時,點選 allow
d.watcher.when('allow').click()
# 移除 allow 的監控
d.watcher.remove("allow")
# 移除所有的監控
d.watcher.remove()
# 開始背景監控
d.watcher.start()
d.watcher.start(2.0) # 預設監控間隔 2.0s
# 強制運作所有監控
d.watcher.run()
# 停止監控
d.watcher.stop()
# 停止并移除所有的監控,常用于初始化
d.watcher.reset()
2.11.0 版本 新增了一個 watch_context 方法 , 寫法相比 watcher 更簡潔,官方推薦使用此方法來實作監控,目前隻支援 click() 這一種方法。
wct = d.watch_context()
# 監控 ALLOW
wct.when("ALLOW").click()
# 監控 OK
wct.when('OK').click()
# 開啟彈窗監控,并等待界面穩定(兩個彈窗檢查周期内沒有彈窗代表穩定)
wct.wait_stable()
#其它實作代碼
# 停止監控
wct.stop()
5.11 多點滑動
這裡可以用來實作圖案解鎖
使用 touch 類
# 模拟按下不放手
touch.down(x,y)
# 停住 3S
touch.sleep(x,y)
# 模拟移動
touch.move(x,y)
# 模拟放開
touch.up(x,y)
#實作長按 , 同一個點按下休眠 5S 後擡起
d.touch.down(252,1151).sleep(5).up(252,1151)
# 實作四點的圖案解鎖,目前隻支援坐标點
d.touch.down(252,1151).move(559,1431).move(804,1674).move(558,1666).up(558,1666)
六、圖像操作
6.1 截圖
d.screenshot('test.png')
6.2 錄制視訊
這個感覺是比較有用的一個功能,可以在測試用例開始時錄制,結束時停止錄制,然後如果測試 fail。則上傳到測試報告,完美複原操作現場,具體原理後面再去研究。
首先需要下載下傳依賴,官方推薦使用鏡像下載下傳:
pip3 install -U "uiautomator2[image]" -i https://pypi.doubanio.com/simple
執行錄制:
# 啟動錄制,預設幀率為 20
d.screenrecord('test.mp4')
# 其它操作
time.sleep(10)
#停止錄制,隻有停止錄制了才能看到視訊
d.screenrecord.stop()
6.3 圖檔識别點選
下載下傳與錄制視訊同一套依賴。
這個功能是首先手動截取需要點選目标的圖檔,然後 ui2 在界面中去比對這個圖檔,目前我嘗試了精确試不是很高,誤點率非常高,不建議使用。
# 點選
d.image.click('test.png')
# 比對圖檔,傳回相似度和坐标
# {'similarity': 0.9314796328544617, 'point': [99, 630]}
d.image.match('test.png')
七、應用管理
7.1 擷取目前界面的 APP 資訊
d.app_current()
#傳回目前界面的包名,activity 及 pid
{
"package": "com.xueqiu.android",
"activity": ".common.MainActivity",
"pid": 23007
}
7.2 安裝應用
可以從本地路徑及 url 下載下傳安裝 APP,此方法無傳回值,當安裝失敗時,會抛出 RuntimeError 異常
# 本地路徑安裝
d.app_install('test.apk')
# url 安裝
d.app_install('http://s.toutiao.com/UsMYE/')
7.3 運作應用
預設當應用在運作狀态執行 start 時不會關閉應用,而是繼續保持目前界面。
如果需要消除前面的啟動狀态,則需要加 stop=True 參數。
# 通過包名啟動
d.app_start("com.xueqiu.android",stop=True)
#源碼說明
def app_start(self, package_name: str,
activity: Optional[str]=None,
wait: bool = False,
stop: bool=False,
use_monkey: bool=False):
""" Launch application
Args:
package_name (str): package name
activity (str): app activity
stop (bool): Stop app before starting the activity. (require activity)
use_monkey (bool): use monkey command to start app when activity is not given
wait (bool): wait until app started. default False
"""
7.4 停止應用
stop 和 clear 的差別是結束應用使用的指令不同
stop 使用的是 “am force-stop”
clear 使用的是 “pm clear”
# 通過包名結束單個應用
d.app_stop("com.xueqiu.android")
d.app_clear('com.xueqiu.android')
# 結束所有應用 , 除了 excludes 參數清單中的應用包名
# 如果不傳參,則會隻保留兩個依賴服務應用
# 會傳回一個結束應用的包名清單
d.app_stop_all(excludes=['com.xueqiu.android'])
7.5 擷取應用資訊
d.app_info('com.xueqiu.android')
#輸出
{
"packageName": "com.xueqiu.android",
"mainActivity": "com.xueqiu.android.common.splash.SplashActivity",
"label": " 雪球 ",
"versionName": "12.6.1",
"versionCode": 257,
"size": 72597243
}
7.6 擷取應用圖示
img = d.app_icon('com.xueqiu.android')
img.save('icon.png')
7.7 等待應用啟動
# 等待此應用變為目前應用,傳回 pid,逾時未啟動成功則傳回 0
# front 為 true 表示等待 app 成為目前 app,
# 預設為 false,表示隻要背景有這個應用的程序就會傳回 PID
d.app_wait('com.xueqiu.android',60,front=True)
7.8 解除安裝應用
# 解除安裝成功傳回 true, 沒有此包或者解除安裝失敗傳回 False
d.app_uninstall('com.xueqiu.android')
# 解除安裝所有自己安裝的第三方應用 , 傳回解除安裝 app 的包名清單
# excludes 表示不解除安裝的清單
# verbose 為 true 則會列印解除安裝資訊
d.app_uninstall_all(excludes=[],verbose=True)
解除安裝全部應用傳回的包名清單并一定是解除安裝成功了,最好使用 verbose=true 列印一下資訊,這樣可以檢視到是否解除安裝成功
uninstalling com.xueqiu.android OK
uninstalling com.android.cts.verifier FAIL
或者可以修改一下源碼,使其隻輸出成功的包名,注釋的為增加的代碼,未注釋的是源碼
def app_uninstall_all(self, excludes=[], verbose=False):
""" Uninstall all apps """
our_apps = ['com.github.uiautomator', 'com.github.uiautomator.test']
output, _ = self.shell(['pm', 'list', 'packages', '-3'])
pkgs = re.findall(r'package:([^\s]+)', output)
pkgs = set(pkgs).difference(our_apps + excludes)
pkgs = list(pkgs)
# 增加一個解除安裝成功的清單
#sucess_list = []
for pkg_name in pkgs:
if verbose:
print("uninstalling", pkg_name, " ", end="", flush=True)
ok = self.app_uninstall(pkg_name)
if verbose:
print("OK" if ok else "FAIL")
# 增加如下語句,當成功則将包名加入 list
#if ok:
# sucess_list.append(pkg_name)
# 傳回成功的清單
# return sucess_list
return pkgs
八、其它實用方法
8.1 連接配接裝置
#當 PC 隻連接配接了一個裝置時,可以使用此種方式
d = u2.connect()
#傳回的是 Device 類 , 此類繼承方式如下
class Device(_Device, _AppMixIn, _PluginMixIn, _InputMethodMixIn, _DeprecatedMixIn):
""" Device object """
# for compatible with old code
Session = Device
connect() 可以使用如下其它方式進行連接配接
#當 PC 與裝置在同一網段時,可以使用 IP 位址和端口号通過 WIFI 連接配接,無需連接配接 USB 線
connect("10.0.0.1:7912")
connect("10.0.0.1") # use default 7912 port
connect("http://10.0.0.1")
connect("http://10.0.0.1:7912")
#多個裝置時,使用裝置号指定哪一個裝置
connect("cff1123ea") # adb device serial number
8.2 擷取裝置及 driver 資訊
8.2.1 擷取 driver 資訊
d.info
#輸出
{
"currentPackageName": "com.android.systemui",
"displayHeight": 2097,
"displayRotation": 0,
"displaySizeDpX": 360,
"displaySizeDpY": 780,
"displayWidth": 1080,
"productName": "freedom_turbo_XL",
"screenOn": true,
"sdkInt": 29,
"naturalOrientation": true
}
8.2.2 擷取裝置資訊
會輸出測試裝置的所有資訊,包括電池,CPU,記憶體等
d.device_info
#輸出
{
"udid": "61c90e6a-ba:1b:ba:46:91:0e-freedom_turbo_XL",
"version": "10",
"serial": "61c90e6a",
"brand": "Schok",
"model": "freedom turbo XL",
"hwaddr": "ba:1b:ba:46:91:0e",
"port": 7912,
"sdk": 29,
"agentVersion": "0.9.4",
"display": {
"width": 1080,
"height": 2340
},
"battery": {
"acPowered": false,
"usbPowered": true,
"wirelessPowered": false,
"status": 2,
"health": 2,
"present": true,
"level": 98,
"scale": 100,
"voltage": 4400,
"temperature": 292,
"technology": "Li-ion"
},
"memory": {
"total": 5795832,
"around": "6 GB"
},
"cpu": {
"cores": 8,
"hardware": "Qualcomm Technologies, Inc SDM665"
},
"arch": "",
"owner": null,
"presenceChangedAt": "0001-01-01T00:00:00Z",
"usingBeganAt": "0001-01-01T00:00:00Z",
"product": null,
"provider": null
}
8.2.3 擷取螢幕分辨率
# 傳回(寬,高)元組
d.window_size()
# 例 分辨率為 1080*1920
# 手機豎屏狀态傳回 (1080,1920)
# 橫屏狀态傳回 (1920,1080)
8.2.4 擷取 IP 位址
# 傳回 ip 位址字元串,如果沒有則傳回 None
d.wlan_ip
8.3 driver 全局設定
8.3.1 使用 settings 設定
檢視 settings 預設設定
d.settings
#輸出
{
#點選後的延遲,(0,3)表示元素點選前等待 0 秒,點選後等待 3S 再執行後續操作
'operation_delay': (0, 3),
# opretion_delay 生效的方法,預設為 click 和 swipe
# 可以增加 press,send_keys,long_click 等方式
'operation_delay_methods': ['click', 'swipe'],
# 預設等待時間,相當于 appium 的隐式等待
'wait_timeout': 20.0,
# xpath 日志
'xpath_debug': False
}
修改預設設定,隻需要修改 settings 字典即可
#修改延遲為操作前延遲 2S 操作後延遲 4.5S
d.settings['operation_delay'] = (2,4.5)
#修改延遲生效方法
d.settings['operation_delay_methods'] = {'click','press','send_keys'}
# 修改預設等待
d.settings['wait_timeout'] = 10
8.3.2 使用方法或者屬性設定
- http 預設請求逾時時間
# 預設值 60s,
d.HTTP_TIMEOUT = 60
- 當裝置掉線時,等待裝置線上時長
# 僅當 TMQ=true 時有效,支援通過環境變量 WAIT_FOR_DEVICE_TIMEOUT 設定
d.WAIT_FOR_DEVICE_TIMEOUT = 70
- 元素查找預設等待時間
# 打不到元素時,等待 10 後再報異常
d.implicitly_wait(10.0)
- 打開 HTTP debug 資訊
d.debug = True
d.info
#輸出
15:52:04.736 $ curl -X POST -d '{"jsonrpc": "2.0", "id": "0eed6e063989e5844feba578399e6ff8", "method": "deviceInfo", "params": {}}' 'http://localhost:51046/jsonrpc/0'
15:52:04.816 Response (79 ms) >>>
{"jsonrpc":"2.0","id":"0eed6e063989e5844feba578399e6ff8","result":{"currentPackageName":"com.android.systemui","displayHeight":2097,"displayRotation":0,"displaySizeDpX":360,"displaySizeDpY":780,"displayWidth":1080,"productName":"freedom_turbo_XL","screenOn":true,"sdkInt":29,"naturalOrientation":true}}
<<< END
- 休眠
# 相當于 time.sleep(10)
d.sleep(10)
8.4 亮滅屏
# 亮屏
d.screen_on()
# 滅屏
d.screen_off()
8.5 螢幕方向
# 設定螢幕方向
d.set_orientation(value)
# 擷取目前螢幕方向
d.orientation
value 值參考,隻要是元組中的任一一個值就可以。
# 正常豎屏
(0, "natural", "n", 0),
# 往左橫屏,相當于手機螢幕順時針旋轉 90 度
# 現實中如果要達到此效果,需要将手機逆時針旋轉 90 度
(1, "left", "l", 90),
# 倒置,這個需要看手機系統是否支援 , 倒過來顯示
(2, "upsidedown", "u", 180),
# 往右橫屏,調整與往左相反,螢幕順時針旋轉 270 度
(3, "right", "r", 270))
8.6 打開通知欄與快速設定
- 打開通知欄
d.open_notification()
- 打開快速設定
d.open_quick_settings()
8.7 檔案導入導出
8.7.1 導入檔案
# 如果是目錄,這裡 "/sdcrad/" 最後一個斜杠一定要加,否則會報錯
d.push("test.txt","/sdcrad/")
d.push("test.txt","/sdcrad/test.txt")
8.7.2 導出檔案
d.pull('/sdcard/test.txt','text.txt')
8.8 執行 shell 指令
使用 shell 方法執行
8.8.1 執行非阻塞指令
output 傳回的是一個整體的字元串,如果需要抽取值,需要對 output 進行解析提取處理
# 傳回輸出和退出碼,正常為 0,異常為 1
output,exit_code = d.shell(["ls","-l"],timeout=60)
8.8.2 執行阻塞指令(持續執行的指令)
# 傳回一個指令的資料流 output 為 requests.models.Response
output = d.shell('logcat',stream=True)
try:
# 按行讀取,iter_lines 為疊代響應資料,一次一行
for line in output.iter_lines():
print(line.decode('utf8'))
finally:
output.close()
源碼描述
def shell(self, cmdargs: Union[str, List[str]], stream=False, timeout=60):
"""
Run adb shell command with arguments and return its output. Require atx-agent >=0.3.3
Args:
cmdargs: str or list, example: "ls -l" or ["ls", "-l"]
timeout: seconds of command run, works on when stream is False
stream: bool used for long running process.
Returns:
(output, exit_code) when stream is False
requests.Response when stream is True, you have to close it after using
Raises:
RuntimeError
For atx-agent is not support return exit code now.
When command got something wrong, exit_code is always 1, otherwise exit_code is always 0
"""
8.9 session(目前已經被棄用)
8.10 停止 UI2 服務
因為有 atx-agent 的存在,Uiautomator 會被一直守護着,如果退出了就會被重新啟動起來。但是 Uiautomator 又是霸道的,一旦它在運作,手機上的輔助功能、電腦上的 uiautomatorviewer 就都不能用了,除非關掉該架構本身的 uiautomator
使用代碼停止
d.service("uiautomator").stop()
手動停止
直接打開 ATX APP(init 成功後,就會安裝上),點選關閉 UIAutomator
以上,歡迎大家一起交流探讨。