Android 实现远程控制(类似QQ的远程协助)

这里简单的给个思路和已经实现点击的Demo

明确两者:控制方被控制方

实现思路:控制方获得被控制方的屏幕共享

控制方获得点击屏幕的X轴,Y轴坐标

通过服务器把坐标指令发送给被控制方

被控制方采用adb命令模拟点击;

模拟操作都是用 input 来完成

我们用adb shell来简单的看一下该指令的使用帮助


tap = 点按<X坐标> <y坐标>(默认触摸屏)
swipe = 滑动<x1> <y1> <x2> <y2> [持续时间(毫秒)](默认值:触摸屏)

由此可见,我们可以通过这个命令来实现模拟人操作从而达成远程操作功能

adb命令:input tap X Y

该命令在Window,cmd shell下不用root可以执行。

java代码调用时需要ROOT权限;

(本文Demo没有实现屏幕共享,所以点击时看着被控方的屏幕需要点击的大概位置点击控制方屏幕测试效果

屏幕共享功能可通过API21的录屏+MediaCode编码器实现

adb命令下似乎还有个截屏的API 也可以实现屏幕共享,不过该API是隐藏的,即使用系统反射也调不起来。但可以用adb命令来获得,Vysor这个软件就是通过adb调起这个截屏API不断的截屏发送数据来实现屏幕共享的)

本文Demo采用的是UDP通讯 在同一wifi局域网下实现通讯进行控制交互

发送坐标的控制方:用 dispatchtouchEvent();方法取得X,Y坐标

  @Override public boolean dispatchTouchEvent(MotionEvent ev) { msgShow.setText("点击位置的X坐标" + ev.getX() + "点击位置的Y坐标" + ev.getY()); switch (ev.getAction()) { // 点击的开始位置 case MotionEvent.ACTION_DOWN: msgShow.setText("起始位置:(" + ev.getX() + "," + ev.getY()); break; //触屏实时位置 case MotionEvent.ACTION_MOVE: msgShow.setText("实时位置:(" + ev.getX() + "," + ev.getY()); break; //离开屏幕的位置 case MotionEvent.ACTION_UP: msgShow.setText("结束位置:(" + ev.getX() + "," + ev.getY()); sendMsg(ev.getX(), ev.getY()); break; default: break; } // 注意返回值 // true:view继续响应Touch操作; // false:view不再响应Touch操作,故此处若为false,只能显示起始位置,不能显示实时位置和结束位置 return super.dispatchTouchEvent(ev); }

通过UDP发送JSON坐标数据 Data实体类就X,Y两个变量;这里的JSON我是用GSON插件生成的

 private Gson gson = new Gson(); private DatagramSocket sendSocket = null; private int sendPort = 8856;//发送端口 private void sendMsg(float x, float y) { try { if (sendSocket == null) { sendSocket = new DatagramSocket(sendPort); } //发送到的IP InetAddress inetAddress = InetAddress.getByName("192.168.1.50"); String jsno = getJsonStr(x,y); byte[] bytes = jsno.getBytes(); DatagramPacket datagramPacket = new DatagramPacket(bytes, bytes.length, inetAddress, sendPort); sendSocket.send(datagramPacket); msgShow.setText("点击的坐标" + x + y); } catch (SocketException e) { e.printStackTrace(); } catch (UnknownHostException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } } public String getJsonStr(float x, float y) { Data p = new Data(); p.setX(x); p.setY(y); return gson.toJson(p); }

接下来接收端:UDP监听帮助类

 public class UdpReceive { private DatagramSocket receiveSocket = null; private int receivePort = 8856; private DatagramPacket datagramPacket; private boolean isRunning = true; private static UdpReceive udpReceive; public static UdpReceive getUdpReceive() { if (udpReceive == null) { synchronized (UdpReceive.class) { udpReceive = new UdpReceive(); } } return udpReceive; } //初始化UDP监听 传入监听的端口号,回调 public void init(int port, Touch touch) { new Thread(() -> { try { while (isRunning) { if (receiveSocket == null) { receiveSocket = new DatagramSocket(port); } byte[] bytes = new byte[1024]; datagramPacket = new DatagramPacket(bytes, 0, bytes.length); receiveSocket.receive(datagramPacket); Log.e("接收成功 : " , new String(datagramPacket.getData()).trim()); if(touch!=null) touch.move(datagramPacket.getData()); } } catch (SocketException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } }).start(); } }

UDP初始化 JSON数据用GSON解析

 UdpReceive.getUdpReceive().init(8856, new Touch() { @Override public void move(byte[] msg) { String data = new String(msg).trim(); Data json = gson.fromJson(data,Data.class); Log.e("",String.valueOf(json.getX()+json.getY())); Log.e("X",String.valueOf(json.getX())); Log.e("Y",String.valueOf(json.getY())); runOnUiThread (() -> { RootCmd.execRootCmd("input tap "+json.getX()+" "+ json.getY()); }); } @Override public void touch() { } });

回调

  public interface Touch { void move(byte[] msg); void touch(); } 

ADB命令执行方法

  /** * 执行命令且得到结果 */ public static String inputRootCmd(String cmd) { String result = ""; DataOutputStream dos = null; DataInputStream dis = null; try { // 经过Root的android系统即有su命令 Process p = Runtime.getRuntime().exec("su"); dos = new DataOutputStream(p.getOutputStream()); dis = new DataInputStream(p.getInputStream()); dos.writeBytes(cmd + "\n"); dos.flush(); dos.writeBytes("exit\n"); dos.flush(); String line = null; while ((line = dis.readLine()) != null) { Log.d("result", line); result += line; } p.waitFor(); } catch (Exception e) { e.printStackTrace(); } finally { if (dos != null) { try { dos.close(); } catch (IOException e) { e.printStackTrace(); } } if (dis != null) { try { dis.close(); } catch (IOException e) { e.printStackTrace(); } } } return result; }

被控制方需要ROOT权限才能通过java调用ADB命令来实现点击,滑动等操作

Demo下载

GitHub地址:https://github.com/zhuangguangkang0013/udpSend.git 发送端(控制方)

https://github.com/zhuangguangkang0013/udpReceive.git 接收端(被控制方)

限速云地址:https://pan.baidu.com/s/1x3_2YGtidTg3F9LsEmoeZA 提取码:6vm9

CSDN地址: https://download.csdn.net/download/u011046184/11150664

Demo效果图

新款加入了屏幕共享,滑动事件