公司開發任務是,將醫療設備經過藍牙集成到app中,在這開發中遇到了數不盡的坑.在此記錄一下作一個記錄,若是其餘開發人員看見或許能提供一些幫助,若有不對,盡情指正,不勝感激!android
剛開始接觸的時候,被各類超長的API嚇到了,像:BluetoothGatt , BluetoothGattCharacteristic , BluetoothGattDescriptor 等等.並且還要作多鏈接,上位機一對多下位機.網上例子也是雜七雜八.看的頭暈.後來在老大的幫助下,漸漸明白許多,在此感謝老大.廢話到此結束,下面進入正題.數組
1. 判斷當前設備是否支持藍牙app
BluetoothManager mBluetoothManager = (BluetoothManager) activity.getSystemService(Context.BLUETOOTH_SERVICE); BluetoothAdapter adapter = mBluetoothManager.getAdapter(); if(adapter==null){ //系統不支持藍牙。 }
2. 判斷當前設備是否支持低功耗藍牙BLEide
boolean isSupportBle = activity.getPackageManager().hasSystemFeature(PackageManager.FEATURE_BLUETOOTH_LE);
3. 判斷藍牙是否開啓,開啓藍牙!post
BluetoothManager mBluetoothManager = (BluetoothManager) context.getSystemService(Context.BLUETOOTH_SERVICE); BluetoothAdapter adapter = mBluetoothManager.getAdapter(); if(!adapter.isEnable){ //未開啓藍牙 //申請開啓藍牙 Intent intent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE); startActivityForResult(intent , request); }
BluetoothAdapter adapter = mBluetoothManager.getAdapter(); adapter.startLeScan(callback); //掃描須要一個回調。
注意,掃描周圍藍牙是一個很耗電的過程,最好加上一個掃描時間。自動中止。code
handler.postDelayed(new Runnable() { @Override public void run() { adapter.stopLeScan(callback); //中止掃描 } },10000);//設置10秒鐘結束掃描
public BluetoothAdapter.LeScanCallback scanCallBack = new BluetoothAdapter.LeScanCallback() { @Override public void onLeScan(BluetoothDevice device, int rssi, byte[] scanRecord) { //這裏注意,本人在開發中遇到的是 常常有的藍牙設備是沒有名字的, (device.getName == null) //不知道這是什麼緣由引發的,後來跟不少藍牙高手討論的是結果初步懷疑應該是芯片的問題 //尤爲是MTK的芯片常常出現這種問題,換了搭載高通和華爲的芯片的設備就沒問題了。 } };
BluetoothDevice remoteDevice = adapter.getRemoteDevice(address); remoteDevice.connectGatt(context, true, mGattCallback);//參數1:上下文。 //參數2:是否自動鏈接(當設備能夠用時) //參數3:鏈接回調。
這裏可能有些疑問就是,明明已經掃描到了,在回調中已經有了 BluetoothDevice 爲什麼還要去 getRemoteDevice(address)?
那是由於,不少低功耗的設備開機時間是不多的,就拿咱們公司開發的那個血壓計,他是開機纔開啓藍牙,而測量完了以後過一段時間就會自動關閉。因此防止去鏈接設備的時候設備已經關機的狀況。xml
鏈接回調ip
private BluetoothGattCallback mGattCallback = new BluetoothGattCallback() { @Override public void onConnectionStateChange(BluetoothGatt gatt, int status, int newState) { if (newState == BluetoothProfile.STATE_CONNECTED) { //鏈接成功 } else if (newState == BluetoothProfile.STATE_DISCONNECTED) { //鏈接斷開 } changeStatus(newState);//改變當前狀態 } @Override public void onServicesDiscovered(BluetoothGatt gatt, int status) { //當服務發現以後回調這裏 } @Override public void onCharacteristicRead(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, int status) { } @Override public void onCharacteristicChanged(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic) { } @Override public void onDescriptorWrite(BluetoothGatt gatt, BluetoothGattDescriptor descriptor, int status) { } };
一個低功耗藍牙設備是有不少種服務的,就好比該設備的電量信息,設備的當前狀態(好比血壓計,是正在測量仍是在等待測量)
有的設備支持歷史數據等等。這些都是在藍牙的服務當中。咱們要去發現藍牙的服務!開發
這裏很簡單就是一句話,在鏈接成功的回調中調用:rem
public void onConnectionStateChange(BluetoothGatt gatt, int status, int newState) { if (newState == BluetoothProfile.STATE_CONNECTED) { //鏈接成功 gatt.discoverServices();//開始發現設備的服務 } else if (newState == BluetoothProfile.STATE_DISCONNECTED) { //鏈接斷開 } changeStatus(newState);//改變當前狀態 }
調用了以後會在 另外一個回調中 回調回來。
@Override public void onServicesDiscovered(BluetoothGatt gatt, int status) { //當服務發現以後回調這裏 }
這裏是比較重要的地方,注意,每個藍牙的通信協議不通,有的設備是鏈接了以後不須要任何操做就等待藍牙設備上傳數據的,而有的設備是須要手動打開數據通道!或者發送指令給藍牙設備,每個Gatt協議中有多個BluetoothGattService,而每一個BluetoothGattService中又有多個BluetoothGattCharacteristic (我把它看作一個數據通道-_-!),而每個BluetoothGattCharacteristic 的屬性是不一樣的,有的是可讀,有的是可寫,有的是可訂閱,因此必定不要搞混了,能夠用UUID區分他們,這裏大多數設備廠家都會給一份設備的通信協議其中就有 哪個UUID 表明什麼。都會有說明。經過UUID 獲取到了對應的BluetoothGattCharacteristic 以後就能夠判斷他的屬性是什麼。
開啓數據通道
@Override public void onServicesDiscovered(BluetoothGatt gatt, int status) { //服務發現方法回調。 if (status == BluetoothGatt.GATT_SUCCESS) { BluetoothGattService service = gatt.getService(SERVICE_UUID); //經過廠家給的UUID獲取BluetoothGattService if (service != null) { BluetoothGattCharacteristic characteristic = service.getCharacteristic(CHARACTERISTIC_UUID);//同上 if (characteristic != null && (characteristic.getProperties() | BluetoothGattCharacteristic.PROPERTY_NOTIFY) > 0) { //經過判斷,打開Notification 通知,提醒。通常是設備測量完成了以後會發送對應的數據上來。 gatt.setCharacteristicNotification(characteristic, true); //在經過上面的設置返回爲true以後還要進行下面的操做,才能訂閱到數據的上傳。下面是完整的訂閱數據代碼! if(gatt.setCharacteristicNotification(characteristic, true)){ for(BluetoothGattDescriptor dp: characteristic.getDescriptors()){ if (dp != null) { if ((characteristic.getProperties() & BluetoothGattCharacteristic.PROPERTY_NOTIFY) != 0) { dp.setValue(BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE); } else if ((characteristic.getProperties() & BluetoothGattCharacteristic.PROPERTY_INDICATE) != 0) { dp.setValue(BluetoothGattDescriptor.ENABLE_INDICATION_VALUE); } gatt.writeDescriptor(dp); } } } } } } }
像設備發送指令
通常向設備發送什麼指令在通信協議上面也是有的,都是發送一個byte[]數組,每一位表明什麼協議裏面都是不一樣的。例如:一個測量溫度的設備,他當前是華氏度的單位,咱們能夠給他發送一個指令讓他把單位更換成攝氏度:
private void changeMonitorMod(BluetoothGatt gatt, byte[] buffer) { if (gatt != null && gatt != null) { BluetoothGattService writeService = gatt.getService(MYUUID); if (writeService == null) { return; } } BluetoothGattCharacteristic writeCharacteristic = writeService.getCharacteristic(MYWRITECHARACTERISTIC); if (writeCharacteristic == null) { return; } writeCharacteristic.setValue(buffer); //上面的buffer數組中裝的就是指令,多長? 每一位上面的數字表明什麼意思在協議中查看! gatt.writeCharacteristic(writeCharacteristic);//像設備寫入指令。 }
不要忘了,要在清單文件中AndroidManifest.xml 聲明權限哦。
<uses-permission android:name="android.permission.BLUETOOTH"/> <uses-permission android:name="android.permission.BLUETOOTH_ADMIN"/>
關於一些坑:
不少廠家很坑爹,給的文檔水的要命,第一時間要看看文檔詳細不詳細,若是沒有文檔至少也要給個Demo.
注意設備的開機時間,自動關機時間,對狀態的保存。
不少設備在自動關機以後的回調是很慢的,甚至設備關機10秒以後纔會回調到鏈接狀態的回調方法中。
關於手動設置斷開 gatt.disConnect() 這個方法,我試過了,調用以後確實會當即回調到對應的狀態方法中,可是實際上物理上的鏈接是尚未斷開的。物理上的鏈接斷開以後還會再次回調到方法中。這是一個比較漫長的回調,區別與設備,不通設備的機制不同,有的快,有的慢。
好了,差很少就這麼多,寫的匆忙,若是有哪裏不對,輕噴,還請大佬們指正。謝啦!