无线通信方案,有三种方案可以实施:
1、NFC 2、蓝牙 3、WIFI
下面是对这三个知识点做的一个总结,参照对比可以选择合适的方案。而本章着重讲的蓝牙之间通信。
首先介绍一下蓝牙的两个广播Receiver。
第一个:蓝牙状态的改变是通过广播接收到的。
// 注册蓝牙状态接收广播
IntentFilter intentFilter = new ntentFilter(BluetoothAdapter.ACTION_STATE_CHANGED);
registerReceiver(mReceiverBluetoothStatus,intentFilter);
/**
* 定义接收蓝牙状态广播receiver
*
* @param savedInstanceState
*/
private BroadcastReceiver mReceiverBluetoothStatus = new BroadcastReceiver() {
@Override
public void onReceive(Context context,Intent intent) {
int status = intent.getIntExtra(BluetoothAdapter.EXTRA_STATE,-1);
switch (status) {
case BluetoothAdapter.STATE_OFF:
Log.d(TAG,"蓝牙已关闭");
break;
case BluetoothAdapter.STATE_ON:
Log.d(TAG,"蓝牙已打开");
break;
case BluetoothAdapter.STATE_TURNING_OFF:
Log.d(TAG,"蓝牙关闭中...");
break;
case BluetoothAdapter.STATE_TURNING_ON:
Log.d(TAG,"蓝牙打开中...");
break;
default:
break;
}
}
};
第二个:蓝牙搜索到设备、绑定设备(配对)也是通过广播接收的。(搜索到设备系统会自动发一个广播)
// 注册蓝牙device接收广播
IntentFilter intentFilterDevice = new IntentFilter();
// 开始查找
intentFilterDevice.addAction(BluetoothAdapter.ACTION_DISCOVERY_STARTED);
// 结束查找
intentFilterDevice.addAction(BluetoothAdapter.ACTION_DISCOVERY_FINISHED);
// 查找设备(查找到设备)
intentFilterDevice.addAction(BluetoothDevice.ACTION_FOUND);
// 设备扫描模式改变 (自己状态的改变action,当设置可见或者不见时都会发送此广播)
intentFilterDevice.addAction(BluetoothAdapter.ACTION_SCAN_MODE_CHANGED);
// 绑定状态
intentFilterDevice.addAction(BluetoothDevice.ACTION_BOND_STATE_CHANGED);
registerReceiver(mReceiverDeceiver,intentFilterDevice);
/**
* 定义接收蓝牙device广播Receiver
*/
private BroadcastReceiver mReceiverDeceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context,Intent intent) {
String action = intent.getAction();
if (BluetoothAdapter.ACTION_DISCOVERY_STARTED.equals(action)) {
// 开始搜索 ——接收广播
Log.d(TAG,"开始搜索");
mList.clear();
mAdapter.refresh(mList);
} else if (BluetoothAdapter.ACTION_DISCOVERY_FINISHED.equals(action)) {
// 查找到设备完成 —— 接收广播
Log.d(TAG,"查找到设备完成");
} else if (BluetoothDevice.ACTION_FOUND.equals(action)) {
// 搜索到设备 —— 接收广播
Log.d(TAG,"搜索到设备");
BluetoothDevice device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
mList.add(device);
mAdapter.refresh(mList);
} else if (BluetoothAdapter.ACTION_SCAN_MODE_CHANGED.equals(action)) {
// 当自己设备设置蓝牙可见时或者不可见时 —— 接收广播
int scanMode = intent.getIntExtra(BluetoothAdapter.EXTRA_SCAN_MODE,0);
// 可见时
if (scanMode == BluetoothAdapter.SCAN_MODE_CONNECTABLE_DISCOVERABLE) {
Log.d(TAG,"设备可见监听");
} else {
Log.d(TAG,"设备不可见监听");
}
} else if (BluetoothDevice.ACTION_BOND_STATE_CHANGED.equals(action)) {
// 绑定状态改变回调
BluetoothDevice remoteDevice = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
if (remoteDevice == null) {
Log.d(TAG,"没有绑定设备");
return;
}
int status = intent.getIntExtra(BluetoothDevice.EXTRA_BOND_STATE,0);
if (status == BluetoothDevice.BOND_BONDED) {
Log.d(TAG,"绑定设备完成: " + remoteDevice.getName());
} else if (status == BluetoothDevice.BOND_BONDING) {
Log.d(TAG,"绑定设备中: " + remoteDevice.getName());
} else if (status == BluetoothDevice.BOND_NONE) {
Log.d(TAG,"取消绑定: ");
}
}
}
};
以上基本上就是蓝牙接收状态和搜索到设备,绑定设备一系列改变的操作!基本上已经很全了。下面介绍一个蓝牙通信的流程。
左为客户端Socket连接的一个流程:首先获取一个客户端Socket(),然后连接上,就可以读取数据和发送数据了。
右为服务端Socket操作流程:
蓝牙通信原理介绍:
蓝牙通信和socket通信原理基本上是一致的,下面我给大家上一张图(图为Socket通信图)。分析一下。
蓝牙客户端Socket的与Sokcet流程是一样的,只不过参数不同而已。如下:
1、创建客户端蓝牙Sokcet
2、创建连接
3、读写数据
4、关闭
服务端socket:
1、创建服务端蓝牙Socket
2、绑定端口号(蓝牙忽略)
3、创建监听listen(蓝牙忽略, 蓝牙没有此监听,而是通过whlie(true)死循环来一直监听的)
4、通过accept(),如果有客户端连接,会创建一个新的Socket,体现出并发性,可以同时与多个socket通讯)
5、读写数据
6、关闭
下面看客户端代码:
/**
* Title: ConnectThread
* Description: 客户端逻辑: 客户端的线程,处理客户端socket
* Company: ihaveu
*
* @author MaWei
* @date 2017/12/26
*/
public class ConnectThread extends Thread{
private static final UUID MY_UUID = UUID.fromString(Constant.CONNECTTION_UUID);
/** 客户端socket*/
private final BluetoothSocket mmSoket;
/** 要连接的设备*/
private final BluetoothDevice mmDevice;
private BluetoothAdapter mBluetoothAdapter;
/** 主线程通信的Handler*/
private final Handler mHandler;
/** 发送和接收数据的处理类*/
private ConnectedThread mConnectedThread;
public ConnectThread(BluetoothDevice device, BluetoothAdapter bluetoothAdapter, Handler mUIhandler) {
mmDevice = device;
mBluetoothAdapter = bluetoothAdapter;
mHandler = mUIhandler;
BluetoothSocket tmp = null;
try {
// 创建客户端Socket
tmp = device.createRfcommSocketToServiceRecord(MY_UUID);
} catch (IOException e) {
e.printStackTrace();
}
mmSoket = tmp;
}
@Override
public void run() {
super.run();
// 关闭正在发现设备.(如果此时又在查找设备,又在发送数据,会有冲突,影响传输效率)
mBluetoothAdapter.cancelDiscovery();
try {
// 连接服务器
mmSoket.connect();
} catch (IOException e) {
// 连接异常就关闭
try {
mmSoket.close();
} catch (IOException e1) {
}
return;
}
manageConnectedSocket(mmSoket);
}
private void manageConnectedSocket(BluetoothSocket mmSoket) {
// 通知主线程连接上了服务端socket,更新UI
mHandler.sendEmptyMessage(Constant.MSG_CONNECTED_TO_SERVER);
// 新建一个线程进行通讯,不然会发现线程堵塞
mConnectedThread = new ConnectedThread(mmSoket,mHandler);
mConnectedThread.start();
}
/**
* 关闭当前客户端
*/
public void cancle() {
try {
mmSoket.close();
} catch (IOException e) {
e.printStackTrace();
}
}
/**
* 发送数据
* @param data
*/
public void sendData(byte[] data) {
if(mConnectedThread != null) {
mConnectedThread.write(data);
}
}
}
服务端代码:
/**
* Title: AccepThread
* Description: 服务端Socket通过accept()一直监听客户端连接的线程
* Company: ihaveu
*
* @author MaWei
* @date 2017/12/26
*/
public class AccepThread extends Thread {
/** 连接的名称*/
private static final String NAME = "BluetoothClass";
/** UUID*/
private static final UUID MY_UUID = UUID.fromString(Constant.CONNECTTION_UUID);
/** 服务端蓝牙Sokcet*/
private final BluetoothServerSocket mmServerSocket;
private final BluetoothAdapter mBluetoothAdapter;
/** 线程中通信的更新UI的Handler*/
private final Handler mHandler;
/** 监听到有客户端连接,新建一个线程单独处理,不然在此线程中会堵塞*/
private ConnectedThread mConnectedThread;
public AccepThread(BluetoothAdapter adapter, Handler handler) throws IOException {
mBluetoothAdapter = adapter;
this.mHandler = handler;
// 获取服务端蓝牙socket
mmServerSocket = mBluetoothAdapter.listenUsingRfcommWithServiceRecord(NAME, MY_UUID);
}
@Override
public void run() {
super.run();
// 连接的客户端soacket
BluetoothSocket socket = null;
// 服务端是不退出的,要一直监听连接进来的客户端,所以是死循环
while (true){
// 通知主线程更新UI,客户端开始监听
mHandler.sendEmptyMessage(Constant.MSG_START_LISTENING);
try {
// 获取连接的客户端socket
socket = mmServerSocket.accept();
} catch (IOException e) {
// 通知主线程更新UI, 获取异常
mHandler.sendEmptyMessage(Constant.MSG_ERROR);
e.printStackTrace();
// 服务端退出一直监听线程
break;
}
if(socket != null) {
// 管理连接的客户端socket
manageConnectSocket(socket);
// 这里应该是手动断开,案例应该是只保证连接一个客户端,所以连接完以后,关闭了服务端socket
// try {
// mmServerSocket.close();
// mHandler.sendEmptyMessage(Constant.MSG_FINISH_LISTENING);
// } catch (IOException e) {
// e.printStackTrace();
// }
}
}
}
/**
* 管理连接的客户端socket
* @param socket
*/
private void manageConnectSocket(BluetoothSocket socket) {
// 只支持同时处理一个连接
// mConnectedThread不为空,踢掉之前的客户端
if(mConnectedThread != null) {
mConnectedThread.cancle();
}
// 主线程更新UI,连接到了一个客户端
mHandler.sendEmptyMessage(Constant.MSG_GOT_A_CLINET);
// 新建一个线程,处理客户端发来的数据
mConnectedThread = new ConnectedThread(socket, mHandler);
mConnectedThread.start();
}
/**
* 断开服务端,结束监听
*/
public void cancle() {
try {
mmServerSocket.close();
mHandler.sendEmptyMessage(Constant.MSG_FINISH_LISTENING);
} catch (IOException e) {
e.printStackTrace();
}
}
/**
* 发送数据
* @param data
*/
public void sendData(byte[] data){
if(mConnectedThread != null) {
mConnectedThread.write(data);
}
}
}
下面看一个共同通讯处理类:
/**
* Title: ConnectedThread
* Description: 客户端和服务端 处理 发送数据 和获取数据
* Company: ihaveu
*
* @author MaWei
* @date 2017/12/26
*/
public class ConnectedThread extends Thread{
/** 当前连接的客户端BluetoothSocket*/
private final BluetoothSocket mmSokcet;
/** 读取数据流*/
private final InputStream mmInputStream;
/** 发送数据流*/
private final OutputStream mmOutputStream;
/** 与主线程通信Handler*/
private Handler mHandler;
private String TAG = "ConnectedThread";
public ConnectedThread(BluetoothSocket socket,Handler handler) {
mmSokcet = socket;
mHandler = handler;
InputStream tmpIn = null;
OutputStream tmpOut = null;
try {
tmpIn = socket.getInputStream();
tmpOut = socket.getOutputStream();
} catch (IOException e) {
e.printStackTrace();
}
mmInputStream = tmpIn;
mmOutputStream = tmpOut;
}
@Override
public void run() {
super.run();
byte[] buffer = new byte[1024];
while (true) {
try {
// 读取数据
int bytes = mmInputStream.read(buffer);
if(bytes > 0) {
String data = new String(buffer,0,bytes,"utf-8");
// 把数据发送到主线程, 此处还可以用广播
Message message = mHandler.obtainMessage(Constant.MSG_GOT_DATA,data);
mHandler.sendMessage(message);
}
Log.d(TAG, "messge size :" + bytes);
} catch (IOException e) {
e.printStackTrace();
}
}
}
// 踢掉当前客户端
public void cancle() {
try {
mmSokcet.close();
} catch (IOException e) {
e.printStackTrace();
}
}
/**
* 服务端发送数据
* @param data
*/
public void write(byte[] data) {
try {
mmOutputStream.write(data);
} catch (IOException e) {
e.printStackTrace();
}
}
}
下面是自己写的一个聊天demo
/**
* Title: ChatController
* Description: 聊天控制器
* Company: ihaveu
*
* @author MaWei
* @date 2017/12/26
*/
public class ChatController {
/** 客户端的线程*/
private ConnectThread mConnectThread;
/** 服务端的线程*/
private AccepThread mAccepThread;
private ChatProtocol mProtocol = new ChatProtocol();
/**
* 网络协议的处理函数
*/
private class ChatProtocol implements ProtocoHandler<String>{
private static final String CHARSET_NAME = "utf-8";
/**
* 封包(发送数据)
* 把发送的数据变成 数组 2进制流
*/
@Override
public byte[] encodePackge(String data) {
if(data == null) {
return new byte[0];
}else {
try {
return data.getBytes(CHARSET_NAME);
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
return new byte[0];
}
}
}
/**
* 解包(接收处理数据)
* 把网络上数据变成自己想要的数据体
*/
@Override
public String decodePackage(byte[] netData) {
if(netData == null) {
return "";
}else {
try {
return new String(netData, CHARSET_NAME);
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
return "";
}
}
}
}
/**
* 与服务器连接进行聊天
*/
public void startChatWith(BluetoothDevice device,BluetoothAdapter adapter,Handler handler){
mConnectThread = new ConnectThread(device, adapter, handler);
mConnectThread.start();
}
/**
* 等待客户端来连接
* handler : 用来跟主线程通信,更新UI用的
*/
public void waitingForFriends(BluetoothAdapter adapter, Handler handler) {
try {
mAccepThread = new AccepThread(adapter,handler);
mAccepThread.start();
} catch (IOException e) {
e.printStackTrace();
}
}
/**
* 发出消息
*/
public void sendMessage(String msg){
// 封包
byte[] data = mProtocol.encodePackge(msg);
if(mConnectThread != null) {
mConnectThread.sendData(data);
}else if(mAccepThread != null) {
mAccepThread.sendData(data);
}
}
/**
* 网络数据解码
*/
public String decodeMessage(byte[] data){
return mProtocol.decodePackage(data);
}
/**
* 停止聊天
*/
public void stopChart(){
if(mConnectThread != null) {
mConnectThread.cancle();
}
if(mAccepThread != null) {
mAccepThread.cancle();
}
}
/**
* 以下是单例写法
*/
private static class ChatControlHolder{
private static ChatController mInstance = new ChatController();
}
public static ChatController getInstance(){
return ChatControlHolder.mInstance;
}
}
OK,完成,大家主要看上面的广播,和蓝牙sokcet流程,其他的根据自己业务要求写就OK