前言
BLE藍牙的興起主要因為近年來可穿戴設(shè)備的流行。由于傳統(tǒng)藍牙功耗高不能滿足可穿戴設(shè)備對于續(xù)航的要求。所以大部分可穿戴設(shè)備采用藍牙4.0,即BLE藍牙技術(shù)。BLE(Bluetooth Low Energy)低功耗藍牙,主要特點是快速搜索,快速連接,超低功耗保持連接和數(shù)據(jù)傳輸。
缺點:BLE藍牙數(shù)據(jù)傳輸速率低,特別是在安卓開發(fā)過程,BLE藍牙一包數(shù)據(jù)最多為20字節(jié),因此安卓系統(tǒng)下最好不要使用BLE藍牙傳輸大量數(shù)據(jù)。
藍牙開發(fā)的主要流程
(一)申請權(quán)限
安卓手機涉及藍牙權(quán)限問題,藍牙開發(fā)需要在AndroidManifest.xml文件中添加權(quá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" />
手機權(quán)限管理中允許此權(quán)限,否則會出現(xiàn)無法搜索到設(shè)備的情況。
(二)打開藍牙
在搜索設(shè)備之前需要詢問打開手機藍牙,其關(guān)鍵代碼如下:
//獲取系統(tǒng)藍牙適配器管理類
private BluetoothAdapter mBluetoothAdapter = BluetoothAdapter
.getDefaultAdapter();
// 詢問打開藍牙
if (mBluetoothAdapter != null && !mBluetoothAdapter.isEnabled()) {
Intent enableBtIntent = new Intent(
BluetoothAdapter.ACTION_REQUEST_ENABLE);
startActivityForResult(enableBtIntent, 1);
}
// 申請打開藍牙請求的回調(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, "藍牙已經(jīng)開啟", Toast.LENGTH_SHORT).show();
} else if (resultCode == RESULT_CANCELED) {
Toast.makeText(this, "沒有藍牙權(quán)限", Toast.LENGTH_SHORT).show();
finish();
}
}
}
(三)搜索設(shè)備
本文主要針對BLE藍牙開發(fā),因此采用mBluetoothAdapter.startLeScan(LeScanCallback callback)
方式掃描BLE藍牙設(shè)備。調(diào)用方法如下:
mBluetoothAdapter.startLeScan(callback);
private LeScanCallback callback = new LeScanCallback() {
@Override
public void onLeScan(BluetoothDevice device, int arg1, byte[] arg2) {
//device為掃描到的BLE設(shè)備
if(device.getName() == "目標設(shè)備名稱"){
//獲取目標設(shè)備
targetDevice = device;
}
}
};
(四)連接設(shè)備
通過掃描BLE設(shè)備,根據(jù)設(shè)備名稱區(qū)分出目標設(shè)備targetDevice,下一步實現(xiàn)與目標設(shè)備的連接。在連接設(shè)備之前要停止搜索藍牙。
mBluetoothAdapter.stopLeScan(callback);
注 :停止搜索一般需要一定的時間來完成,最好調(diào)用停止搜索函數(shù)之后加以100ms的延時,保證系統(tǒng)能夠完全停止搜索藍牙設(shè)備。停止搜索之后啟動連接過程。
BLE藍牙的連接方法相對簡單只需調(diào)用connectGatt
方法,函數(shù)原型如下:
public BluetoothGatt connectGatt (Context context, boolean autoConnect, BluetoothGattCallback callback);
參數(shù)說明
返回值 BluetoothGatt: BLE藍牙連接管理類,主要負責(zé)與設(shè)備進行通信。后續(xù)會進一步介紹該類。
boolean autoConnect:建議置為false,能夠提升連接速度。
BluetoothGattCallback callback 連接回調(diào),重要參數(shù),BLE通信的核心部分。
(五)設(shè)備通信
與設(shè)備建立連接之后與設(shè)備通信,整個通信過程都是在BluetoothGattCallback的異步回調(diào)函數(shù)中完成。
BluetoothGattCallback中主要回調(diào)函數(shù)如下:
private BluetoothGattCallback gattCallback = new BluetoothGattCallback() {
@Override
public void onConnectionStateChange(BluetoothGatt gatt, int status,
int newState) {
}
@Override
public void onCharacteristicWrite(BluetoothGatt gatt,
BluetoothGattCharacteristic characteristic, int status) {
super.onCharacteristicWrite(gatt, characteristic, status);
}
@Override
public void onDescriptorWrite(BluetoothGatt gatt,
BluetoothGattDescriptor descriptor, int status) {
};
@Override
public void onServicesDiscovered(BluetoothGatt gatt, int status) {
}
@Override
public void onCharacteristicChanged(BluetoothGatt gatt,
BluetoothGattCharacteristic characteristic) {
}
};
上述幾個回調(diào)函數(shù)是BLE開發(fā)中不可缺少的,每個函數(shù)的意義以及被調(diào)用的時機會在后續(xù)步驟中一一說明。
(1)等待設(shè)備連接成功
當(dāng)調(diào)用targetdDevice.connectGatt(context, false, gattCallback)
后系統(tǒng)會主動發(fā)起與BLE藍牙設(shè)備的連接,若成功連接到設(shè)備將回調(diào)onConnectionStateChange
方法,其處理過程如下:
@Override
public void onConnectionStateChange(BluetoothGatt gatt, int status,
int newState) {
if (newState == BluetoothGatt.STATE_CONNECTED) {
Log.e(TAG, "設(shè)備連接上 開始掃描服務(wù)");
// 開始掃描服務(wù),安卓藍牙開發(fā)重要步驟之一
mBluetoothGatt.discoverServices();
}
if (newState == BluetoothGatt.STATE_DISCONNECTED) {
// 連接斷開
/*連接斷開后的相應(yīng)處理*/
}
};
判斷newState == BluetoothGatt.STATE_CONNECTED
表明此時已經(jīng)成功連接到設(shè)備。
(2)開啟掃描服務(wù)
mBluetoothGatt.discoverServices();
掃描BLE設(shè)備服務(wù)是安卓系統(tǒng)中關(guān)于BLE藍牙開發(fā)的重要一步,一般在設(shè)備連接成功后調(diào)用,掃描到設(shè)備服務(wù)后回調(diào)onServicesDiscovered()
函數(shù),函數(shù)原型如下:。
@Override
public void onServicesDiscovered(BluetoothGatt gatt, int status) {
private List<BluetoothGattService> servicesList;
//獲取服務(wù)列表
servicesList = mBluetoothGatt.getServices();
}
BLE藍牙協(xié)議下數(shù)據(jù)的通信方式采用BluetoothGattService、BluetoothGattCharacteristic和BluetoothGattDescriptor三個主要的類實現(xiàn)通信。
BluetoothGattService 簡稱服務(wù),是構(gòu)成BLE設(shè)備協(xié)議棧的組成單位,一個藍牙設(shè)備協(xié)議棧一般由一個或者多個BluetoothGattService組成。
BluetoothGattCharacteristic 簡稱特征,一個服務(wù)包含一個或者多個特征,特征作為數(shù)據(jù)的基本單元。
一個BluetoothGattCharacteristic特征包含一個數(shù)據(jù)值和附加的關(guān)于特征的描述BluetoothGattDescriptor。
BluetoothGattDescriptor:用于描述特征的類,其同樣包含一個value值。
(3)獲取負責(zé)通信的BluetoothGattCharacteristic
BLE藍牙開發(fā)主要有負責(zé)通信的BluetoothGattService完成的。當(dāng)且稱為通信服務(wù)。通信服務(wù)通過硬件工程師提供的UUID獲取。獲取方式如下:
BluetoothGattService service = mBluetoothGatt.getService(UUID.fromString("藍牙模塊提供的負責(zé)通信UUID字符串"));
通信服務(wù)中包含負責(zé)讀寫的BluetoothGattCharacteristic,且分別稱為notifyCharacteristic和writeCharacteristic。其中notifyCharacteristic負責(zé)開啟監(jiān)聽,也就是啟動收數(shù)據(jù)的通道,writeCharacteristic負責(zé)寫入數(shù)據(jù)。
具體操作方式如下:
BluetoothGattService service = mBluetoothGatt.getService(UUID.fromString("藍牙模塊提供的負責(zé)通信服務(wù)UUID字符串"));
// 例如形式如:49535343-fe7d-4ae5-8fa9-9fafd205e455
notifyCharacteristic = service.getCharacteristic(UUID.fromString("notify uuid"));
writeCharacteristic = service.getCharacteristic(UUID.fromString("write uuid"));
(4)開啟監(jiān)聽
開啟監(jiān)聽,即建立與設(shè)備的通信的首發(fā)數(shù)據(jù)通道,BLE開發(fā)中只有當(dāng)上位機成功開啟監(jiān)聽后才能與下位機收發(fā)數(shù)據(jù)。開啟監(jiān)聽的方式如下:
mBluetoothGatt.setCharacteristicNotification(notifyCharacteristic, true)
BluetoothGattDescriptor descriptor = characteristic
.getDescriptor(UUID
.fromString
("00002902-0000-1000-8000-00805f9b34fb"));
descriptor.setValue(BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE);
若開啟監(jiān)聽成功則會回調(diào)BluetoothGattCallback中的onDescriptorWrite()
方法,處理方式如下:
@Override
public void onDescriptorWrite(BluetoothGatt gatt,
BluetoothGattDescriptor descriptor, int status) {
if (status == BluetoothGatt.GATT_SUCCESS) {
//開啟監(jiān)聽成功,可以像設(shè)備寫入命令了
Log.e(TAG, "開啟監(jiān)聽成功");
}
};
(5)寫入數(shù)據(jù)
監(jiān)聽成功后通過向 writeCharacteristic寫入數(shù)據(jù)實現(xiàn)與下位機的通信。寫入方式如下:
//value為上位機向下位機發(fā)送的指令
writeCharacteristic.setValue(value);
mBluetoothGatt.writeCharacteristic(writeCharacteristic)
其中:value一般為Hex格式指令,其內(nèi)容由設(shè)備通信的藍牙通信協(xié)議規(guī)定。
(6)接收數(shù)據(jù)
若寫入指令成功則回調(diào)BluetoothGattCallback中的onCharacteristicWrite()
方法,說明將數(shù)據(jù)已經(jīng)發(fā)送給下位機。
@Override
public void onCharacteristicWrite(BluetoothGatt gatt,
BluetoothGattCharacteristic characteristic, int status) {
if (status == BluetoothGatt.GATT_SUCCESS) {
Log.e(TAG, "發(fā)送成功");
}
super.onCharacteristicWrite(gatt, characteristic, status);
}
若發(fā)送的數(shù)據(jù)符合通信協(xié)議,則下位機會向上位機回復(fù)相應(yīng)的數(shù)據(jù)。發(fā)送的數(shù)據(jù)通過回調(diào)onCharacteristicChanged()
方法獲取,其處理方式如下:
@Override
public void onCharacteristicChanged(BluetoothGatt gatt,
BluetoothGattCharacteristic characteristic) {
// value為設(shè)備發(fā)送的數(shù)據(jù),根據(jù)數(shù)據(jù)協(xié)議進行解析
byte[] value = characteristic.getValue();
}
通過向下位機發(fā)送指令獲取下位機的回復(fù)數(shù)據(jù),即可完成與設(shè)備的通信過程。
(六)斷開連接
當(dāng)與設(shè)備完成通信之后之后一定要斷開與設(shè)備的連接。調(diào)用以下方法斷開與設(shè)備的連接:
mBluetoothGatt.disconnect();
mBluetoothGatt.close();
通過以上六個主要步驟即可實現(xiàn)與設(shè)備的通信流程,BLE藍牙開發(fā)流程相對固定,只需按照固定步驟執(zhí)行即可。若按照上述流程扔不能完成整個通信過程,可以查看另外一篇文章安卓藍牙開發(fā)填坑之路,看看是不是踩到坑了。
筆者能力有限,謝謝大家的支持。。。