最近課上剛好需要做一個課程設計關于藍牙的就挑選了個藍牙聊天室,其實關鍵還是在于對藍牙API的了解
一.藍牙API
與藍牙開發主要的相關類是以下四個
- BluetoothAdapter
字面上則理解為藍牙適配器,打開藍牙,關閉藍牙,搜索設備,獲取藍牙Socket連接都是通過這個類來實現的。對應的方法就有- enable():用來打開藍牙,一般在5.0后的話則會出現彈框提示是否開啟藍牙,其他則沒有提示,也可以調用來進行彈窗打開
Intent intent =new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE);
startActivity(intent );
disable():關閉藍牙
getDefaultAdapter():獲取藍牙適配器BluetoothAdapter,只有這個方法來獲取
getAddress():獲取本地藍牙的MAC地址,這個則是每一個藍牙設備的唯一
getName():獲取本地藍牙的名稱
getRemoteDevice(String address):獲取藍牙地址獲取到遠程的設備
startDiscovery():開啟藍牙設備搜索
cancelDiscovery():關閉掃描
listenUsingRfcommWithServiceRecord(serverSocketName,UUID):獲取BluetoothServerSocket
-
BluetoothDevice
代表一個藍牙設備createRfcommSocketToServiceRecord(UUIDuuid):根據UUID創建并返回一個BluetoothSocket
getName():獲取藍牙設備名稱
getAddress():獲取藍牙的MAC地址
BluetoothServerSocket:類似于ServerSocket,方法都差不多,可以說藍牙之間的通訊跟Socket相似。這個相當于服務器Socket
accept():這個方法會阻塞,直到連接建立,用來監聽連接
close():關閉socket連接
-
BluetoothSocket:相當于客戶端Socket
connect():用來向服務器BluetoothServerSocket發起連接
getInputStream():獲取輸入流
getOutputStream():獲取輸出流
close():關閉連接
getRemoteDevice():獲取這個Socket連接的遠程設備
二.搜索藍牙設備
知道對應API后就可以進行對應的藍牙開發,這里以獲取藍牙設備為例子
1.獲取本地藍牙適配器
BluetoothAdapter
mAdapter= BluetoothAdapter.getDefaultAdapter();
2.打開藍牙
if(!mAdapter.isEnabled()){
//彈出對話框提示用戶是后打開
Intent enabler = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE);
startActivityForResult(enabler, REQUEST_ENABLE);
//不做提示,強行打開
// mAdapter.enable();
}
3.搜索設備
mAdapter.startDiscovery(); //開啟搜索
搜索設備的回調則需要通過注冊廣播的形式來獲取
//發現設備的Action
IntentFilter filter = new IntentFilter(BluetoothDevice.ACTION_FOUND);
registerReceiver(mReceiver, filter);
//設備搜索完畢的action
filter = new IntentFilter(BluetoothAdapter.ACTION_DISCOVERY_FINISHED);
registerReceiver(mReceiver, filter);
定義廣播
BroadcastReceiver mReceiver = new BroadcastReceiver() {
public void onReceive(Context context, Intent intent) {
String action = intent.getAction();
//當掃描到設備的時候
if (BluetoothDevice.ACTION_FOUND.equals(action)) {
// 獲取設備對象
BluetoothDevice device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
//提取強度信息
int rssi = intent.getExtras().getShort(BluetoothDevice.EXTRA_RSSI);
Log.e(TAG, device.getName() + "\n" + device.getAddress() + "\n強度:" + rssi);
} //搜索完成
else if (BluetoothAdapter.ACTION_DISCOVERY_FINISHED.equals(action)) {
Log.e(TAG, "onReceive: 搜索完成" );
}
}
};
之后就可以進行個人的一些操作
三.藍牙聊天的實現
要實現藍牙聊天則涉及到藍牙之間的傳輸通信,前面也說到了,這里肯定就是用到BluetoothServerSocket以及BluetoothSocket。
藍牙傳輸通信相當于服務器端與客戶端之間的通信,只不過不同是這里每一個藍牙設備本身自己既充當服務器端也充當客戶端,大致的關系就是
注意,這些連接都是阻塞式的,都要放在線程里去執行。
1.對于BluetoothServerSocket,建立一個AcceptThread,來監聽是否有設備發起連接
//連接等待線程
class AcceptThread extends Thread{
private final BluetoothServerSocket serverSocket;
public AcceptThread(){
BluetoothServerSocket tmp = null;
try {
//獲取實例
tmp = bluetoothAdapter.listenUsingRfcommWithServiceRecord(NAME_SECURE, MY_UUID_SECURE);
} catch (IOException e) {
e.printStackTrace();
}
serverSocket = tmp;
}
@Override
public void run() {
super.run();
//監聽是否有端口連接
BluetoothSocket socket = null;
while(mState != STATE_TRANSFER) {
try {
Log.e(TAG, "run: AcceptThread 阻塞調用,等待連接");
socket = serverSocket.accept();
} catch (IOException e) {
e.printStackTrace();
Log.e(TAG, "run: ActivityThread fail");
break;
}
//獲取到連接Socket后則開始通信
if(socket != null){
synchronized (BluetoothChatService.this) {
switch (mState) {
case STATE_LISTEN:
case STATE_CONNECTING:
//傳輸數據,服務器端調用
Log.e(TAG, "run: 服務器AcceptThread傳輸" );
sendMessageToUi(MainActivity.BLUE_TOOTH_DIALOG , "正在與" + socket.getRemoteDevice().getName() + "連接");
//開始數據傳輸
dataTransfer(socket, socket.getRemoteDevice());
break;
case STATE_NONE:
case STATE_TRANSFER:
// 沒有準備好或者終止連接
try {
socket.close();
} catch (IOException e) {
Log.e(TAG, "Could not close unwanted socket" + e);
}
break;
}
}
}
}
}
public void cancel(){
Log.e(TAG, "close: activity Thread" );
try {
if(serverSocket != null)
serverSocket.close();
} catch (IOException e) {
e.printStackTrace();
Log.e(TAG, "close: activity Thread fail");
}
}
}
可以看到,當BluetoothServerSocket監聽到有設備連接的時候,就會調用dataTransfer開啟一個數據傳輸。
2.同樣如何發起連接BluetoothSocket呢
需要一個ConnectThread來發起
class ConnectThread extends Thread{
private final BluetoothSocket socket;
private final BluetoothDevice device;
public ConnectThread(BluetoothDevice device) {
this.device = device;
BluetoothSocket mSocket = null;
try {
//獲取Socket
mSocket = device.createRfcommSocketToServiceRecord(
MY_UUID_SECURE);
} catch (IOException e) {
e.printStackTrace();
Log.e(TAG, "ConnectThread: fail" );
sendMessageToUi(MainActivity.BLUE_TOOTH_TOAST , "連接失敗,請重新連接");
}
socket = mSocket;
}
@Override
public void run() {
super.run();
//建立后取消掃描
bluetoothAdapter.cancelDiscovery();
try {
//開啟連接
socket.connect();
} catch (IOException e) {
e.printStackTrace();
try {
socket.close();
} catch (IOException e1) {
e1.printStackTrace();
Log.e(TAG, "run: unable to close" );
}
//TODO 連接失敗顯示
sendMessageToUi(MainActivity.BLUE_TOOTH_TOAST , "連接失敗,請重新連接");
BluetoothChatService.this.start();
}
// 重置
synchronized (BluetoothChatService.this) {
mConnectThread = null;
}
//連接建立,開始傳輸
dataTransfer(socket, device);
}
public void cancel(){
try {
if(socket != null)
socket.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
之后建立連接之后就會調用dataTransfer來進行數據傳輸,同樣也需要一個線程來維護數據傳輸
class TransferThread extends Thread{
private final BluetoothSocket socket;
private final OutputStream out;
private final InputStream in;
public TransferThread(BluetoothSocket mBluetoothSocket){
socket = mBluetoothSocket;
OutputStream mOutputStream = null;
InputStream mInputStream = null;
try {
if(socket != null){
//獲取連接的輸入輸出流
mOutputStream = socket.getOutputStream();
mInputStream = socket.getInputStream();
}
} catch (IOException e) {
e.printStackTrace();
}
out = mOutputStream;
in = mInputStream;
isTransferError = false;
}
@Override
public void run() {
super.run();
//讀取數據
byte[] buffer = new byte[1024];
int bytes;
while (true){
try {
bytes = in.read(buffer);
//TODO 分發到主線程顯示
uiHandler.obtainMessage(MainActivity.BLUE_TOOTH_READ, bytes, -1, buffer)
.sendToTarget();
Log.e(TAG, "run: read " + new String(buffer , 0 , bytes) );
} catch (IOException e) {
e.printStackTrace();
Log.e(TAG, "run: Transform error" + e.toString());
BluetoothChatService.this.start();
//TODO 連接丟失顯示并重新開始連接
sendMessageToUi(MainActivity.BLUE_TOOTH_TOAST , "設備連接失敗/傳輸關閉");
isTransferError = true;
break;
}
}
}
/**
* 寫入數據傳輸
* @param buffer
*/
public void write(byte[] buffer) {
try {
out.write(buffer);
//TODO 到到UI顯示
uiHandler.obtainMessage(MainActivity.BLUE_TOOTH_WRAITE , -1, -1, buffer)
.sendToTarget();
} catch (IOException e) {
Log.e(TAG, "Exception during write " + e);
}
}
public void cancel() {
try {
if(socket != null)
socket.close();
} catch (IOException e) {
Log.e(TAG, "close() of connect socket failed" + e);
}
}
}
藍牙聊天則是基于上面三個線程來進行實現,同樣,對于藍牙文件間的傳輸也是同個道理,通過輸入輸出流來進行處理。之后的操作就比較容易處理了