上期回顧:Airtest之iOS API匯總
以下基於airtest1.2.0
airtest中安卓的屏幕截圖代碼都在
/airtest/core/android/cap_methods/
該目錄結構為
.
├── base_cap.py
├── adbcap.py
├── javacap.py
├── minicap.py
└── screen_proxy.py
base_cap.py
其中的BaseCap類是所有屏幕截圖方法的基類,定義了一些基本的方法,其他具體截圖方法類使用或重寫這些方法。
minicap.py
minicap是stf框架內置的一個截圖工具,通過建立websocket連接,支持實時傳輸手機屏幕stream到PC端。
https://github.com/openstf/minicap
Minicap類基於stf的minicap。通過adb forward
設置airtest和手機端的端口映射關系,建立一個socket持續獲取minicap stream,應該就是airtestIDE中的投屏基礎。
def get_stream(self, lazy=True):
"""
Get stream, it uses `adb forward`and socket communication. Use minicap ``lazy``mode (provided by gzmaruijie)
for long connections - returns one latest frame from the server
Args:
lazy: True or Fals
"""
gen = self._get_stream(lazy)
# if quirk error, restart server and client once
stopped = next(gen)
if stopped:
try:
next(gen)
except StopIteration:
pass
gen = self._get_stream(lazy)
next(gen)
return gen
通過adb minicap -s
命令獲取一幀截圖,以實現airtestIDE中的截圖及對比的基礎。
def get_frame(self, projection=None):
"""
Get the single frame from minicap -s, this method slower than `get_frames`
1. shell cmd
1. remove log info
1. \r\r\n -> \n ...
Args:
projection: screenshot projection, default is None which means using self.projection
Returns:
jpg data
"""
params, display_info = self._get_params(projection)
if self.display_id:
raw_data = self.adb.raw_shell(
self.CMD + " -d " + str(self.display_id) + " -n 'airtest_minicap' -P %dx%d@%dx%d/%d -s" % params,
ensure_unicode=False,
)
else:
raw_data = self.adb.raw_shell(
self.CMD + " -n 'airtest_minicap' -P %dx%d@%dx%d/%d -s" % params,
ensure_unicode=False,
)
jpg_data = raw_data.split(b"for JPG encoder" + self.adb.line_breaker)[-1]
jpg_data = jpg_data.replace(self.adb.line_breaker, b"\n")
return jpg_data
adbcap.py
AdbCap類就是通過adb shell screencap -p
來獲取屏幕截圖
class AdbCap(BaseCap):
def get_frame_from_stream(self):
warnings.warn("Currently using ADB screenshots, the efficiency may be very low.")
return self.adb.snapshot()
def snapshot(self, ensure_orientation=True):
screen = super(AdbCap, self).snapshot()
if ensure_orientation and self.adb.sdk_version <= SDK_VERISON_ANDROID7:
screen = aircv.rotate(screen, self.adb.display_info["orientation"] * 90, clockwise=False)
return screen
看一下其調用的adb.snapshot()方法
def snapshot(self):
"""
Take the screenshot of the device display
Returns:
command output (stdout)
"""
if self.display_id:
raw = self.cmd('shell screencap -d {0} -p'.format(self.display_id), ensure_unicode=False)
else:
raw = self.cmd('shell screencap -p', ensure_unicode=False)
return raw.replace(self.line_breaker, b"\n")
javacap.py
Javacap類基於yosemite作為service,建立socket連接和airtest通訊,提供截圖。性能比minicap差,但兼容性高,有些機型不支持minicap。
screen_proxy.py
ScreenProxy類,屏幕代理,airtest1.2.0引入。在以前遇到特殊機型,是要提供特殊的連接參數的,如cap_method="JAVACAP"
,或在airtestIDE連接前選中JAVACAP。
從airtest1.2.0、airtestIDE1.2.10開始,就不需要再指定連接參數了,airtest會自己去進行適配,按性能初始化順序為自定義方法 > MINICAP > JAVACAP > ADBCAP,如果調用到了ADBCAP,將會打印一個warning,告訴用戶此時截圖效率很低。
這樣,以后無需再關注底層到底用的是誰,需要截圖時直接通過screen_proxy即可,例如:
dev = Android()
screen_proxy = ScreenProxy.auto_setup(dev.adb, rotation_watcher=dev.rotation_watcher)
screen_proxy.get_frame_from_stream()
screen_proxy.teardown_stream()
如果希望實現自定義的新截圖方法:在airtest.core.android.cap_methods.base_cap中,實現了所有截圖方法的基類BaseCap,可以通過繼承它,並實現get_frame_from_stream這個接口,就能新增一個自定義的截圖方法,將它注冊到ScreenProxy中就可以使用了。
from airtest.core.android.cap_methods.base_cap import BaseCap
class TestCap(BaseCap):
def get_frame_from_stream(self):
return b"frame"
ScreenProxy.register_method("TESTCPY", TestCap)
# 默認優先初始化為自定義的TestCap
cap = ScreenProxy.auto_setup(dev.adb)
總結
划重點,MINICAP性能最好,JAVACAP性能次之但兼容性好,ADBCAP性能最差。
---------------------------------------------------------------------------------
關注微信公眾號即可在手機上查閱,並可接收更多測試分享~