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.   ->   ...

        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" ")
        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" ")

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性能最差。

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

关注微信公众号即可在手机上查阅,并可接收更多测试分享~

原文地址:https://www.cnblogs.com/songzhenhua/p/15315249.html