利用wifi实现android手机远程遥控
本帖最后由 lynn3653 于 2014-10-10 16:06 编辑当前诸多电视盒都提供了手机端的远程遥控软件,如乐视的多屏看看等,那如果我们拥有一部android手机和一块android开发板(或者电视盒),我们也可轻松开发出一款远程遥控软件,因为主要的技术点仅仅以下两点而已:
1)Client端(android手机)Socket广播搜寻Server端(android开发板),获取Server端IP地址;
Client端使用socket 7777(端口号自行选用,不冲突即可)广播"is_remoteService ?"
socket_7777 = new DatagramSocket(7777);
Log.v("tag", "socket_7777");
InetAddress serverAddress = InetAddress.getByName("255.255.255.255");
String str = "is_remoteService ?";
byte data [] = str.getBytes();//把传输内容分解成字节
DatagramPacket packet = new DatagramPacket(data, data.length, serverAddress, 7777);
//调用socket对象的send方法,发送数据
socket_7777.send(packet);
进入循环等待Server端回复,获取Server端IP地址
//创建一个空的DatagramPacket对象
//DatagramPacket packet_re = new DatagramPacket(data,data.length);
//使用receive方法接收客户端所发送的数据,
//如果客户端没有发送数据,该进程就停滞在这里
while(true)
{
DatagramPacket packet_re = new DatagramPacket(data,data.length);
try {
socket_7777.setSoTimeout(3000);
Log.e("TAG", ">>>receive_packet_begin");
socket_7777.receive(packet_re);
Log.e("TAG", ">>>receive_packet_end");
String result = new String(packet_re.getData(),packet_re.getOffset(),packet_re.getLength());
Log.v("TAG", "result--->" + result);
String address = packet_re.getAddress().getHostAddress().toString();
Log.v("TAG", "address--->" + address);
if(result.indexOf("is_remoteService") == -1 && "is_remoteService".indexOf(result) == -1)
{
list.add(result);
listAddress.add(address);
}
} catch (IOException e) {
e.printStackTrace();
break;
}
}
Server端使用socket 7777接收"is_remoteService ?",然后获取IP地址并回复mDeviceName给Client端
byte[] buffer = new byte;
try {
mListenerSocket = new DatagramSocket(7777);
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
Log.e(TAG, "new DatagramSocket(7777) err!");
return;
}
while(mFlag){
try {
DatagramPacket packet = new DatagramPacket(buffer, buffer.length);
mListenerSocket.receive(packet);
InetAddress address = packet.getAddress();
System.out.println(address.getHostAddress() + " "+new String(packet.getData()));
String revStr = new String(packet.getData());
if(revStr.indexOf("is_remoteService ?")==0){
mDeviceName = android.os.Build.MODEL;
packet = new DatagramPacket(mDeviceName.getBytes(), 0, mDeviceName.getBytes().length, address, packet.getPort());
mListenerSocket.send(packet);
}else{
Log.e(TAG, "not is remoteServiec ! err!");
}
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
2)将Client端的虚拟按键事件转化成简单字符串用socket发送至Sever端,Server端解析字符串获得按键事件,并使用injectInputEvent方法向android上层上报按键事件;
Client 发起端口号9999的TCP连接
socket_9999 = new Socket(serverAddress_s, 9999);//serverAddress_s为第一步Server端回复中取得
try {
m9999Out = new PrintWriter(socket_9999.getOutputStream(), true);
} catch (IOException e1) {
// TODO Auto-generated catch block
e1.printStackTrace();
}
连接成功后即可使用m9999Out发送命令字符串
m9999Out.println(cmd_string);
Server端用端口号9999 accept TCP连接
try {
mWifiServerSocket = new ServerSocket(9999, 3);
} catch (IOException e1) {
// TODO Auto-generated catch block
e1.printStackTrace();
}
while(mFlag){
try {
Socket client = mWifiServerSocket.accept();
Remote newRemote = new Remote(client); //每接收到一个TCP连接都建立一个Remote节点单独处理
mRemotes.add(newRemote);
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
try {
Thread.sleep(100);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
private class Remote{
private Object mSocket;
private BufferedReader mIn = null;
private PrintWriter mOut = null;
private boolean mFlag = true;
Remote(Object socket){
mSocket = socket;
try {
mIn = new BufferedReader(new InputStreamReader(((Socket)mSocket).getInputStream()));
mOut = new PrintWriter(new OutputStreamWriter(((Socket)mSocket).getOutputStream()), true);
} catch (IOException e1) {
// TODO Auto-generated catch block
e1.printStackTrace();
}
ReadThread thread = new ReadThread(Remote.this, mIn, mOut);
thread.start();
}
public void release(){
mFlag = false;
try {
mIn.close();
mOut.close();
((Socket)mSocket).close();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
ReadThread中使用mIn阻塞接收Client端的命令字符串并解析
while(mFlag)
{
try {
String mrecvStr = mIn.readLine();
Log.d(TAG, "read: "+mrecvStr);
if(mrecvStr == null || mrecvStr == "null"){
Log.w(TAG, "null is received!");
mRemoteNode.release();
mRemotes.remove(mRemoteNode);
break;
}
String[] mstrs = mrecvStr.split("\n");
int i = 0;
while( i < mstrs.length)
{
recvStr = mstrs<i>;
</i>i++;
if (i >= 2)
{
Log.d(TAG, "Multicommand send in one time!");
}
if(recvStr.equalsIgnoreCase("online")){
String str = "online";
mOut.println(str);
}else{
String[] strs = recvStr.split(" ",5);
if(strs.length <= 1){
Log.e(TAG,"error cmd!");
continue;
}
if( strs.equalsIgnoreCase("key")) {
int keyCode = Integer.parseInt(strs);
VKey.sendKeyEventToAndroid(keyCode);
Log.d(TAG, "new sendKeyEvent keyCode:"+keyCode);
}else{
Log.w(TAG,"Unknow Command:"+recvStr);
}
}
}
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
mRemoteNode.release();
mRemotes.remove(mRemoteNode);
return;
}
}
将解析的指令转成KeyEvent 注入系统即完成整个遥控流程!
public class VKey {
public static boolean sendKeyEventToAndroid(int userKey) {
Log.d(TAG, "sendKeyEventToAndroid() start");
boolean result = false;
if (userKey != -1) {
//TODO: 以下代码必须依存系统源码编译
final KeyEvent evd = new KeyEvent(SystemClock.uptimeMillis(), SystemClock.uptimeMillis(), KeyEvent.ACTION_DOWN, userKey, 0,
0, KeyCharacterMap.VIRTUAL_KEYBOARD, 0,
KeyEvent.FLAG_FROM_SYSTEM | KeyEvent.FLAG_VIRTUAL_HARD_KEY,
InputDevice.SOURCE_KEYBOARD);
final KeyEvent evu = new KeyEvent(SystemClock.uptimeMillis(), SystemClock.uptimeMillis(), KeyEvent.ACTION_UP, userKey, 0,
0, KeyCharacterMap.VIRTUAL_KEYBOARD, 0,
KeyEvent.FLAG_FROM_SYSTEM | KeyEvent.FLAG_VIRTUAL_HARD_KEY,
InputDevice.SOURCE_KEYBOARD);
InputManager.getInstance().injectInputEvent(evd,
InputManager.INJECT_INPUT_EVENT_MODE_ASYNC);
InputManager.getInstance().injectInputEvent(evu,
InputManager.INJECT_INPUT_EVENT_MODE_ASYNC);
// mIWindowManager.injectKeyEvent(new KeyEvent(KeyEvent.ACTION_DOWN, key), false);
// mIWindowManager.injectKeyEvent(new KeyEvent(KeyEvent.ACTION_UP, key), false);
result = true;
}
Log.d(TAG, "sendKeyEventToAndroid() end");
return result;
}
笔者用三星的GT-I9308手机实际效果如下图:
当扫描到设备列表如笔者的firefly开发板"rk3288"后,点击“rk3288”即会有“已链接设备*.*.*.*”提示,然后点击“左”“右”按钮即可看到开发板有相应的响应!
附源码!
本帖最后由 zhoumushui 于 2014-10-9 19:15 编辑
赞一个。楼主可以把代码放到code标签里面,看起来方便一点儿~~
像这样:public class VKey {
public static boolean sendKeyEventToAndroid(int userKey) {
Log.d(TAG, "sendKeyEventToAndroid() start");
boolean result = false;
if (userKey != -1) {
//TODO: 以下代码必须依存系统源码编译
final KeyEvent evd = new KeyEvent(SystemClock.uptimeMillis(), SystemClock.uptimeMillis(), KeyEvent.ACTION_DOWN, userKey, 0,
0, KeyCharacterMap.VIRTUAL_KEYBOARD, 0,
KeyEvent.FLAG_FROM_SYSTEM | KeyEvent.FLAG_VIRTUAL_HARD_KEY,
InputDevice.SOURCE_KEYBOARD);
final KeyEvent evu = new KeyEvent(SystemClock.uptimeMillis(), SystemClock.uptimeMillis(), KeyEvent.ACTION_UP, userKey, 0,
0, KeyCharacterMap.VIRTUAL_KEYBOARD, 0,
KeyEvent.FLAG_FROM_SYSTEM | KeyEvent.FLAG_VIRTUAL_HARD_KEY,
InputDevice.SOURCE_KEYBOARD);
InputManager.getInstance().injectInputEvent(evd,
InputManager.INJECT_INPUT_EVENT_MODE_ASYNC);
InputManager.getInstance().injectInputEvent(evu,
InputManager.INJECT_INPUT_EVENT_MODE_ASYNC);
// mIWindowManager.injectKeyEvent(new KeyEvent(KeyEvent.ACTION_DOWN, key), false);
// mIWindowManager.injectKeyEvent(new KeyEvent(KeyEvent.ACTION_UP, key), false);
result = true;
}
Log.d(TAG, "sendKeyEventToAndroid() end");
return result;
}
不错。学习了! mark 学习参考一下 mark,支持下 mark,支持下 mark,支持下 zhoumushui 发表于 2014-10-9 19:01
**** 作者被禁止或删除 内容自动屏蔽 ****
了解一下,回头学习学习 mark,支持下 感谢分享
页:
[1]
2