Android开发之无线遥控器
最近弄了一个UDP/TCP的小东西,主要需要实现的功能如下(服务器端):
1、基于局域网
2、服务器端网络接口为无线与有线
3、服务器端接收到客户端的数据需要模拟按键进行处理
4、开机自启动
5、使用UDP进行连接,TCP进行通讯
基于以上几点,我们开始分析:
1.需要获取当前的网络IP地址,这里枚举了本机所有的网络地址,只返回ipv4
public String getAddressIP() {
//检查网络是否连接
while (!isNetWorkConnected()) {
//等待网络连接
}
ip = getLocalIpAddress();
return ip;
}
public String getLocalIpAddress() {
String address = null;
try {
for (Enumeration<NetworkInterface> en = NetworkInterface.getNetworkInterfaces(); en.hasMoreElements();) {
NetworkInterface intf = en.nextElement();
for (Enumeration<InetAddress> enumIpAddr = intf.getInetAddresses(); enumIpAddr.hasMoreElements();) {
InetAddress inetAddress = enumIpAddr.nextElement();
if (!inetAddress.isLoopbackAddress()) {//127.0.0.1
address = inetAddress.getHostAddress().toString();
//ipV6
if(!address.contains("::")){
return address;
}
}
}
}
} catch (SocketException ex) {
Log.e("getIpAddress Exception", ex.toString());
}
return null;
}
private boolean isNetWorkConnected() {
// TODO Auto-generated method stub
try{
connectivity = (ConnectivityManager) getSystemService(Context.CONNECTIVITY_SERVICE);
if(connectivity != null){
netWorkinfo = connectivity.getActiveNetworkInfo();
if(netWorkinfo != null && netWorkinfo.isAvailable()){
if(netWorkinfo.getState() == NetworkInfo.State.CONNECTED){
isConnected = true;
return true;
}
}
}
}catch(Exception e){
Log.e("UdpService : ",e.toString());
return false;
}
return false;
}
2.获得IP之后,创建一个多播组
try {
while(ip == null){
ip = getAddressIP();
}
inetAddress = InetAddress.getByName(BROADCAST_IP);//多点广播地址组
multicastSocket = new MulticastSocket(BROADCAST_PORT);//多点广播套接字
multicastSocket.setTimeToLive(1);
multicastSocket.joinGroup(inetAddress);
} catch (UnknownHostException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
这里设置一组特殊网络地址作为多点广播地址,第一个多点广播地址都被看作是一个组,当客户端需要发送接收广播信息时,加入该组就可以了。
IP协议为多点广播提供这批特殊的IP地址,这些IP地址范围是224.0.0.0---239.255.255.255,其中224.0.0.0为系统自用。
下面BROADCAST_IP是自己声明的一个String类型的变量,其范围也是前面所说的IP范围,比如BROADCAST_IP="224.224.224.224"。
private static int BROADCAST_PORT = 1234;
private static int PORT = 4444;
private static String BROADCAST_IP = "224.0.0.1";
3.服务端开始发送本机IP地址广播,如果网络断开,则结束掉此线程,并设置标识
public class UDPBoardcastThread extends Thread {
public UDPBoardcastThread() {
this.start();
}
@Override
public void run() {
DatagramPacket dataPacket = null;
//将本机的IP地址放到数据包里
byte[] data = ip.getBytes();
dataPacket = new DatagramPacket(data, data.length, inetAddress, BROADCAST_PORT);
//判断是否中断连接了
while (isNetWorkConnected()) {
try {
multicastSocket.send(dataPacket);
Thread.sleep(5000);
Log.i("UDPService:","再次发送ip地址广播");
} catch (Exception e) {
e.printStackTrace();
}
}
isConnected = false;
Message msg = new Message();
msg.what = 0x0001;
mHandler01.sendMessage(msg);
}
}
4.新开一个线程,等待客户端连接,使用TCP进行通讯
new Thread() {
@Override
public void run() {
try {
//建立一个线程池,每次收到一个客户端,新开一个线程
mExecutorService = Executors.newCachedThreadPool();
Socket client = null;
mList.clear();
while (isConnected) {
client = server.accept();
//把客户端放入客户端集合中
if (!connectOrNot(client)) {
mList.add(client);
Log.i("UDPService","当前连接数:"+mList.size());
}
mExecutorService.execute(new Service(client));
}
//释放客户端
for(int i = 0 ; i < mList.size() ; i++)
mList.get(i).close();
} catch (IOException e) {
e.printStackTrace();
}
}
}.start();
5.新开一个客户端的线程,处理客户端发送过来的数据等
//客户端线程,组成线程池
class Service implements Runnable {
private Socket socket;
private BufferedReader in = null;
private String msg = ""; public Service(Socket socket) {
this.socket = socket;
} @Override
public void run() {
try {
in = new BufferedReader(new InputStreamReader(socket.getInputStream()));
//等待接收客户端发送的数据
while (isConnected) { if ((msg = in.readLine()) != null) { // 创建一个Instrumentation对象,调用inst对象的按键模拟方法
Instrumentation inst = new Instrumentation();
try{
int codeKey = Integer.parseInt(msg);
//codeKey对应键值参照KeyCodeTable.txt文件,在客户端中实现
inst.sendKeyDownUpSync(codeKey); //发送回执
this.sendmsg(socket);
}catch(Exception ex){
ex.printStackTrace();
} }
}
} catch (Exception e) {
e.printStackTrace();
}
} private void sendmsg(Socket socket2) {
// TODO Auto-generated method stub
PrintWriter pout = null; try {
pout = new PrintWriter(new BufferedWriter(
new OutputStreamWriter(socket2.getOutputStream())), true);
pout.println("I am ok");
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} } }
这里使用了Instrumentation()对象来模拟按键的处理,在实际使用中,效率还行,没有很严重的延时,若真有延时,感觉也是网络方面的。
使用了socket.getInputStream()与socket.getOutputStream()方法来进行socket数据的接收与发送
6.最后新开一个Handler对网络断开时进行处理,也可以监听系统网络变化的广播,有时间研究下service的生命周期
private Handler mHandler01 = new Handler(){
@Override
public void handleMessage(Message msg) {
// TODO Auto-generated method stub
super.handleMessage(msg);
switch(msg.what){
//连接失败
case 0x0001:
initData();
break;
}
}
};
7.开机自启动,继承BroadcastReceiver,监听系统开机广播就ok了,记得在AndroidManifest.xml文件中声明BOOT_COMPLETED属性
if(intent.getAction().equals("android.intent.action.BOOT_COMPLETED")){
Intent intent2 = new Intent(context, UdpService.class);
context.startService(intent2);
}
8.还有一个问题,如果我们就这样直接编译,输出apk到电视中,会出现权限不足的error,原因是apk不是系统应用,只有uid为system id才可以去模拟按键事件,所以在
AndroidManifest.xml中加上android:sharedUserId="android.uid.system",以及<uses-permission android:name="android.permission.INJECT_EVENTS" />
再编写Android.mk,最后在android源码中使用mm命令编译apk,这样就ok了。
服务器端的流程差不多是这样了,附上完整源码,包含服务器端与客户端Demo:
http://download.csdn.net/detail/u012062785/9684842
thread与runnable的区别:https://www.oschina.net/question/565065_86563
Android开发之无线遥控器的更多相关文章
- Android学习探索之Java 8 在Android 开发中的应用
前言: Java 8推出已经将近2年多了,引入很多革命性变化,加入了函数式编程的特征,使基于行为的编程成为可能,同时减化了各种设计模式的实现方式,是Java有史以来最重要的更新.但是Android上, ...
- Android 开发一定要看的15个实战项目
前言: 虽说网上有太多的Android课程,但是大多都是视频,有Android在线开发环境的几乎没有,但是对于学习Android的人来说拥有在线的Android开发环境是非常好的,可以随时动手操作学习 ...
- Android开发学习之路-关于Exception
Exception在Java中是表示异常的一个类.它是Throwable的子类. 而Exception的子类RuntimeException是一个特殊的异常类,在代码中不需要对此类进行throw,而是 ...
- Android开发学习之路-Android中使用RxJava
RxJava的核心内容很简单,就是进行异步操作.类似于Handler和AsyncTask的功能,但是在代码结构上不同. RxJava使用了观察者模式和建造者模式中的链式调用(类似于C#的LINQ). ...
- Android开发学习之路-记一次CSDN公开课
今天的CSDN公开课Android事件处理重难点快速掌握中老师讲到一个概念我觉得不正确. 原话是这样的:点击事件可以通过事件监听和回调两种方法实现. 我一听到之后我的表情是这样的: 这跟我学的看的都不 ...
- Android开发学习之路-RecyclerView滑动删除和拖动排序
Android开发学习之路-RecyclerView使用初探 Android开发学习之路-RecyclerView的Item自定义动画及DefaultItemAnimator源码分析 Android开 ...
- Android开发-之监听button点击事件
一.实现button点击事件的方法 实现button点击事件的监听方法有很多种,这里总结了常用的四种方法: 1.匿名内部类 2.外部类(独立类) 3.实现OnClickListener接口 4.添加X ...
- Android 开发环境在 Windows7 下的部署安装
Android SDK Android SDK 为 Android 应用的开发.测试和调试提了必要的API库和开发工具. ADT Bundle 下载 如果你是一个android 开发新手,推荐你下载使 ...
- Android开发之自定义的ListView(UITableViewController)
Android开发中的ListView, 顾名方法思义,就是表视图.表示图在iOS开发中就是TableView.两者虽然名称不一样,但是其使用方法,使用场景以及该控件的功能都极为相似,都是用来展示大量 ...
随机推荐
- C# 获取当前路径7种方法
//获取模块的完整路径. string path1 = System.Diagnostics.Process.GetCurrentProcess().MainModule.FileName; //获取 ...
- JS移动客户端--触屏滑动事件
移动端触屏滑动的效果其实就是图片轮播,在PC的页面上很好实现,绑定click和mouseover等事件来完成.但是在移动设备上,要实现这种轮播的效果,就需要用到核心的touch事件.处理touch事件 ...
- mysql 5.6 online ddl
innodb存储引擎实现online ddl的原理是在执行创建或删除操作的同时,将DML操作日志写入到一个缓存中,待完成索引创建后再重做应用到表上,以此达到数据的一致性,这个缓存大小由参数innodb ...
- C语言-Hello, world
你好, 世界 --1-- 语言的编写准备 1.1 C语言源文件的编译执行过程 1.2 常见文件的拓展名 1.3 常用的命令行指令 1.4 环境及运行方法 --2--编写代码 2.1练习 --3-- ...
- Android的学习第六章(布局一LinearLayout)
今天我们来说一下Android五大布局-LinearLayout布局(线性布局) 含义:线性布局,顾名思义,指的是整个Android布局中的控件摆放方式是以线性的方式摆放的, 主要作用:主要对整个界面 ...
- java如何修改java.library.path并且不重启jvm也能生效
先说一下需求吧, 目前在用JCEF实现java程序桌面版包装,源码中需要加载编译好的几个dll文件,而这些文件的路径必然是根据程序安装的路径而变化的,这就需要在程序运行的时候,去动态修改java.li ...
- Python爬网获取全国各地律师电话号
[本文出自天外归云的博客园] 从64365网站获取全国各地律师电话号,用到了python的lxml库进行对html页面内容的解析,对于xpath的获取和正确性校验,需要在火狐浏览器安装firebug和 ...
- js 倒计时(可自定义时间)
<html> <head> <title>js 倒计时</title> </head> <body> <div> & ...
- java split进行字符串分割
在java.lang包中有String.split()方法,返回是一个数组 我在应用中用到一些,给大家总结一下,仅供大家参考: 1.如果用"."作为分隔的话,必须是如下写法,Str ...
- lseek函数
所有打开的文件都有一个当前文件偏移量(current file offset),以下简称为 cfo.cfo 通常是一个非负整数,用于表明文件开始处到文件当前位置的字节数.读写操作通常开始于 cfo,并 ...