minicap java_minicap_工具使用
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_工具使用相关推荐
- STF开源框架之minicap工具
1.minicap 1.1 minicap介绍 minicap是开源项目STF(Smartphone Test Farm)中的一个工具,负责屏幕显示. stf自己写了一个工具叫minicap用来替代原 ...
- stf java_STF 框架之 minicap 工具
minicap 介绍 从WEB 端批量移动设备管理控制工具 STF 的环境搭建和运行文章了解到 STF 这个工具,然后试用了一下.最近在做一个测试工具,发现 Android 原生的截图工具截图非常缓慢 ...
- android UI定位工具-uiautomator2的使用
介绍: uiautomator2 是一个可以使用Python对Android设备进行UI自动化的库.其底层基于Google uiautomator,Google提供的uiautomator库可以获取屏 ...
- minicap_工具使用
minicap介绍 minicap是开源项目STF(Smartphone Test Farm)中的一个工具,负责屏幕显示. stf自己写了一个工具叫minicap用来替代原生的screencap,这个 ...
- android动态刷新主页,Android app 页面加载统计工具
工具:appium+mincap+opencv appium 用于自动化操作,minicap 用于截图,opencv 用于图像识别 页面统计原理 1.app 启动:命令执行的时候开始统计到图片数据匹 ...
- android 脚本录制工具,[atx 系列] android 脚本录制
简介 自动化测试中写测试脚本.维护脚本是件苦差事.atx 中已有辅助写脚本的 gui 工具和 webide,最近又增加了一个录制生成脚本的辅助工具,这个工具使用起来也比较简单,启动后直接在手机上操作即 ...
- android 测试工具,安卓测试工具的几种实现方式
因为工作之余经常研究一些乱七八糟的东西,学到了一些东西. 脑洞大开,这里分享一些实现测试工具的歪门邪道,大佬们请补充. APK 这种方式没有什么介绍的,就是和常规开发一样,Java 调用安卓 sdk ...
- Airtest网易自动化测试工具
一 使用目的 该工具主要是面向游戏UI测试基于图像识别,如游戏框架unity,Cocos-js以及网易内部的游戏框架 同时也支持原生Android App 的基于元素识别的UI自动化测试. 本文主要使 ...
- 网易 UI 自动化工具 Airtest 浅用记录
一 使用目的 该工具主要是面向游戏UI测试基于图像识别,如游戏框架unity,Cocos-js以及网易内部的游戏框架 同时也支持原生Android App 的基于元素识别的UI自动化测试. 本文主要使 ...
最新文章
- 魅族手机使用应用沙盒一键修改位置数据
- HTTP metadata数据
- python和c哪个好找工作-Python和c哪个效率高一些
- C++ Primer 5th笔记(5)chapter5 语句
- 【大白话系列】带你进入网络的世界【都说计网难,一篇即可激发你的兴趣】
- Android 极广推送接入
- privoxy支持同时代理多少个_使用ssh隧道+privoxy实现Linux全局代理
- PyTorch入门-词向量
- java通过jri 数据分析_SparkR安装部署及数据分析实例
- 大二物竞金牌转北大计算机,物理竞赛保送去北大还是清华?
- 在VB语言中,DOEVENTS的具体的用法和含义
- .axf文件_一文看懂hex文件、bin文件、axf文件的区别
- 项目管理的49个过程整理
- mysql or 索引失效_MySQL索引失效的几种情况详析
- 孩子给产品经理的一堂课
- Behavior tree 编程实战
- bzoj 3739 DZY loves math VIII
- STM32 使用CubeMX HAL库快速生成USBVCP虚拟串口工程
- 响应式织梦模板玩具动漫类网站
- 利用pandas自动化办公填表
热门文章
- JS 两个数组(对象)去重合并
- 用C语言自动将小写字母转换为大写字母
- 剑指 Offer 10- I. 斐波那契数列
- Html5的废弃与新增全局属性及元素
- winwods 10 移动硬盘无法弹出
- PMBOK(第六版) PMP笔记——《十》第十章(项目沟通管理)
- 人机交互是为了解决计算机的,人机交互与交互设计有什么区别
- 视频剪切合并器使用方法,如何使用视频剪切合并器分割合并视频片段
- springboot+神奇桔乡旅游信息系统 毕业设计-附源码191750
- 经典论文翻译导读之《A Bloat-Aware Design for Big Data Applications》