minicap介绍

stf自己写了一个工具叫minicap用来替代原生的screencap,这个工具是stf框架的依赖工具之一,最近手头上的项目刚好由于帧率卡顿需要优化,刚好来在testerhome社区看到对STF的介绍,WEB 端批量移动设备管理控制工具 STF 的环境搭建和运行

minicap工具是用NDK开发的,属于Android的底层开发,该工具分为两个部分,一个是动态连接库.so文件,一个是minicap可执行文件。但不是通用的,因为CPU架构的不同分为不同的版本文件,STF提供的minicap文件根据CPU 的ABI分为如下4种:

arm64-v8a、armeabi-v7a,x86,x86_64 架构。而minicap.so文件在这个基础上还要分为不同的sdk版本。这些都可以从Github地址:链接地址下载而来

结构树目录.

├── bin

│ ├── arm64-v8a

│ │ ├── minicap

│ │ └── minicap-nopie

│ ├── armeabi-v7a

│ │ ├── minicap

│ │ └── minicap-nopie

│ ├── x86

│ │ ├── minicap

│ │ └── minicap-nopie

│ └── x86_64

│ ├── minicap

│ └── minicap-nopie

└── shared

├── android-10

│ └── armeabi-v7a

│ └── minicap.so

├── android-14

│ ├── armeabi-v7a

│ │ └── minicap.so

│ └── x86

│ └── minicap.so

├── android-15

│ ├── armeabi-v7a

│ │ └── minicap.so

│ └── x86

│ └── minicap.so

├── android-16

│ ├── armeabi-v7a

│ │ └── minicap.so

│ └── x86

│ └── minicap.so

├── android-17

│ ├── armeabi-v7a

│ │ └── minicap.so

│ └── x86

│ └── minicap.so

├── android-18

│ ├── armeabi-v7a

│ │ └── minicap.so

│ └── x86

│ └── minicap.so

├── android-19

│ ├── armeabi-v7a

│ │ └── minicap.so

│ └── x86

│ └── minicap.so

├── android-21

│ ├── arm64-v8a

│ │ └── minicap.so

│ ├── armeabi-v7a

│ │ └── minicap.so

│ ├── x86

│ │ └── minicap.so

│ └── x86_64

│ └── minicap.so

├── android-22

│ ├── arm64-v8a

│ │ └── minicap.so

│ ├── armeabi-v7a

│ │ └── minicap.so

│ ├── x86

│ │ └── minicap.so

│ └── x86_64

│ └── minicap.so

├── android-9

│ └── armeabi-v7a

│ └── minicap.so

└── android-M

├── arm64-v8a

│ └── minicap.so

├── armeabi-v7a

│ └── minicap.so

├── x86

│ └── minicap.so

└── x86_64

└── minicap.so

准备对应文件

a、查看CPU架构(adb shell getprop ro.product.cpu.abi)及查看android版本level(adb shell getprop ro.build.version.sdk)

b、根据上面获取的信息,将适合设备的可执行文件和.so文件push到手机的/data/local/tmp目录下,或者在STF框架的源码下找到vendor/minicap文件夹下

c、adb shell进入到目录下chmod 777 minicap

d、测试一下minicap是否可用:(-P后面跟的参数为你屏幕的尺寸)

adb shell LD_LIBRARY_PATH=/data/local/tmp /data/local/tmp/minicap -P 1080x1920@1080x1920/0 -t

安装运行环境

a、安装nodejs:

查看版本号:node -v

b、安装运行依赖 ws和express包

npm install ws –g

npm install express -g

启动手机端服务

就是启动了一个socket服务器

adb shell LD_LIBRARY_PATH=/data/local/tmp /data/local/tmp/minicap -P 1080x1920@1080x1920/0

本地端口转发

a、跟上面的socket服务通信,首先我们要将本地的端口映射到minicap工具上,端口随意:

adb forward tcp:1717 localabstract:minicap

b、输入 node app.js 回车启动服务:

控制台显示  Listening on port 9002 表示启动成功

c、浏览器打开本地 localhost:9002  链接地址,查看

获取信息

然后使用命令nc localhost 1717来与minicap通信,然后你会发现好多乱码。官方提供了一个demo来看效果,在minicap项目下的example目录

但是这些信息是有规则的,只是我们无法实际查看。但是我们做的工具需要用java来获得该信息,所以弄懂这些格式是很有必要的,结果分析后得出这些信息分3部分

Banner模块(第一部分)

这一部分的信息只在连接后,只发送一次,是一些汇总信息,一般为24个16进制字符,每一个字符都表示不同的信息:

位置信息

0

版本

1

该Banner信息的长度,方便循环使用

2,3,4,5

相加得到进程id号

6,7,8,9

累加得到设备真实宽度

10,11,12,13

累加得到设备真实高度

14,15,16,17

累加得到设备的虚拟宽度

18,19,20,21

累加得到设备的虚拟高度

22

设备的方向

23

设备信息获取策略

携带图片大小信息和图片二进制信息模块(第二部分)

得到上面的Banner部分处理完成后,以后不会再发送Banner信息,后续只会发送图片相关的信息。那么接下来就接受图片信息了,第一个过来的图片信息的前4个字符不是图片的二进制信息,而是携带着图片大小的信息,我们需要累加得到图片大小。这一部分的信息除去前四个字符,其他信息也是图片的实际二进制信息,比如我们接受到的信息长度为n,那么4~(n-4)部分是图片的信息,需要保存下来。

只携带图片二进制信息模块(第三部分)

每一个变化的界面都会有上面的[携带图片大小信息和图片二进制信息模块],当得到大小后,或许发送过来的数据都是要组装成图片的二进制信息,知道当前屏幕的数据发送完成。

有2种方式可以看出来图片组装完成了:

又遇到第二部分

设定大小的数据已经装满了

java的实现:

import java.awt.image.BufferedImage;

import java.io.ByteArrayInputStream;

import java.io.DataInputStream;

import java.io.File;

import java.io.IOException;

import java.io.InputStream;

import java.net.Socket;

import java.net.UnknownHostException;

import java.util.ArrayList;

import java.util.List;

import java.util.Queue;

import java.util.Stack;

import java.util.concurrent.ConcurrentLinkedQueue;

import javax.imageio.ImageIO;

import org.apache.log4j.Logger;

import com.android.ddmlib.AdbCommandRejectedException;

import com.android.ddmlib.CollectingOutputReceiver;

import com.android.ddmlib.IDevice;

import com.android.ddmlib.IDevice.DeviceUnixSocketNamespace;

import com.android.ddmlib.ShellCommandUnresponsiveException;

import com.android.ddmlib.SyncException;

import com.android.ddmlib.TimeoutException;

import com.wuba.utils.DirStructureUtil;

import com.wuba.utils.TimeUtil;

/**

*@date 2015年8月12日 上午11:02:53

*/

public class MiniCapUtil {

private Logger LOG = Logger.getLogger(MiniCapUtil.class);

// CPU架构的种类

public static final String ABIS_ARM64_V8A = "arm64-v8a";

public static final String ABIS_ARMEABI_V7A = "armeabi-v7a";

public static final String ABIS_X86 = "x86";

public static final String ABIS_X86_64 = "x86_64";

private Queue dataQueue = new ConcurrentLinkedQueue();

private Banner banner = new Banner();

private static final int PORT = 1717;

private IDevice device;

private String REMOTE_PATH = "/data/local/tmp";

private String ABI_COMMAND = "ro.product.cpu.abi";

private String SDK_COMMAND = "ro.build.version.sdk";

private String MINICAP_BIN = "minicap";

private String MINICAP_SO = "minicap.so";

private String MINICAP_CHMOD_COMMAND = "chmod 777 %s/%s";

private String MINICAP_WM_SIZE_COMMAND = "wm size";

private String MINICAP_START_COMMAND = "LD_LIBRARY_PATH=/data/local/tmp /data/local/tmp/minicap -P %s@%s/0";

private boolean isRunning = false;

public MiniCapUtil(IDevice device) {

this.device = device;

init();

}

/**

* 将minicap的二进制和.so文件push到/data/local/tmp文件夹下,启动minicap服务

*/

private void init() {

String abi = device.getProperty(ABI_COMMAND);

String sdk = device.getProperty(SDK_COMMAND);

File minicapBinFile = new File(DirStructureUtil.getMinicapBin(), abi

+ File.separator + MINICAP_BIN);

File minicapSoFile = new File(DirStructureUtil.getMinicapSo(),

"android-" + sdk + File.separator + abi + File.separator

+ MINICAP_SO);

try {

// 将minicap的可执行文件和.so文件一起push到设备中

device.pushFile(minicapBinFile.getAbsolutePath(), REMOTE_PATH

+ File.separator + MINICAP_BIN);

device.pushFile(minicapSoFile.getAbsolutePath(), REMOTE_PATH

+ File.separator + MINICAP_SO);

executeShellCommand(String.format(MINICAP_CHMOD_COMMAND,

REMOTE_PATH, MINICAP_BIN));

// 端口转发

device.createForward(PORT, "minicap",

DeviceUnixSocketNamespace.ABSTRACT);

// 获取设备屏幕的尺寸

String output = executeShellCommand(MINICAP_WM_SIZE_COMMAND);

String size = output.split(":")[1].trim();

final String startCommand = String.format(MINICAP_START_COMMAND,

size, size);

// 启动minicap服务

new Thread(new Runnable() {

@Override

public void run() {

LOG.info("minicap服务器启动");

executeShellCommand(startCommand);

}

}).start();

} catch (SyncException e) {

// TODO Auto-generated catch block

e.printStackTrace();

} catch (IOException e) {

// TODO Auto-generated catch block

e.printStackTrace();

} catch (AdbCommandRejectedException e) {

// TODO Auto-generated catch block

e.printStackTrace();

} catch (TimeoutException e) {

// TODO Auto-generated catch block

e.printStackTrace();

}

}

private String executeShellCommand(String command) {

CollectingOutputReceiver output = new CollectingOutputReceiver();

try {

device.executeShellCommand(command, output, 0);

} catch (TimeoutException e) {

// TODO Auto-generated catch block

e.printStackTrace();

} catch (AdbCommandRejectedException e) {

// TODO Auto-generated catch block

e.printStackTrace();

} catch (ShellCommandUnresponsiveException e) {

// TODO Auto-generated catch block

e.printStackTrace();

} catch (IOException e) {

// TODO Auto-generated catch block

e.printStackTrace();

}

return output.getOutput();

}

public void startScreenListener() {

isRunning = true;

new Thread(new ImageConverter()).start();

new Thread(new ImageBinaryFrameCollector()).start();

}

public void stopScreenListener() {

isRunning = false;

}

private synchronized void createImageFromByte(byte[] binaryData) {

InputStream in = new ByteArrayInputStream(binaryData);

try {

BufferedImage bufferedImage = ImageIO.read(in);

ImageIO.write(bufferedImage, "jpg", new File("screen.jpg"));

} catch (IOException e) {

e.printStackTrace();

}

}

// java合并两个byte数组

private static byte[] byteMerger(byte[] byte_1, byte[] byte_2) {

byte[] byte_3 = new byte[byte_1.length + byte_2.length];

System.arraycopy(byte_1, 0, byte_3, 0, byte_1.length);

System.arraycopy(byte_2, 0, byte_3, byte_1.length, byte_2.length);

return byte_3;

}

private static byte[] subByteArray(byte[] byte1, int start, int end) {

byte[] byte2 = new byte[end - start];

System.arraycopy(byte1, start, byte2, 0, end - start);

return byte2;

}

class ImageBinaryFrameCollector implements Runnable {

private Socket socket;

@Override

public void run() {

LOG.debug("图片二进制数据收集器已经开启");

// TODO Auto-generated method stub

InputStream stream = null;

DataInputStream input = null;

try {

socket = new Socket("localhost", PORT);

stream = socket.getInputStream();

input = new DataInputStream(stream);

while (isRunning) {

byte[] buffer;

int len = 0;

while (len == 0) {

len = input.available();

}

buffer = new byte[len];

input.read(buffer);

dataQueue.add(buffer);

}

} catch (IOException e) {

e.printStackTrace();

} finally {

if (socket != null && socket.isConnected()) {

try {

socket.close();

} catch (IOException e) {

e.printStackTrace();

}

}

if (stream != null) {

try {

stream.close();

} catch (IOException e) {

// TODO Auto-generated catch block

e.printStackTrace();

}

}

}

LOG.debug("图片二进制数据收集器已关闭");

}

}

class ImageConverter implements Runnable {

private int readBannerBytes = 0;

private int bannerLength = 2;

private int readFrameBytes = 0;

private int frameBodyLength = 0;

private byte[] frameBody = new byte[0];

@Override

public void run() {

LOG.debug("图片生成器已经开启");

long start = System.currentTimeMillis();

while (isRunning) {

byte[] binaryData = dataQueue.poll();

if (binaryData == null)

continue;

int len = binaryData.length;

for (int cursor = 0; cursor < len;) {

int byte10 = binaryData[cursor] & 0xff;

if (readBannerBytes < bannerLength) {

cursor = parserBanner(cursor, byte10);

}

minicap java_minicap_工具使用相关推荐

  1. STF开源框架之minicap工具

    1.minicap 1.1 minicap介绍 minicap是开源项目STF(Smartphone Test Farm)中的一个工具,负责屏幕显示. stf自己写了一个工具叫minicap用来替代原 ...

  2. stf java_STF 框架之 minicap 工具

    minicap 介绍 从WEB 端批量移动设备管理控制工具 STF 的环境搭建和运行文章了解到 STF 这个工具,然后试用了一下.最近在做一个测试工具,发现 Android 原生的截图工具截图非常缓慢 ...

  3. android UI定位工具-uiautomator2的使用

    介绍: uiautomator2 是一个可以使用Python对Android设备进行UI自动化的库.其底层基于Google uiautomator,Google提供的uiautomator库可以获取屏 ...

  4. minicap_工具使用

    minicap介绍 minicap是开源项目STF(Smartphone Test Farm)中的一个工具,负责屏幕显示. stf自己写了一个工具叫minicap用来替代原生的screencap,这个 ...

  5. android动态刷新主页,Android app 页面加载统计工具

    ​工具:appium+mincap+opencv appium 用于自动化操作,minicap 用于截图,opencv 用于图像识别 页面统计原理 1.app 启动:命令执行的时候开始统计到图片数据匹 ...

  6. android 脚本录制工具,[atx 系列] android 脚本录制

    简介 自动化测试中写测试脚本.维护脚本是件苦差事.atx 中已有辅助写脚本的 gui 工具和 webide,最近又增加了一个录制生成脚本的辅助工具,这个工具使用起来也比较简单,启动后直接在手机上操作即 ...

  7. android 测试工具,安卓测试工具的几种实现方式

    因为工作之余经常研究一些乱七八糟的东西,学到了一些东西. 脑洞大开,这里分享一些实现测试工具的歪门邪道,大佬们请补充. APK 这种方式没有什么介绍的,就是和常规开发一样,Java 调用安卓 sdk ...

  8. Airtest网易自动化测试工具

    一 使用目的 该工具主要是面向游戏UI测试基于图像识别,如游戏框架unity,Cocos-js以及网易内部的游戏框架 同时也支持原生Android App 的基于元素识别的UI自动化测试. 本文主要使 ...

  9. 网易 UI 自动化工具 Airtest 浅用记录

    一 使用目的 该工具主要是面向游戏UI测试基于图像识别,如游戏框架unity,Cocos-js以及网易内部的游戏框架 同时也支持原生Android App 的基于元素识别的UI自动化测试. 本文主要使 ...

最新文章

  1. 魅族手机使用应用沙盒一键修改位置数据
  2. HTTP metadata数据
  3. python和c哪个好找工作-Python和c哪个效率高一些
  4. C++ Primer 5th笔记(5)chapter5 语句
  5. 【大白话系列】带你进入网络的世界【都说计网难,一篇即可激发你的兴趣】
  6. Android 极广推送接入
  7. privoxy支持同时代理多少个_使用ssh隧道+privoxy实现Linux全局代理
  8. PyTorch入门-词向量
  9. java通过jri 数据分析_SparkR安装部署及数据分析实例
  10. 大二物竞金牌转北大计算机,物理竞赛保送去北大还是清华?
  11. 在VB语言中,DOEVENTS的具体的用法和含义
  12. .axf文件_一文看懂hex文件、bin文件、axf文件的区别
  13. 项目管理的49个过程整理
  14. mysql or 索引失效_MySQL索引失效的几种情况详析
  15. 孩子给产品经理的一堂课
  16. Behavior tree 编程实战
  17. bzoj 3739 DZY loves math VIII
  18. STM32 使用CubeMX HAL库快速生成USBVCP虚拟串口工程
  19. 响应式织梦模板玩具动漫类网站
  20. 利用pandas自动化办公填表

热门文章

  1. JS 两个数组(对象)去重合并
  2. 用C语言自动将小写字母转换为大写字母
  3. 剑指 Offer 10- I. 斐波那契数列
  4. Html5的废弃与新增全局属性及元素
  5. winwods 10 移动硬盘无法弹出
  6. PMBOK(第六版) PMP笔记——《十》第十章(项目沟通管理)
  7. 人机交互是为了解决计算机的,人机交互与交互设计有什么区别
  8. 视频剪切合并器使用方法,如何使用视频剪切合并器分割合并视频片段
  9. springboot+神奇桔乡旅游信息系统 毕业设计-附源码191750
  10. 经典论文翻译导读之《A Bloat-Aware Design for Big Data Applications》