開(kāi)發(fā)公司的智能鎖,通過(guò)藍(lán)牙控制网缝,是一種低功耗藍(lán)牙巨税,由于之前沒(méi)做過(guò),一路磕磕碰碰總算完成了粉臊,在此寫(xiě)下總結(jié)草添,幫助需要的朋友少走一些彎路,主要解決藍(lán)牙讀寫(xiě)數(shù)據(jù)和設(shè)置通知失敗的問(wèn)題扼仲。
開(kāi)發(fā)步驟:
- 檢測(cè)藍(lán)牙是否可用远寸,綁定藍(lán)牙服務(wù)
- 使用BluetoothAdapter.startLeScan來(lái)掃描低功耗藍(lán)牙設(shè)備
- 在掃描到設(shè)備的回調(diào)函數(shù)中會(huì)得到BluetoothDevice對(duì)象抄淑,并使用BluetoothAdapter.stopLeScan停止掃描
- 使用BluetoothDevice.connectGatt來(lái)獲取到BluetoothGatt對(duì)象
- 執(zhí)行BluetoothGatt.discoverServices,這個(gè)方法是異步操作驰后,在回調(diào)函數(shù)onServicesDiscovered中得到status肆资, 通過(guò)判斷status是否等于BluetoothGatt.GATT_SUCCESS來(lái)判斷查找Service是否成功
- 如果成功了,則通過(guò)BluetoothGatt.getService來(lái)獲取BluetoothGattService
- 接著通過(guò)BluetoothGattService.getCharacteristic獲取BluetoothGattCharacteristic
- 然后通過(guò)BluetoothGattCharacteristic.getDescriptor獲取BluetoothGattDescriptor
首先1-2步都是固定步驟灶芝,沒(méi)什么好說(shuō)的郑原,按照Android提供的Demo寫(xiě)就可以了,第3步掃描到設(shè)備會(huì)回調(diào)笙以,在回調(diào)中活烙,一般根據(jù)設(shè)備名稱找到想要連接的設(shè)備割岛,然后連接,獲取到BluetoothGatt對(duì)象酸役,然后在BluetoothGatt.discoverServices中會(huì)找到很多Services,根據(jù)硬件工程師提供的UUID連接你需要的Services驾胆;
//Ble服務(wù)發(fā)現(xiàn)回調(diào)
mBleService.setOnServicesDiscoveredListener(newBleService.OnServicesDiscoveredListener() {
@Override
public voidonServicesDiscovered(BluetoothGatt gatt,intstatus) {
List services = gatt.getServices();
for(BluetoothGattService service : services) {
if(service.getUuid().toString().equals(UUID_KEY_DATA)) {
LogUtil.d("藍(lán)牙服務(wù)找到了");
mReadCharacteristic= service.getCharacteristic(UUID_READ);
mWriteCharacteristic= service.getCharacteristic(UUID_WRITE);
break;
}
}
}
});
//設(shè)置通知
private BluetoothGatt mBluetoothGatt;
BluetoothGattCharacteristic characteristic;
boolean enabled;
...
mBluetoothGatt.setCharacteristicNotification(characteristic, enabled);
...
BluetoothGattDescriptor descriptor = characteristic.getDescriptor(
UUID.fromString(SampleGattAttributes.CLIENT_CHARACTERISTIC_CONFIG));
descriptor.setValue(BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE);
mBluetoothGatt.writeDescriptor(descriptor);
//寫(xiě)入
if(mBluetoothGatt!=null) {
characteristic.setValue(value);
returnmBluetoothGatt.writeCharacteristic(characteristic);
}
//讀取
mBluetoothGatt.readCharacteristic(characteristic);
讀寫(xiě)和設(shè)置通知有幾個(gè)重載的方法簇捍,這里就只列舉其中一種,可以根據(jù)需求來(lái)寫(xiě)俏拱。
找到Services后再根據(jù)硬件工程師提供的UUID找到讀寫(xiě)和通知的UUID暑塑,然后就能進(jìn)行讀寫(xiě)操作了。這里有一個(gè)地方需要注意9亍J赂瘛!讀寫(xiě)和設(shè)置通知都是單步操作搞隐,必須執(zhí)行完一個(gè)才能執(zhí)行第二個(gè)驹愚,否則會(huì)操作失敗。劣纲,之前我在這個(gè)地方糾結(jié)了很久逢捺,網(wǎng)上搜索了很久找不到答案,官方demo里面也沒(méi)有關(guān)于讀寫(xiě)的操作癞季,網(wǎng)上大部分寫(xiě)的都是仿照官方demo寫(xiě)的原理之類的劫瞳。通知打開(kāi)或關(guān)閉其實(shí)也是一次寫(xiě)入的操作,這個(gè)在官方demo中有绷柒。
后來(lái)我查看源碼志于,終于找到了解決的辦法。(還是要多看源碼废睦,少搜索伺绽,養(yǎng)成了習(xí)慣就不好了,依賴性強(qiáng),不會(huì)獨(dú)立解決問(wèn)題)在源碼BluetoothGatt類中奈应,有讀寫(xiě)和通知的具體代碼澜掩,都包含了下面這段代碼,所以判斷都是單步操作杖挣。
synchronized(mDeviceBusy) {
if(mDeviceBusy)return false;
mDeviceBusy=true;
}
通過(guò)搜索mDeviceBusy=false找到了解決的答案肩榕,在每次執(zhí)行寫(xiě)、讀程梦、通知操作后都會(huì)有一個(gè)是否成功的回調(diào)点把,在回調(diào)中設(shè)置了mDeviceBusy=false,所以在回調(diào)中可以繼續(xù)下一步操作了屿附;在BluetoothGattCallback 中可以重寫(xiě)以下回調(diào)郎逃,根據(jù)具體業(yè)務(wù)邏輯來(lái)重寫(xiě),都是異步操作挺份。
// 發(fā)現(xiàn)Services回調(diào)
@Override
public voidonServicesDiscovered(BluetoothGatt gatt,intstatus) {
if(mOnServicesDiscoveredListener!=null) {
mOnServicesDiscoveredListener.onServicesDiscovered(gatt, status);
}
if(status == BluetoothGatt.GATT_SUCCESS) {
broadcastUpdate(ACTION_GATT_SERVICES_DISCOVERED);
}else{
Log.w(TAG,"onServicesDiscovered received: "+ status);
}
}
// 讀操作回調(diào)
@Override
public voidonCharacteristicRead(BluetoothGatt gatt,
BluetoothGattCharacteristic characteristic,intstatus) {
if(mOnDataAvailableListener!=null) {
mOnDataAvailableListener.onCharacteristicRead(gatt, characteristic, status);
}
}
//寫(xiě)操作回調(diào)
@Override
public voidonCharacteristicWrite(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic,intstatus) {
super.onCharacteristicWrite(gatt, characteristic, status);
String address = gatt.getDevice().getAddress();
for(inti =0; i < characteristic.getValue().length; i++) {
Log.i(TAG,"address: "+ address +",Write: "+ characteristic.getValue()[i]);
}
if(mOnDataAvailableListener!=null) {
mOnDataAvailableListener.onCharacteristicWrite(gatt, characteristic,status);
}
}
//通知寫(xiě)入回調(diào)
@Override
public voidonDescriptorWrite(BluetoothGatt gatt, BluetoothGattDescriptor descriptor,
intstatus) {
if(mOnDataAvailableListener!=null) {
mOnDataAvailableListener.onDescriptorWrite(gatt, descriptor,status);
}
}
//通知收到數(shù)據(jù)的回調(diào)
@Override
public voidonCharacteristicChanged(BluetoothGatt gatt,
BluetoothGattCharacteristic characteristic) {
if(mOnDataAvailableListener!=null) {
mOnDataAvailableListener.onCharacteristicChanged(gatt, characteristic);
}
}
最后在BLE低功耗藍(lán)牙中最重要的三個(gè)操作完成了褒翰,剩下就是一些收尾工作,最后說(shuō)幾點(diǎn)需要注意的地方匀泊,釋放該釋放的資源避免內(nèi)存泄漏优训,這可以參考官方demo來(lái)寫(xiě);掃描到需要設(shè)備后記得停止掃描各聘;打開(kāi)通知后揣非,記得關(guān)閉通知,提高藍(lán)牙的性能躲因。
BLE官方demo
官方講解