自动化工具 app_process 与反射大法实现安卓截屏

yoegg · 2018年03月12日 · 最后由 a1354592998 回复于 2018年09月14日 · 3545 次阅读
本帖已被设为精华帖!

去年写过关于安卓运行dex的文章 https://testerhome.com/topics/9649
那时候觉得真是好东西。最近研究minicap(C++),vysor(反射调用screenshot)这些同屏工具,想造个轮子试试:dex+反射截屏。

原文在此:http://www.wanyor.com/index.php/2018/03/12/115.html

0x00.截屏核心代码:

参考安卓源码:

/**
反射大法调用Surface|SurfaceControl的screenshot方法
参考安卓源码:
sdk > 17: https://android.googlesource.com/platform/frameworks/base/+/master/core/java/android/view/SurfaceControl.java
sdk <= 17: https://android.googlesource.com/platform/frameworks/base/+/android-4.2.2_r1.2/core/java/android/view/Surface.java
*/

public static Bitmap screecap(int screenWidth, int screenHeight){

String surfaceClassName = " ";

if (Build.VERSION.SDK_INT <= 17) {
surfaceClassName = "android.view.Surface";
} else {
surfaceClassName = "android.view.SurfaceControl";
}

// 关键在于此处反射调用获取bitmap
Bitmap bitmap = (Bitmap) Class.forName(surfaceClassName).getDeclaredMethod("screenshot", new Class[]{Integer.TYPE, Integer.TYPE})
.invoke(null, new Object[]{picWidth, picHeight});
return bitmap;
}

0x01.bitmap转换为图片

压缩bitmap为jpg|png图片,写入sd卡或其他合适路径即可。

// 关键代码,压缩mBitmap为不失真的jpg图片,写入fOut。
mBitmap.compress(Bitmap.CompressFormat.JPEG, 100, fOut);

0x02.拓展:浏览器看图

java实现一个简单的HTTP server,把图片base64编码, 嵌在html中自动刷新,然后使用adb forward重定向绑定到电脑的某一端口,浏览器访问。

//截屏转base64字符串
public static String cap2base64(){
int picWidth = 1080;
int picHeight = 1920;
String result = "";

System.out.println("Starting screen capture...");

long startTime = System.currentTimeMillis();

String surfaceClassName = " ";
if (Build.VERSION.SDK_INT <= 17) {
surfaceClassName = "android.view.Surface";
} else {
surfaceClassName = "android.view.SurfaceControl";
}

try {
Bitmap bitmap;
bitmap = (Bitmap) Class.forName(surfaceClassName).getDeclaredMethod("screenshot", new Class[]{Integer.TYPE, Integer.TYPE})
.invoke(null, new Object[]{picWidth, picHeight});

System.out.println(bitmap.getWidth() + "x" + bitmap.getHeight());
System.out.println(bitmap.toString());

ByteArrayOutputStream baos = new ByteArrayOutputStream();
bitmap.compress(Bitmap.CompressFormat.JPEG, 100, baos);

baos.flush();
baos.close();

byte[] bitmapBytes = baos.toByteArray();
result = Base64.encodeToString(bitmapBytes, Base64.DEFAULT);

long endTime = System.currentTimeMillis();
System.out.println("Cost: " + (endTime - startTime) + "ms");
System.out.println("Screen capture finished.");

} catch (IllegalAccessException e) {
System.out.println("1 error");
e.printStackTrace();
} catch (InvocationTargetException e) {
System.out.println("2 error");
e.printStackTrace();
} catch (NoSuchMethodException e) {
System.out.println("3 error");
e.printStackTrace();
} catch (ClassNotFoundException e) {
System.out.println("4 error");
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
return result;
}

//http server
public void startServer(int port){
try {
try (ServerSocket ss = new ServerSocket(port)) {
while (true) {
Socket socket = ss.accept();

PrintWriter pw = new PrintWriter(socket.getOutputStream());

pw.println("HTTP/1.1 200 OK");
pw.println("Content-type:text/html");
pw.println();
pw.println("<head>" +
"<meta charset=\"utf-8\"/>" +
"<meta http-equiv=\"refresh\" content=\"0.25\">" +
"<title>Android Screen Mirror</title>" +
"</head>");
pw.println("<h2>A screen mirror tool for android by Wanyor.</h2>" );

Utils u = new Utils();
String imgBase64 = u.cap2base64();
pw.println("<img src=\"data:image/png;base64," + imgBase64 + "\" width=\"480\" height=\"800\"/>");

pw.flush();
socket.close();
}
}
} catch (IOException e) {
e.printStackTrace();
}
}

0x03.demo尝鲜

点此下载:yocap.zip
运行方式:

  • adb push yocap.dex /data/local/tmp/yocap.dex
  • adb shell app_process -Djava.class.path=/data/local/tmp/yocap.dex /data/local/tmp Main -s
  • adb forward tcp:8888 tcp:8888
  • 打开浏览器,输入localhost:8888即可看到你的手机屏幕了。

0x04.完成

如果觉得我的文章对您有用,请随意打赏。您的支持将鼓励我继续创作!
最佳回复

可以搭建websocket服务,来实现投屏,帧数可以到28左右。

共收到 23 条回复 时间 点赞
seveniruby 将本帖设为了精华贴 03月13日 07:47

chrome浏览器访问127.0.0.1:8888,命令行窗口显示server killed。测试设备为网易Mumu模拟器。

你把服务绑定到8888了?
adb forward tcp:8888 tcp:8888

yoegg 回复

是的,有绑定的。

我win7这边正常,你的是安卓啥版本?
run.bat脚本内容如下:

@echo off

rem 1. push dex文件到/data/local/tmp
rem 2. 绑定设备8888端口到电脑8888端口
rem 3. 打开截屏服务和查看网页

adb push yocap.dex /data/local/tmp&&adb forward tcp:8888 tcp:8888&&start http://localhost:8888&adb shell app_process -Djava.class.path=/data/local/tmp/yocap.dex /data/local/tmp Main -s

pause

yoegg 回复

我用模拟器玩的,我换个真机试试。

yoegg 回复

我这换了真机也还是不行,小米2S MIUI 8。

我换了小米5,OPPO R11s测试了下都可以

yoegg 回复

可能我这边设备跟PC环境有问题,我再捣鼓一下。😂

可以搭建websocket服务,来实现投屏,帧数可以到28左右。

yoegg #12 · 2018年04月20日 作者
kinget007 回复

我最近在试这些,想做个远程真机分享的工具。

yoegg 回复

minicap帧数会更高,mix2极限是32

yoegg #14 · 2018年04月20日 作者
kinget007 回复

主要是因为jpeg-turbo编码快

yoegg 回复

主要是截图方法有瓶颈。

yoegg #16 · 2018年04月23日 作者
kinget007 回复

我这里测试情况如下:
获取到屏幕bitmap平均10ms(首次获取要20ms),编码20ms左右。

测试机型一加3T(高通821)

yoegg 回复

b.compress(Bitmap.CompressFormat.JPEG, 100, outputStream) 这个我这边基本耗时在80-90ms

yoegg #18 · 2018年04月24日 作者
kinget007 回复

1、你这个是编码慢,你是什么机器?
2、反射获取截图那块你那边平均花多少时间?

yoegg 回复

mix2 反射获取截图10ms不到

kinget007 回复

我这边用google pixcel基本上获取时间在100ms左右,波动比较大,七八十的有,两百的也有

sj_cug 回复

把图片压缩下浏览器端还是挺流畅的,清晰度可以接受

仅楼主可见
yoegg 安卓屏幕旋转角度监控 中提及了此贴 07月17日 17:36

连接进不去了

需要 登录 后方可回复, 如果你还没有账号请点击这里 注册