Android投屏及控制
对于mac 笔记本用户:
对于所有用户:
adb push Main.dex /sdcard/Main.dex
adb shell
export CLASSPATH=/sdcard/Main.dex
exec app_process /sdcard com.wanjian.puppet.Main
adb forward tcp:8888 localabstract:puppet-ver1
注意:高版本的android手机需要去开发者选项中开启 允许模拟点击
完整命令如下
MGJwanjian:sss wanjian$ adb push Main.dex /sdcard/Main.dex
[100%] /sdcard/Main.dex
MGJwanjian:sss wanjian$ adb shell
shell@mx5:/ $ export CLASSPATH=/sdcard/Main.dex
shell@mx5:/ $ exec app_process /sdcard com.wanjian.puppet.Main
MGJwanjian:~ wanjian$ adb forward tcp:8888 localabstract:puppet-ver1
原理和Vysor相同,Android提供了两个截屏方法Surface. screenshot和SurfaceControl. screenshot, 这两个API是隐藏的,客户端没有权限调用,即使通过反射也得不到bitmap,我们可以使用adb命令 启动一个进程,让该进程调用该API就可以得到bitmap了,然后通过socket把数据发送到PC即可。
关键代码如下:
public class Main{
public static void main(String[]args){
Point size = new Point();
size.x = 1080;//最终截屏图片的大小,可以和屏幕不一样大
size.y = 1920;
String surfaceClassName;
if (Build.VERSION.SDK_INT <= 17) {
surfaceClassName = "android.view.Surface";
} else {
surfaceClassName = "android.view.SurfaceControl";
}
Bitmap b = (Bitmap) Class.forName(surfaceClassName).getDeclaredMethod("screenshot", new Class[]{Integer.TYPE, Integer.TYPE}).invoke(null, new Object[]{Integer.valueOf(size.x), Integer.valueOf(size.y)});
}
}
这样就可以调用到Main.main方法
InputManager im = (InputManager) InputManager.class.getDeclaredMethod("getInstance", new Class[0]).invoke(null, new Object[0]);
MotionEvent.class.getDeclaredMethod("obtain", new Class[0]).setAccessible(true);
Method injectInputEventMethod = InputManager.class.getMethod("injectInputEvent", new Class[]{InputEvent.class, Integer.TYPE});
MotionEvent event = MotionEvent.obtain(downTime, eventTime, action, x, y, pressure, 1.0f, 0, 1.0f, 1.0f, 0, 0);
event.setSource(InputDeviceCompat.SOURCE_TOUCHSCREEN);
injectInputEventMethod.invoke(im, new Object[]{event, Integer.valueOf(0)});
获取屏幕大小时会用到几个特殊的API,Android sdk没有提供这几个API,但Android运行时却可以调用,
为了保证编译不报错,我们可以自己手写这几个API,保证包名,方法签名和系统中的相同即可,方法若有
返回值直接返回null即可。例如
package android.view;
import android.graphics.Point;
import android.os.IBinder;
/**
* Created by wanjian on 2017/4/4.
*/
public interface IWindowManager {
void getInitialDisplaySize(int i, Point displaySize);
int getRotation();
void getRealDisplaySize(Point displaySize);
abstract class Stub {
public static IWindowManager asInterface(IBinder invoke) {
return null;
}
}
}
package android.view;
/**
* Created by wanjian on 2017/4/4.
*/
public interface DisplayInfo {
}
package android.view;
/**
* Created by wanjian on 2017/4/4.
*/
public interface IRotationWatcher {
}
由于Android的双亲委派类加载机制,Android会从系统路径下加载这几个类,并不会使用我们编写的类,我们编 写的这几个类只是为了编译不报错,所以返回null也不会出现空指针
http://blog.csdn.net/qingchunweiliang/article/details/69210431
https://github.com/android-notes/androidScreenShareAndControl
最简单的方式:
在android studio
中右击com.wanjian.puppet.Main
这个文件,选择 run Main.main()
,编译后的class文件就会自动保存到
androidScreenShareAndControl/shareandcontrollib/build/intermediates/classes/debug
这个目录中
方式2:
把 android sdk
目录下的android.jar
和supportv4.jar
拷贝到
androidScreenShareAndControl/shareandcontrollib/src/main/java
目录下
同时在这个目录下新建classes
文件夹,用于保存编译后的class文件,并把命令行切换到这个目录
执行如下命令,其中android.jar
和support-v4-23.4.0-sources.jar
是android sdk
中的jar
包,一个在platforms/android-xx
文件夹下,一个在extras/android/m2repository/com/android/support/support-v4
下。(命令中间用:分割,windows的话需要用;分割)
javac -cp android.jar:support-v4-23.4.0-sources.jar:./ com/wanjian/puppet/Main.java -d classes
这样就会在classes文件夹中生成class文件了 (JDK版本不能太高,不然会提示 unsupported class file version 52.0)
首先命令窗口切换到 /androidScreenShareAndControl/shareandcontrollib/build/intermediates/javac/debug/compileDebugJavaWithJavac/classes
目录下,可以看到
所有编译生成的class文件,如果没有先执行上面步骤生成class文件。
然后使用 dx --dex --output=Main.dex ./
命令生成dex文件。dx命令文件在 sdk/build-tools/版本号
下