無線通訊方案,有三種方案能夠實施:
一、NFC 二、藍牙 三、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:socket
break;
}
}
};ide
第二個:藍牙搜索到設備、綁定設備(配對)也是經過廣播接收的。(搜索到設備系統會自動發一個廣播)函數
// 註冊藍牙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);this
/**
* 定義接收藍牙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);.net
} 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流程是同樣的,只不過參數不一樣而已。以下:
一、建立客戶端藍牙Sokcet
二、建立鏈接
三、讀寫數據
四、關閉
服務端socket:
一、建立服務端藍牙Socket
二、綁定端口號(藍牙忽略)
三、建立監聽listen(藍牙忽略, 藍牙沒有此監聽,而是經過whlie(true)死循環來一直監聽的)
四、經過accept(),若是有客戶端鏈接,會建立一個新的Socket,體現出併發性,能夠同時與多個socket通信)
五、讀寫數據
六、關閉
下面看客戶端代碼:
/**
* <p>Title: ConnectThread</p >
* <p>Description: 客戶端邏輯: 客戶端的線程,處理客戶端socket</p >
* <p>Company: ihaveu</p >
*
* @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);
}
}
}
服務端代碼:
/**
* <p>Title: AccepThread</p >
* <p>Description: 服務端Socket經過accept()一直監聽客戶端鏈接的線程</p >
* <p>Company: ihaveu</p >
*
* @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);
}
}
}
下面看一個共同通信處理類:
/**
* <p>Title: ConnectedThread</p >
* <p>Description: 客戶端和服務端 處理 發送數據 和獲取數據</p >
* <p>Company: ihaveu</p >
*
* @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
/**
* <p>Title: ChatController</p >
* <p>Description: 聊天控制器</p >
* <p>Company: ihaveu</p >
*
* @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; } }