Airtest源碼分析--Android屏幕截圖方式


上期回顧: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性能最差。

---------------------------------------------------------------------------------

關注微信公眾號即可在手機上查閱,並可接收更多測試分享~


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM