前言
目前比較通用的藍(lán)牙類型主要有兩種,一種為手機(jī)端使用的經(jīng)典藍(lán)牙3.0,一種是當(dāng)前比較流行在穿戴設(shè)備上的低功耗藍(lán)牙,即常說的BLE藍(lán)牙。關(guān)于BLE藍(lán)牙開發(fā)可以查看另外一篇文章
安卓BLE藍(lán)牙開發(fā)詳解。
經(jīng)典藍(lán)牙采用服務(wù)器客戶端模式進(jìn)行通信,手機(jī)端既可以是服務(wù)端也可以是客戶端。
安卓手機(jī)經(jīng)典藍(lán)牙開發(fā)通信流程如下:
添加權(quán)限
<!-- 藍(lán)牙權(quán)限 -->
<uses-permission android:name="android.permission.BLUETOOTH" />
<uses-permission android:name="android.permission.BLUETOOTH_ADMIN" />
為適配安卓6.0以及以上版本需要添加一個模糊定位的權(quán)限
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
打開藍(lán)牙
// 詢問打開藍(lán)牙
if (mBluetoothAdapter != null && !mBluetoothAdapter.isEnabled()) {
Intent enableBtIntent = new Intent(
BluetoothAdapter.ACTION_REQUEST_ENABLE);
startActivityForResult(enableBtIntent, 1);
}
// 申請打開藍(lán)牙回調(diào)
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
// TODO Auto-generated method stub
super.onActivityResult(requestCode, resultCode, data);
if (requestCode == 1) {
if (resultCode == RESULT_OK) {
Toast.makeText(this, "藍(lán)牙已經(jīng)開啟", Toast.LENGTH_SHORT).show();
} else if (resultCode == RESULT_CANCELED) {
Toast.makeText(this, "沒有藍(lán)牙權(quán)限", Toast.LENGTH_SHORT).show();
finish();
}
}
}
BluetoothAdapter類封裝了關(guān)于安卓系統(tǒng)藍(lán)牙的操作。其獲取方式為:
BluetoothAdapter mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
if (mBluetoothAdapter == null) {
// 設(shè)備不支持藍(lán)牙
}
搜索藍(lán)牙設(shè)備
安卓對于經(jīng)典藍(lán)牙的搜索采用注冊廣播的方式實現(xiàn)。
// 注冊掃描監(jiān)聽器,一般寫在onCreate()函數(shù)中執(zhí)行
IntentFilter filter = new IntentFilter();
filter.addAction(BluetoothDevice.ACTION_FOUND);
registerReceiver(searchReceiver, filter);
// 監(jiān)聽器監(jiān)聽藍(lán)牙掃描結(jié)果
BroadcastReceiver searchReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
String action = intent.getAction();
BluetoothDevice device = intent
.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
if (BluetoothDevice.ACTION_FOUND.equals(action)) {
if (BluetoothDevice.ACTION_FOUND.equals(action)) {
if (device.getName() == null) {
return;
}
if(device.getName().equals("目標(biāo)設(shè)備藍(lán)牙名")) {
targetDevice = device;
}
}
}
}
};
//啟動搜索周邊設(shè)備
if (mBluetoothAdapter.isDiscovering()) {
mBluetoothAdapter.cancelDiscovery();
}
mBluetoothAdapter.startDiscovery();
注:當(dāng)注冊廣播后需要在銷毀界面時調(diào)用unregisterReceiver(searchReceiver);解除注冊防止內(nèi)存泄露。
連接設(shè)備
當(dāng)在廣播監(jiān)聽結(jié)果中獲取到目標(biāo)設(shè)備之后,進(jìn)行連接設(shè)備。安卓啟動連接的方式通常需啟動服務(wù)端線程作為監(jiān)聽,然后創(chuàng)建客戶端線程進(jìn)行連接。連接方法如下:
//啟動藍(lán)牙連接
public void connect(BluetoothDevice targetDevice) {
this.targetDevice = targetDevice;
//啟動服務(wù)端線程
serverThread = new ServerThread();
serverThread.start();
try {
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
//啟動客戶端線程
clientThread = new ClientThread();
clientThread.start();
}
// 服務(wù)端線程
private class ServerThread extends Thread {
public void run() {
try {
// 創(chuàng)建一個藍(lán)牙服務(wù)器 參數(shù)分別:服務(wù)器名稱、UUID
mserverSocket = mBluetoothAdapter
.listenUsingRfcommWithServiceRecord(
"btspp",
UUID.fromString("00001101-0000-1000-8000-00805F9B34FB"));
// 接受客戶端的連接請求
socket = mserverSocket.accept();
} catch (Exception e) {
e.printStackTrace();
}
}
}
// 客戶端線程
private class ClientThread extends Thread {
public void run() {
try {
Thread.sleep(500);
socket = targetDevice.createRfcommSocketToServiceRecord(UUID
.fromString("00001101-0000-1000-8000-00805F9B34FB"));
//開始連接
socket.connect();
//連接成功后,啟動數(shù)據(jù)通信線程
isReading = true;
new ReadThread().start();
} catch (Exception e) {
}
}
}
通信過程
安卓經(jīng)典藍(lán)牙的通信采用Socket通信機(jī)制。Socket通信線程如下:
private class ReadThread extends Thread {
public void run() {
Log.e("TAG", "開啟通信線程");
byte[] buffer = new byte[1024];
int bytes;
in = null;
try {
in = socket.getInputStream();
out = socket.getOutputStream();
} catch (Exception e) {
Log.e("TAG", "獲取流出錯");
}
while (isReading) {
try {
if ((bytes = in.read(buffer)) > 0) {
//讀取到的數(shù)據(jù)值
byte[] rece = new byte[bytes];
}
} catch (Exception e) {
e.printStackTrace();
try {
Log.e("關(guān)閉流", "");
in.close();
} catch (Exception e2) {
e2.printStackTrace();
}
break;
}
}
Log.e(TAG, "DataRead線程關(guān)閉");
}
}
Socket通信線程在沒有斷開連接之前一直在Socket流中讀取數(shù)據(jù)。通常在與設(shè)備通信過程中不僅要讀取設(shè)備數(shù)據(jù),同時需要向設(shè)備寫入數(shù)據(jù),發(fā)送數(shù)據(jù)的方法如下:
//發(fā)送數(shù)據(jù)方法
public void sendData(byte[] cmd) {
if (out != null) {
try {
out.write(cmd);
} catch (IOException e) {
Log.e(TAG, "寫入失敗");
e.printStackTrace();
}
}else {
Log.e(TAG, "建立連接失敗");
}
}
結(jié)語
通過以上流程可以基本完成與經(jīng)典藍(lán)牙設(shè)備的通信過程。本文采用的是手機(jī)端作為服務(wù)端,周邊藍(lán)牙設(shè)備作為客戶端的方式。若文章中存在錯誤,還請指出。