Android-Ble蓝牙通讯开发–扫描,连接,发送和接收数据,分包解包(附源码)
前言
萬(wàn)物互聯(lián)的物聯(lián)網(wǎng)時(shí)代的已經(jīng)來(lái)臨,ble藍(lán)牙開(kāi)發(fā)在其中扮演著舉重若輕的角色。最近剛好閑一點(diǎn),抽時(shí)間梳理下這塊的知識(shí)點(diǎn)。
涉及ble藍(lán)牙通訊的客戶(hù)端(開(kāi)啟、掃描、連接、發(fā)送和接收數(shù)據(jù)、分包解包)和服務(wù)端(初始化廣播數(shù)據(jù)、開(kāi)始廣播、配置Services、Server回調(diào)操作)整個(gè)環(huán)節(jié)以及一些常見(jiàn)的問(wèn)題即踩過(guò)的一些坑。
比如
1、在Android不同版本或不同手機(jī)的適配問(wèn)題,掃描不到藍(lán)牙設(shè)備
2、如何避免ble藍(lán)牙連接出現(xiàn)133錯(cuò)誤?
3、單次寫(xiě)的數(shù)據(jù)大小有20字節(jié)限制,如何發(fā)送長(zhǎng)數(shù)據(jù)
藍(lán)牙有傳統(tǒng)(經(jīng)典)藍(lán)牙和低功耗藍(lán)牙BLE(Bluetooth Low Energy)之分,兩者的開(kāi)發(fā)的API不一樣,本文主講Ble藍(lán)牙開(kāi)發(fā),傳統(tǒng)藍(lán)牙不展開(kāi),有需要的可以自行了解。
相對(duì)傳統(tǒng)藍(lán)牙,BLE低功耗藍(lán)牙,主要特點(diǎn)是快速搜索,快速連接,超低功耗保持連接和數(shù)據(jù)傳輸。
Demo效果展示
客戶(hù)端
服務(wù)端
API
Android4.3(API Level 18)開(kāi)始引入BLE的核心功能并提供了相應(yīng)的 API。應(yīng)用程序通過(guò)這些 API 掃描藍(lán)牙設(shè)備、查詢(xún) services、讀寫(xiě)設(shè)備的 characteristics(屬性特征)等操作。
BLE藍(lán)牙協(xié)議是GATT協(xié)議, BLE相關(guān)類(lèi)不多, 全都位于android.bluetooth包和android.bluetooth.le包的幾個(gè)類(lèi):
android.bluetooth.
.BluetoothGattService 包含多個(gè)Characteristic(屬性特征值), 含有唯一的UUID作為標(biāo)識(shí)
.BluetoothGattCharacteristic 包含單個(gè)值和多個(gè)Descriptor, 含有唯一的UUID作為標(biāo)識(shí)
.BluetoothGattDescriptor 對(duì)Characteristic進(jìn)行描述, 含有唯一的UUID作為標(biāo)識(shí)
.BluetoothGatt 客戶(hù)端相關(guān)
.BluetoothGattCallback 客戶(hù)端連接回調(diào)
.BluetoothGattServer 服務(wù)端相關(guān)
.BluetoothGattServerCallback 服務(wù)端連接回調(diào)
android.bluetooth.le.
.AdvertiseCallback 服務(wù)端的廣播回調(diào)
.AdvertiseData 服務(wù)端的廣播數(shù)據(jù)
.AdvertiseSettings 服務(wù)端的廣播設(shè)置
.BluetoothLeAdvertiser 服務(wù)端的廣播
.BluetoothLeScanner 客戶(hù)端掃描相關(guān)(Android5.0新增)
.ScanCallback 客戶(hù)端掃描回調(diào)
.ScanFilter 客戶(hù)端掃描過(guò)濾
.ScanRecord 客戶(hù)端掃描結(jié)果的廣播數(shù)據(jù)
.ScanResult 客戶(hù)端掃描結(jié)果
.ScanSettings 客戶(hù)端掃描設(shè)置
BLE設(shè)備分為兩種設(shè)備: 客戶(hù)端(也叫主機(jī)/中心設(shè)備/Central), 服務(wù)端(也叫從機(jī)/外圍設(shè)備/peripheral)
客戶(hù)端的核心類(lèi)是 BluetoothGatt
服務(wù)端的核心類(lèi)是 BluetoothGattServer 和 BluetoothLeAdvertiser
BLE數(shù)據(jù)的核心類(lèi)是 BluetoothGattCharacteristic 和 BluetoothGattDescriptor
下面詳細(xì)講解下客戶(hù)端和服務(wù)端的開(kāi)發(fā)步驟流程
一、BLE客戶(hù)端開(kāi)發(fā)流程
1、申請(qǐng)權(quán)限
安卓手機(jī)涉及藍(lán)牙權(quán)限問(wèn)題,藍(lán)牙開(kāi)發(fā)需要在AndroidManifest.xml文件中添加權(quán)限聲明:
<!-- 藍(lán)牙權(quán)限 --> <uses-permission android:name="android.permission.BLUETOOTH" /> <uses-permission android:name="android.permission.BLUETOOTH_ADMIN" /> 為適配安卓6.0以及以上版本需要添加一個(gè)模糊定位的權(quán)限<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" /> 手機(jī)權(quán)限管理中允許此權(quán)限,否則會(huì)出現(xiàn)無(wú)法搜索到設(shè)備的情況;BLE權(quán)限增加了BEL支持檢查 (1).在manifest中添加權(quán)限 <!-- true 表示手機(jī)必須支持BLE,否則無(wú)法安裝!這里設(shè)為false, 運(yùn)行后在Activity中檢查--> <uses-featureandroid:name="android.hardware.bluetooth_le"android:required="false" />(2).在Activity中設(shè)置藍(lán)牙 // 檢查是否支持BLE藍(lán)牙 if (!getPackageManager().hasSystemFeature(PackageManager.FEATURE_BLUETOOTH_LE)) {Util.toast(this, "本機(jī)不支持低功耗藍(lán)牙!");finish();return; }2、打開(kāi)藍(lán)牙
在搜索設(shè)備之前需要詢(xún)問(wèn)打開(kāi)手機(jī)藍(lán)牙:
//獲取系統(tǒng)藍(lán)牙適配器管理類(lèi) private BluetoothAdapter mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter(); // 詢(xún)問(wèn)打開(kāi)藍(lán)牙 if (mBluetoothAdapter != null && !mBluetoothAdapter.isEnabled()) {Intent enableBtIntent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE);startActivityForResult(enableBtIntent, 1); }// 申請(qǐng)打開(kāi)藍(lán)牙請(qǐng)求的回調(diào) @Override protected void onActivityResult(int requestCode, int resultCode, Intent data) {// TODO Auto-generated method stubsuper.onActivityResult(requestCode, resultCode, data);if (requestCode == 1) {if (resultCode == RESULT_OK) {Toast.makeText(this, "藍(lán)牙已經(jīng)開(kāi)啟", Toast.LENGTH_SHORT).show();} else if (resultCode == RESULT_CANCELED) {Toast.makeText(this, "沒(méi)有藍(lán)牙權(quán)限", Toast.LENGTH_SHORT).show();finish();}} }3、搜索設(shè)備
注意: BLE設(shè)備地址是動(dòng)態(tài)變化(每隔一段時(shí)間都會(huì)變化),而經(jīng)典藍(lán)牙設(shè)備是出廠就固定不變了!
BluetoothAdapter bluetoothAdapter = BluetoothAdapter.getDefaultAdapter(); // 下面使用Android5.0新增的掃描API,掃描返回的結(jié)果更友好,比如BLE廣播數(shù)據(jù)以前是byte[] scanRecord,而新API幫我們解析成ScanRecord類(lèi)\ final BluetoothLeScanner bluetoothLeScanner = bluetoothAdapter.getBluetoothLeScanner(); bluetoothLeScanner.startScan(mScanCallback); mHandler.postDelayed(new Runnable() {@Overridepublic void run() {bluetoothLeScanner.stopScan(mScanCallback); //停止掃描isScanning = false;} }, 3000);// 掃描結(jié)果Callback private final ScanCallback mScanCallback = new ScanCallback() {@Overridepublic void onScanResult(int callbackType, ScanResult result) {、BluetoothDevice dev = result.getDevice() 獲取BLE設(shè)備信息// result.getScanRecord() 獲取BLE廣播數(shù)據(jù)} };// 舊API是BluetoothAdapter.startLeScan(LeScanCallback callback)方式掃描BLE藍(lán)牙設(shè)備,如下: mBluetoothAdapter.startLeScan(callback); private LeScanCallback callback = new LeScanCallback() {@Overridepublic void onLeScan(BluetoothDevice device, int arg1, byte[] arg2) {//device為掃描到的BLE設(shè)備if(device.getName() == "目標(biāo)設(shè)備名稱(chēng)"){//獲取目標(biāo)設(shè)備targetDevice = device;}} };4、連接設(shè)備
通過(guò)掃描BLE設(shè)備,根據(jù)設(shè)備名稱(chēng)區(qū)分出目標(biāo)設(shè)備targetDevice,下一步實(shí)現(xiàn)與目標(biāo)設(shè)備的連接,在連接設(shè)備之前要停止搜索藍(lán)牙;停止搜索一般需要一定的時(shí)間來(lái)完成,最好調(diào)用停止搜索函數(shù)之后加以100ms的延時(shí),保證系統(tǒng)能夠完全停止搜索藍(lán)牙設(shè)備。停止搜索之后啟動(dòng)連接過(guò)程;
BLE藍(lán)牙的連接方法相對(duì)簡(jiǎn)單只需調(diào)用connectGatt方法;
public BluetoothGatt connectGatt (Context context, boolean autoConnect, BluetoothGattCallback callback);參數(shù)說(shuō)明
- 返回值 BluetoothGatt: BLE藍(lán)牙連接管理類(lèi),主要負(fù)責(zé)與設(shè)備進(jìn)行通信;
- boolean autoConnect:建議置為false,能夠提升連接速度;
- BluetoothGattCallback callback 連接回調(diào),重要參數(shù),BLE通信的核心部分;
5、設(shè)備通信
與設(shè)備建立連接之后與設(shè)備通信,整個(gè)通信過(guò)程都是在BluetoothGattCallback的異步回調(diào)函數(shù)中完成;
BluetoothGattCallback中主要回調(diào)函數(shù)如下:
private BluetoothGattCallback gattCallback = new BluetoothGattCallback() {@Overridepublic void onConnectionStateChange(BluetoothGatt gatt, int status,int newState) {//連接狀態(tài)改變的Callback}@Overridepublic void onServicesDiscovered(BluetoothGatt gatt, int status) {//服務(wù)發(fā)現(xiàn)成功的Callback}@Overridepublic void onCharacteristicWrite(BluetoothGatt gatt,BluetoothGattCharacteristic characteristic, int status) {//寫(xiě)入Characteristic}@Overridepublic void onCharacteristicRead(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, int status) {//讀取Characteristic }@Overridepublic void onCharacteristicChanged(BluetoothGatt gatt,BluetoothGattCharacteristic characteristic) {//通知Characteristic}@Overridepublic void onDescriptorWrite(BluetoothGatt gatt, BluetoothGattDescriptor descriptor, int status) {//寫(xiě)入Descriptor}@Overridepublic void onDescriptorRead(BluetoothGatt gatt, BluetoothGattDescriptor descriptor, int status) {//讀取Descriptor}};上述幾個(gè)回調(diào)函數(shù)是BLE開(kāi)發(fā)中不可缺少的;
6、等待設(shè)備連接成功
當(dāng)調(diào)用targetdDevice.connectGatt(context, false, gattCallback)后系統(tǒng)會(huì)主動(dòng)發(fā)起與BLE藍(lán)牙設(shè)備的連接,若成功連接到設(shè)備將回調(diào)onConnectionStateChange方法,其處理過(guò)程如下:
@Override public void onConnectionStateChange(BluetoothGatt gatt, int status,int newState) {if (newState == BluetoothGatt.STATE_CONNECTED) {Log.e(TAG, "設(shè)備連接上 開(kāi)始掃描服務(wù)");// 連接成功后,開(kāi)始掃描服務(wù)mBluetoothGatt.discoverServices();}if (newState == BluetoothGatt.STATE_DISCONNECTED) {// 連接斷開(kāi)/*連接斷開(kāi)后的相應(yīng)處理*/ } };判斷newState == BluetoothGatt.STATE_CONNECTED表明此時(shí)已經(jīng)成功連接到設(shè)備;
7、開(kāi)啟掃描服務(wù)
mBluetoothGatt.discoverServices();
掃描BLE設(shè)備服務(wù)是安卓系統(tǒng)中關(guān)于BLE藍(lán)牙開(kāi)發(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藍(lán)牙協(xié)議下數(shù)據(jù)的通信方式采用BluetoothGattService、BluetoothGattCharacteristic和BluetoothGattDescriptor三個(gè)主要的類(lèi)實(shí)現(xiàn)通信;
- BluetoothGattService 簡(jiǎn)稱(chēng)服務(wù),是構(gòu)成BLE設(shè)備協(xié)議棧的組成單位,一個(gè)藍(lán)牙設(shè)備協(xié)議棧一般由一個(gè)或者多個(gè)BluetoothGattService組成;
- BluetoothGattCharacteristic 簡(jiǎn)稱(chēng)特征,一個(gè)服務(wù)包含一個(gè)或者多個(gè)特征,特征作為數(shù)據(jù)的基本單元;
- 一個(gè)BluetoothGattCharacteristic特征包含一個(gè)數(shù)據(jù)值和附加的關(guān)于特征的描述;
- BluetoothGattDescriptor:用于描述特征的類(lèi),其同樣包含一個(gè)value值;
8、獲取負(fù)責(zé)通信的BluetoothGattCharacteristic
BLE藍(lán)牙開(kāi)發(fā)主要有負(fù)責(zé)通信的BluetoothGattService完成的。當(dāng)且稱(chēng)為通信服務(wù)。通信服務(wù)通過(guò)硬件工程師提供的UUID獲取。獲取方式如下:
- BluetoothGattService service = mBluetoothGatt.getService(UUID.fromString(“藍(lán)牙模塊提供的負(fù)責(zé)通信UUID字符串”));
- 通信服務(wù)中包含負(fù)責(zé)讀寫(xiě)的BluetoothGattCharacteristic,且分別稱(chēng)為notifyCharacteristic和writeCharacteristic。其中notifyCharacteristic負(fù)責(zé)開(kāi)啟監(jiān)聽(tīng),也就是啟動(dòng)收數(shù)據(jù)的通道,writeCharacteristic負(fù)責(zé)寫(xiě)入數(shù)據(jù);
具體操作方式如下:
BluetoothGattService service = mBluetoothGatt.getService(UUID.fromString("藍(lán)牙模塊提供的負(fù)責(zé)通信服務(wù)UUID字符串"));// 例如形式如:49535343-fe7d-4ae5-8fa9-9fafd205e455notifyCharacteristic = service.getCharacteristic(UUID.fromString("notify uuid"));writeCharacteristic = service.getCharacteristic(UUID.fromString("write uuid"));9、開(kāi)啟監(jiān)聽(tīng)
開(kāi)啟監(jiān)聽(tīng),即建立與設(shè)備的通信的首發(fā)數(shù)據(jù)通道,BLE開(kāi)發(fā)中只有當(dāng)客戶(hù)端成功開(kāi)啟監(jiān)聽(tīng)后才能與服務(wù)端收發(fā)數(shù)據(jù)。開(kāi)啟監(jiān)聽(tīng)的方式如下:
mBluetoothGatt.setCharacteristicNotification(notifyCharacteristic, true) BluetoothGattDescriptor descriptor = characteristic .getDescriptor(UUID.fromString("00002902-0000-1000-8000-00805f9b34fb")); descriptor.setValue(BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE); //若開(kāi)啟監(jiān)聽(tīng)成功則會(huì)回調(diào)BluetoothGattCallback中的onDescriptorWrite()方法,處理方式如下: @Override public void onDescriptorWrite(BluetoothGatt gatt, BluetoothGattDescriptor descriptor, int status) {if (status == BluetoothGatt.GATT_SUCCESS) {//開(kāi)啟監(jiān)聽(tīng)成功,可以向設(shè)備寫(xiě)入命令了Log.e(TAG, "開(kāi)啟監(jiān)聽(tīng)成功");} };10、寫(xiě)入數(shù)據(jù)
BLE單次寫(xiě)的數(shù)據(jù)量大小是有限制的,通常是20字節(jié),可以嘗試通過(guò)requestMTU增大,但不保證能成功。分包寫(xiě)是一種解決方案,需要定義分包協(xié)議,假設(shè)每個(gè)包大小20字節(jié),分兩種包,數(shù)據(jù)包和非數(shù)據(jù)包。對(duì)于數(shù)據(jù)包,頭兩個(gè)字節(jié)表示包的序號(hào),剩下的都填充數(shù)據(jù)。對(duì)于非數(shù)據(jù)包,主要是發(fā)送一些控制信息。
監(jiān)聽(tīng)成功后通過(guò)向 writeCharacteristic寫(xiě)入數(shù)據(jù)實(shí)現(xiàn)與服務(wù)端的通信。寫(xiě)入方式如下:
其中:value一般為Hex格式指令,其內(nèi)容由設(shè)備通信的藍(lán)牙通信協(xié)議規(guī)定;
11、接收數(shù)據(jù)
若寫(xiě)入指令成功則回調(diào)BluetoothGattCallback中的onCharacteristicWrite()方法,說(shuō)明將數(shù)據(jù)已經(jīng)發(fā)送給下位機(jī);
@Override public void onCharacteristicWrite(BluetoothGatt gatt,BluetoothGattCharacteristic characteristic, int status) {if (status == BluetoothGatt.GATT_SUCCESS) {Log.e(TAG, "發(fā)送成功");} }若發(fā)送的數(shù)據(jù)符合通信協(xié)議,則服務(wù)端會(huì)向客戶(hù)端回復(fù)相應(yīng)的數(shù)據(jù)。發(fā)送的數(shù)據(jù)通過(guò)回調(diào)onCharacteristicChanged()方法獲取,其處理方式如下:
@Override public void onCharacteristicChanged(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic) {// value為設(shè)備發(fā)送的數(shù)據(jù),根據(jù)數(shù)據(jù)協(xié)議進(jìn)行解析byte[] value = characteristic.getValue(); }通過(guò)向服務(wù)端發(fā)送指令獲取服務(wù)端的回復(fù)數(shù)據(jù),即可完成與設(shè)備的通信過(guò)程;
12、斷開(kāi)連接
當(dāng)與設(shè)備完成通信之后之后一定要斷開(kāi)與設(shè)備的連接。調(diào)用以下方法斷開(kāi)與設(shè)備的連接:
mBluetoothGatt.disconnect(); mBluetoothGatt.close();二、BLE服務(wù)端開(kāi)發(fā)流程
1、設(shè)置廣播以及初始化廣播數(shù)據(jù)
//廣播設(shè)置(必須) AdvertiseSettings settings = new AdvertiseSettings.Builder().setAdvertiseMode(AdvertiseSettings.ADVERTISE_MODE_LOW_LATENCY) //廣播模式: 低功耗,平衡,低延遲.setTxPowerLevel(AdvertiseSettings.ADVERTISE_TX_POWER_HIGH) //發(fā)射功率級(jí)別: 極低,低,中,高.setTimeout(0).setConnectable(true) //能否連接,廣播分為可連接廣播和不可連接廣播.build();//廣播數(shù)據(jù)(必須,廣播啟動(dòng)就會(huì)發(fā)送) AdvertiseData advertiseData = new AdvertiseData.Builder().setIncludeDeviceName(true) //包含藍(lán)牙名稱(chēng).setIncludeTxPowerLevel(true) //包含發(fā)射功率級(jí)別.addManufacturerData(1, new byte[]{23, 33}) //設(shè)備廠商數(shù)據(jù),自定義.build();//掃描響應(yīng)數(shù)據(jù)(可選,當(dāng)客戶(hù)端掃描時(shí)才發(fā)送) AdvertiseData scanResponse = new AdvertiseData.Builder().addManufacturerData(2, new byte[]{66, 66}) //設(shè)備廠商數(shù)據(jù),自定義.addServiceUuid(new ParcelUuid(UUID_SERVICE)) //服務(wù)UUID // .addServiceData(new ParcelUuid(UUID_SERVICE), new byte[]{2}) //服務(wù)數(shù)據(jù),自定義.build();2、開(kāi)始廣播
BluetoothManager bluetoothManager = (BluetoothManager) getSystemService(Context.BLUETOOTH_SERVICE); //BluetoothAdapter bluetoothAdapter = bluetoothManager.getAdapter(); BluetoothAdapter bluetoothAdapter = BluetoothAdapter.getDefaultAdapter();// ============啟動(dòng)BLE藍(lán)牙廣播(廣告) =============== mBluetoothLeAdvertiser = bluetoothAdapter.getBluetoothLeAdvertiser(); mBluetoothLeAdvertiser.startAdvertising(settings, advertiseData, scanResponse, mAdvertiseCallback);// BLE廣播Callback private AdvertiseCallback mAdvertiseCallback = new AdvertiseCallback() {@Overridepublic void onStartSuccess(AdvertiseSettings settingsInEffect) {logTv("BLE廣播開(kāi)啟成功");}@Overridepublic void onStartFailure(int errorCode) {logTv("BLE廣播開(kāi)啟失敗,錯(cuò)誤碼:" + errorCode);} };3、配置Services以及Characteristic
// 注意:必須要開(kāi)啟可連接的BLE廣播,其它設(shè)備才能發(fā)現(xiàn)并連接BLE服務(wù)端! // =============啟動(dòng)BLE藍(lán)牙服務(wù)端====================================== BluetoothGattService service = new BluetoothGattService(UUID_SERVICE, BluetoothGattService.SERVICE_TYPE_PRIMARY);//添加可讀+通知characteristic BluetoothGattCharacteristic characteristicRead = new BluetoothGattCharacteristic(UUID_CHAR_READ_NOTIFY,BluetoothGattCharacteristic.PROPERTY_READ | BluetoothGattCharacteristic.PROPERTY_NOTIFY, BluetoothGattCharacteristic.PERMISSION_READ); characteristicRead.addDescriptor(new BluetoothGattDescriptor(UUID_DESC_NOTITY, BluetoothGattCharacteristic.PERMISSION_WRITE)); service.addCharacteristic(characteristicRead);//添加可寫(xiě)characteristic BluetoothGattCharacteristic characteristicWrite = new BluetoothGattCharacteristic(UUID_CHAR_WRITE, BluetoothGattCharacteristic.PROPERTY_WRITE, BluetoothGattCharacteristic.PERMISSION_WRITE); service.addCharacteristic(characteristicWrite);if (bluetoothManager != null){mBluetoothGattServer = bluetoothManager.openGattServer(this, mBluetoothGattServerCallback); }mBluetoothGattServer.addService(service);4、Server回調(diào)以及操作
/** * 服務(wù)事件的回調(diào) */ private BluetoothGattServerCallback mBluetoothGattServerCallback= new BluetoothGattServerCallback() {/*** 1.連接狀態(tài)發(fā)生變化時(shí)*/@Overridepublic void onConnectionStateChange(BluetoothDevice device, int status, int newState) {Log.e(TAG, String.format("1.onConnectionStateChange:device name = %s, address = %s", device.getName(), device.getAddress()));Log.e(TAG, String.format("1.onConnectionStateChange:status = %s, newState =%s ", status, newState));}@Overridepublic void onServiceAdded(int status, BluetoothGattService service) {Log.e(TAG, String.format("onServiceAdded:status = %s", status));}@Overridepublic void onCharacteristicReadRequest(BluetoothDevice device, int requestId, int offset, BluetoothGattCharacteristic characteristic) {Log.e(TAG, String.format("onCharacteristicReadRequest:device name = %s, address = %s", device.getName(), device.getAddress()));Log.e(TAG, String.format("onCharacteristicReadRequest:requestId = %s, offset = %s", requestId, offset));mGattServer.sendResponse(device, requestId, BluetoothGatt.GATT_SUCCESS, offset, characteristic.getValue());}/*** 3. onCharacteristicWriteRequest,接收具體的字節(jié)*/@Overridepublic void onCharacteristicWriteRequest(BluetoothDevice device, int requestId, BluetoothGattCharacteristic characteristic, boolean preparedWrite, boolean responseNeeded, int offset, byte[] requestBytes) {Log.e(TAG, String.format("3.onCharacteristicWriteRequest:device name = %s, address = %s", device.getName(), device.getAddress()));Log.e(TAG, String.format("3.onCharacteristicWriteRequest:requestId = %s, preparedWrite=%s, responseNeeded=%s, offset=%s, value=%s", requestId, preparedWrite, responseNeeded, offset, requestBytes.toString()));mGattServer.sendResponse(device, requestId, BluetoothGatt.GATT_SUCCESS, offset, requestBytes);//4.處理響應(yīng)內(nèi)容onResponseToClient(requestBytes, device, requestId, characteristic);}/*** 2.描述被寫(xiě)入時(shí),在這里執(zhí)行 bluetoothGattServer.sendResponse(device, requestId, BluetoothGatt.GATT_SUCCESS... 收,觸發(fā) onCharacteristicWriteRequest*/@Overridepublic void onDescriptorWriteRequest(BluetoothDevice device, int requestId, BluetoothGattDescriptor descriptor, boolean preparedWrite, boolean responseNeeded, int offset, byte[] value) {Log.e(TAG, String.format("2.onDescriptorWriteRequest:device name = %s, address = %s", device.getName(), device.getAddress()));Log.e(TAG, String.format("2.onDescriptorWriteRequest:requestId = %s, preparedWrite = %s, responseNeeded = %s, offset = %s, value = %s,", requestId, preparedWrite, responseNeeded, offset, value.toString()));// now tell the connected device that this was all successfullmGattServer.sendResponse(device, requestId, BluetoothGatt.GATT_SUCCESS, offset, value);}/*** 5.特征被讀取。當(dāng)回復(fù)響應(yīng)成功后,客戶(hù)端會(huì)讀取然后觸發(fā)本方法*/@Overridepublic void onDescriptorReadRequest(BluetoothDevice device, int requestId, int offset, BluetoothGattDescriptor descriptor) {Log.e(TAG, String.format("onDescriptorReadRequest:device name = %s, address = %s", device.getName(), device.getAddress()));Log.e(TAG, String.format("onDescriptorReadRequest:requestId = %s", requestId));mGattServer.sendResponse(device, requestId, BluetoothGatt.GATT_SUCCESS, offset, null);}@Overridepublic void onNotificationSent(BluetoothDevice device, int status) {super.onNotificationSent(device, status);Log.e(TAG, String.format("5.onNotificationSent:device name = %s, address = %s", device.getName(), device.getAddress()));Log.e(TAG, String.format("5.onNotificationSent:status = %s", status));}@Overridepublic void onMtuChanged(BluetoothDevice device, int mtu) {super.onMtuChanged(device, mtu);Log.e(TAG, String.format("onMtuChanged:mtu = %s", mtu));}@Overridepublic void onExecuteWrite(BluetoothDevice device, int requestId, boolean execute) {super.onExecuteWrite(device, requestId, execute);Log.e(TAG, String.format("onExecuteWrite:requestId = %s", requestId));} };/** * 4.處理響應(yīng)內(nèi)容 * * @param reqeustBytes * @param device * @param requestId * @param characteristic */ private void onResponseToClient(byte[] reqeustBytes, BluetoothDevice device, int requestId, BluetoothGattCharacteristic characteristic) {Log.e(TAG, String.format("4.onResponseToClient:device name = %s, address = %s", device.getName(), device.getAddress()));Log.e(TAG, String.format("4.onResponseToClient:requestId = %s", requestId));Log.e(TAG, "4.收到:");String str = new String(reqeustBytes) + " hello>";characteristicRead.setValue(str.getBytes());mGattServer.notifyCharacteristicChanged(device, characteristicRead, false);Log.i(TAG, "4.響應(yīng):" + str);MainActivity.handler.obtainMessage(MainActivity.DEVICE, new String(reqeustBytes)).sendToTarget(); }三、源碼下載
源碼上傳在CSDN上了,有需要的可以借鑒。
=====> Android藍(lán)牙Ble通訊Demo示例源碼–掃描,連接,發(fā)送和接收數(shù)據(jù),分包解包
四、藍(lán)牙操作的注意事項(xiàng)
1、如何避免ble藍(lán)牙連接出現(xiàn)133錯(cuò)誤?
- Android 連接外圍設(shè)備的數(shù)量有限,當(dāng)不需要連接藍(lán)牙設(shè)備的時(shí)候,必須調(diào)用 BluetoothGatt#close 方法釋放資源;
- 藍(lán)牙 API 連接藍(lán)牙設(shè)備的超時(shí)時(shí)間大概在 20s 左右,具體時(shí)間看系統(tǒng)實(shí)現(xiàn)。有時(shí)候某些設(shè)備進(jìn)行藍(lán)牙連接的時(shí)間會(huì)很長(zhǎng),大概十多秒。如果自己手動(dòng)設(shè)置了連接超時(shí)時(shí)間在某些設(shè)備上可能會(huì)導(dǎo)致接下來(lái)幾次的連接嘗試都會(huì)在 BluetoothGattCallback#onConnectionStateChange 返回 state == 133;
- 能否避免android設(shè)備與ble設(shè)備連接/斷開(kāi)時(shí)上報(bào)的133這類(lèi)錯(cuò)誤?
1、在連接失敗或者斷開(kāi)連接之后,調(diào)用 close 并刷新緩存
2、盡量不要在startLeScan的時(shí)候嘗試連接,先stopLeScan后再去連
3、對(duì)同一設(shè)備斷開(kāi)后再次連接(連接失敗重連),哪怕調(diào)用完close,需要等待一段時(shí)間(400毫秒試了1次,結(jié)果不 行;1000毫秒則再?zèng)]出現(xiàn)過(guò)問(wèn)題)后再去connectGatt
4、可以在連接前都startLeScan一下,成功率要高一點(diǎn)
2、單次寫(xiě)的數(shù)據(jù)大小有20字節(jié)限制,如何發(fā)送長(zhǎng)數(shù)據(jù)?
BLE單次寫(xiě)的數(shù)據(jù)量大小是有限制的,通常是20字節(jié),可以嘗試通過(guò)requestMTU增大,但不保證能成功。分包寫(xiě)是一種解決方案,需要定義分包協(xié)議,假設(shè)每個(gè)包大小20字節(jié),分兩種包,數(shù)據(jù)包和非數(shù)據(jù)包。對(duì)于數(shù)據(jù)包,頭兩個(gè)字節(jié)表示包的序號(hào),剩下的都填充數(shù)據(jù)。對(duì)于非數(shù)據(jù)包,主要是發(fā)送一些控制信息。
總體流程如下:
1、定義通訊協(xié)議,如下(這里只是個(gè)舉例,可以根據(jù)項(xiàng)目需求擴(kuò)展)
| 01 | 01 | 01 | 0000 | – | 2D |
2、封裝通用發(fā)送數(shù)據(jù)接口(拆包)
該接口根據(jù)會(huì)發(fā)送數(shù)據(jù)內(nèi)容按最大字節(jié)數(shù)拆分(一般20字節(jié))放入隊(duì)列,拆分完后,依次從隊(duì)列里取出發(fā)送
3、封裝通用接收數(shù)據(jù)接口(組包)
該接口根據(jù)從接收的數(shù)據(jù)按協(xié)議里的定義解析數(shù)據(jù)長(zhǎng)度判讀是否完整包,不是的話(huà)把每條消息累加起來(lái)
4、解析完整的數(shù)據(jù)包,進(jìn)行業(yè)務(wù)邏輯處理
5、協(xié)議還可以引入加密解密,需要注意的選算法參數(shù)的時(shí)候,加密后的長(zhǎng)度最好跟原數(shù)據(jù)長(zhǎng)度一致,這樣不會(huì)影響拆包組包
3、在Android不同版本或不同的手機(jī)掃描不到藍(lán)牙設(shè)備
一般都是Android版本適配以及不同ROM機(jī)型(小米/紅米、華為/榮耀等)(EMUI、MIUI、ColorOS等)的權(quán)限問(wèn)題
4、其它
- 藍(lán)牙的寫(xiě)入操作, 讀取操作必須序列化進(jìn)行. 寫(xiě)入數(shù)據(jù)和讀取數(shù)據(jù)是不能同時(shí)進(jìn)行的, 如果調(diào)用了寫(xiě)入數(shù)據(jù)的方法, 馬上調(diào)用又調(diào)用寫(xiě)入數(shù)據(jù)或者讀取數(shù)據(jù)的方法,第二次調(diào)用的方法會(huì)立即返回 false, 代表當(dāng)前無(wú)法進(jìn)行操作;
總結(jié)
藍(lán)牙開(kāi)發(fā)中有很多問(wèn)題,要靜下心分析問(wèn)題,肯定可以解決的,一起加油;
總結(jié)
以上是生活随笔為你收集整理的Android-Ble蓝牙通讯开发–扫描,连接,发送和接收数据,分包解包(附源码)的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: 【python】计算圆周率到任意位支持任
- 下一篇: 《iOS移动开发从入门到精通》图书连载2