藍牙開發其實分2個部分,一個是正常藍牙功能的開發(好比Android藍牙的互相鏈接、讀取藍牙列表、文件傳輸、藍牙耳機等等)、另一個是BLE藍牙開發(屬於低功耗藍牙設備,設備大可能是血糖儀、藍牙手環、藍牙手錶、藍牙溫度槍等等)css
首先分享2個寫的很好的藍牙博客,很是全面,只是個別細節沒有照顧到。android
http://www.javashuo.com/article/p-dhtdobzz-kk.htmlapp
http://www.javashuo.com/article/p-kwhpinam-bz.html異步
<uses-permission android:name="android.permission.BLUETOOTH"/> <uses-permission android:name="android.permission.BLUETOOTH_ADMIN"/> <uses-feature android:name="android.hardware.location.gps" /> <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION"/>
爲何須要gps和定位權限,由於藍牙搜索須要這2個權限(我也以爲莫名其妙)ide
注意如下的開發以6.0爲準,其餘版本藍牙開啓、關閉、搜索些許不一樣ui
public class BTDemo extends AppCompatActivity implements View.OnClickListener{ private final String TAG = "BTDemo"; private Button btn_bt_open,btn_bt_close,btn_bt_goSttings,btn_bt_visible,in_BTList; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_btdemo); btn_bt_open = (Button)findViewById(R.id.btn_BT_open); btn_bt_close = (Button)findViewById(R.id.btn_BT_close); btn_bt_visible = (Button)findViewById(R.id.btn_BT_visible); btn_bt_goSttings = (Button)findViewById(R.id.btn_BT_GoSttings); in_BTList = (Button)findViewById(R.id.in_BTList); btn_bt_open.setOnClickListener(this); btn_bt_close.setOnClickListener(this); btn_bt_visible.setOnClickListener(this); btn_bt_goSttings.setOnClickListener(this); in_BTList.setOnClickListener(this); } //打開藍牙 public void openBT(){ //建立藍牙適配器 BluetoothAdapter bluetoothAdapter = BluetoothAdapter.getDefaultAdapter(); if (bluetoothAdapter == null){ Log.e(TAG, "該設備不支持藍牙"); } if(!bluetoothAdapter.isEnabled()){ Log.e(TAG, "準備打開藍牙" ); //彈窗詢問方式打開藍牙 Intent intent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE);//藍牙適配器. 行動-請求-打開 //startActivity(intent);也可使用這個 startActivityForResult(intent,RESULT_OK); //bluetoothAdapter.enable(); 不詢問直接打開藍牙 } } //關閉藍牙 public void closeBT(){ Log.e(TAG, "coloseBT 被調用" ); BluetoothAdapter bluetoothAdapter = BluetoothAdapter.getDefaultAdapter(); if(bluetoothAdapter.isEnabled()){ bluetoothAdapter.disable(); } } //跳轉到設置-藍牙界面中 public void settingsOpen(){ Intent intent = new Intent(Settings.ACTION_BLUETOOTH_SETTINGS); startActivity(intent); } //藍牙可見(藍牙能夠被其餘設備發現) public void btVisible(){ BluetoothAdapter blueteoothAdapter = BluetoothAdapter.getDefaultAdapter(); //開啓被其它藍牙設備發現的功能 //getScanMode 得到掃描模式 掃描-模式-鏈接-發現 if (blueteoothAdapter.getScanMode() != BluetoothAdapter.SCAN_MODE_CONNECTABLE_DISCOVERABLE) { Intent i = new Intent(BluetoothAdapter.ACTION_REQUEST_DISCOVERABLE);//行動-請求-發現 //設置爲一直開啓 0是一直開着,若是設置了時間就會按照設置時間顯示藍牙可見 i.putExtra(BluetoothAdapter.EXTRA_DISCOVERABLE_DURATION, 0); startActivity(i); } } @Override public void onClick(View v) { switch (v.getId()){ case R.id.btn_BT_open: openBT(); break; case R.id.btn_BT_close: closeBT(); break; case R.id.btn_BT_visible: btVisible(); break; case R.id.btn_BT_GoSttings: settingsOpen(); break; case R.id.in_BTList: Intent intent = new Intent(BTDemo.this,BTListView.class); startActivity(intent); break; default: break; } } }
/** * 初始化藍牙狀態廣播監聽 */ private void initBtState(){ mBtTemperatureReceiver = new BtTemperatureReceiver(); IntentFilter intentFilter = new IntentFilter(); intentFilter.addAction(BluetoothAdapter.ACTION_STATE_CHANGED); registerReceiver(mBtTemperatureReceiver,intentFilter); } /** * 藍牙狀態廣播回調 */ class BtTemperatureReceiver extends BroadcastReceiver{ @Override public void onReceive(Context context, Intent intent) { String action = intent.getAction(); switch (action){ //注意!這裏是先拿action 等於 BluetoothAdapter.ACTION_STATE_CHANGED 在解析intent.getIntExtra(BluetoothAdapter.EXTRA_STATE, 0) case BluetoothAdapter.ACTION_STATE_CHANGED: L.e("觸發藍牙狀態"); int blState = intent.getIntExtra(BluetoothAdapter.EXTRA_STATE, 0); switch (blState){ case BluetoothAdapter.STATE_TURNING_ON: L.e("藍牙正在開啓"); break; case BluetoothAdapter.STATE_ON: L.e("藍牙已經開啓"); break; case BluetoothAdapter.STATE_TURNING_OFF: L.e("藍牙正在關閉"); break; case BluetoothAdapter.STATE_OFF: L.e("藍牙已經關閉"); break; case BluetoothAdapter.ERROR: break; default: break; } break; default: break; } } }
public class BTListView extends AppCompatActivity { private ListView listView; private ArrayAdapter mArrayAdapter; private BluetoothAdapter mBluetoothAdapter; private BroadcastReceiver broadcastReceiver; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_btlist_view); //建立藍牙適配器 mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter(); //搜索藍牙 mBluetoothAdapter.startDiscovery(); listView = (ListView) findViewById(R.id.BTlistView); //建立listView的適配器 mArrayAdapter = new ArrayAdapter(this,android.R.layout.simple_list_item_1); //意圖過濾器 IntentFilter intentFilter = new IntentFilter(); intentFilter.addAction(BluetoothDevice.ACTION_FOUND); //藍牙搜索 intentFilter.addAction(BluetoothAdapter.ACTION_DISCOVERY_STARTED); //開始搜索 intentFilter.addAction(BluetoothAdapter.ACTION_DISCOVERY_FINISHED); //結束搜索 //建立廣播接收器 broadcastReceiver = new BroadcastReceiver() { @Override public void onReceive(Context context, Intent intent) { //得到intent的行動 String action = intent.getAction(); /* 三組藍牙廣播狀態分別是: BluetoothAdapter.ACTION_DISCOVERY_STARTED 開始藍牙搜索 BluetoothDevice.ACTION_FOUND 藍牙搜索中 BluetoothAdapter.ACTION_DISCOVERY_FINISHED 藍牙搜索完畢 */ if (BluetoothDevice.ACTION_FOUND.equals(action)) { //建立藍牙設備,咱們能夠從BluetoothDevice 裏得到各類信息 名稱、地址 等等 BluetoothDevice bluetoothDevice = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE); // 將設備名稱和地址放入array adapter,以便在ListView中顯示 mArrayAdapter.add(bluetoothDevice.getName() + "\n" + bluetoothDevice.getAddress()); } else if (BluetoothAdapter.ACTION_DISCOVERY_STARTED.equals(action)) { Toast.makeText(BTListView.this,"開始搜索", Toast.LENGTH_SHORT).show(); } else if (BluetoothAdapter.ACTION_DISCOVERY_FINISHED.equals(action)) { Toast.makeText(BTListView.this,"搜索完畢",Toast.LENGTH_SHORT).show(); } } }; registerReceiver(broadcastReceiver,intentFilter);//添加廣播 listView.setAdapter(mArrayAdapter); } @Override protected void onDestroy() { super.onDestroy(); mBluetoothAdapter.cancelDiscovery();// 取消搜索藍牙 unregisterReceiver(broadcastReceiver);//註銷廣播接收器 } }
參考:Android BLE 藍牙開發入門 https://www.jianshu.com/p/3a372af38103this
參考:https://www.jianshu.com/p/d70e22ce61bcspa
一個低功耗藍牙設備能夠定義許多 Service, Service 能夠理解爲一個功能的集合。設備中每個不一樣的 Service 都有一個 128 bit 的 UUID 做爲這個 Service 的獨立標誌。藍牙核心規範制定了兩種不一樣的UUID,一種是基本的UUID,一種是代替基本UUID的16位UUID。全部的藍牙技術聯盟定義UUID共用了一個基本的UUID:
0x0000xxxx-0000-1000-8000-00805F9B34FB
爲了進一步簡化基本UUID,每個藍牙技術聯盟定義的屬性有一個惟一的16位UUID,以代替上面的基本UUID的‘x’部分。例如,心率測量特性使用0X2A37做爲它的16位UUID,所以它完整的128位UUID爲:
0x00002A37-0000-1000-8000-00805F9B34FB.net
在 Service 下面,又包括了許多的獨立數據項,咱們把這些獨立的數據項稱做 Characteristic。一樣的,每個 Characteristic 也有一個惟一的 UUID 做爲標識符。在 Android 開發中,創建藍牙鏈接後,咱們說的經過藍牙發送數據給外圍設備就是往這些 Characteristic 中的 Value 字段寫入數據;外圍設備發送數據給手機就是監聽這些 Charateristic 中的 Value 字段有沒有變化,若是發生了變化,手機的 BLE API 就會收到一個監聽的回調。線程
你購買的藍牙設備夠遵照開源思想而且尊重藍牙設備行業規則,那麼你能夠在藍牙官網上查看一份全部特徵碼的對應UUID.他們規範自定了各類數據對應的UUID.
若是藍牙設備商夠無恥厚臉皮就能夠隨便瞎改, 你拿官網的UUID表對照讀取寫入數據就根本沒有意義了.(罵一下這些設備商腦子裏根本沒有開源2個字,臉皮這麼厚爲何還使用藍牙協議?這麼牛皮怎麼不本身開發無線傳輸協議?)
藍牙官網:https://www.bluetooth.com/specifications/gatt/characteristics
<uses-permission android:name="android.permission.BLUETOOTH"/> <uses-permission android:name="android.permission.BLUETOOTH_ADMIN"/> <uses-feature android:name="android.hardware.location.gps" /> <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION"/>
public void startBt(){ BluetoothManager bluetoothManager = (BluetoothManager)getSystemService(Context.BLUETOOTH_SERVICE); BluetoothAdapter bluetoothAdapter = bluetoothManager.getAdapter(); // bluetoothAdapter.enable();//8.0版本 使用這個能夠彈窗詢問開啓 其餘版本則是不提示啓動 if (bluetoothAdapter == null||!bluetoothAdapter.isEnabled()){ Intent intent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE); startActivityForResult(intent,1); } Log.e(TAG, "startBt: 開啓藍牙"); }
public void closeBt(){ BluetoothManager bluetoothManager = (BluetoothManager)getSystemService(Context.BLUETOOTH_SERVICE); BluetoothAdapter bluetoothAdapter = bluetoothManager.getAdapter(); if (bluetoothAdapter.isEnabled()){ bluetoothAdapter.disable(); } Log.e(TAG, "closeBt: 關閉藍牙"); }
public void searchBt(){ BluetoothManager bluetoothManager = (BluetoothManager)getSystemService(Context.BLUETOOTH_SERVICE); BluetoothAdapter bluetoothAdapter = bluetoothManager.getAdapter(); if (!bluetoothAdapter.isEnabled()){ return; } mCallback = new BluetoothAdapter.LeScanCallback() { @Override public void onLeScan(BluetoothDevice device, int rssi, byte[] scanRecord) { //獲得設備掃描結果回調,每掃描到一個就回調一次。 if (TextUtils.isEmpty(device.getName())){ return; } if (device.getName().equals("BY21S")){//判斷掃描到的設備名稱,若是你須要更準確也能夠根據藍牙地址判斷 device.getAddress() device.connectGatt(MainActivity.this,true,btCallback());//鏈接設備,1.參數爲上下文 2.斷開是否自動重連 3.設備鏈接回調接口類btCallback()方法我在下面有描述 stopSearchBt();//鏈接後依然要手動關閉搜索,不然會一直保持在搜索狀態 } } }; bluetoothAdapter.startLeScan(mCallback);//開始掃描 Log.e(TAG, "searchBt: 搜索藍牙"); }
public void stopSearchBt(){ BluetoothManager bluetoothManager = (BluetoothManager)getSystemService(Context.BLUETOOTH_SERVICE); BluetoothAdapter bluetoothAdapter = bluetoothManager.getAdapter(); if (!bluetoothAdapter.isEnabled()||mCallback == null){ return; } bluetoothAdapter.stopLeScan(mCallback);//注意這裏的mCallback,要跟開啓藍牙搜索的Callback一致,不然沒法關閉對應藍牙搜索 }
private void initSearchBt(){ if (mBluetoothAdapter == null || !mBluetoothAdapter.isEnabled()){ Log.e(TAG,"藍牙未開啓"); } scanCallback = new ScanCallback() { @Override public void onScanResult(int callbackType, ScanResult result) { // super.onScanResult(callbackType, result); if (TextUtils.isEmpty(result.getDevice().getName())){ return; } if (result.getDevice().getName().equals("AET-WD")){ result.getDevice().connectGatt(BtActivity.this,true,btCallback()); Log.e(TAG,"藍牙鏈接成功"); mBluetoothAdapter.getBluetoothLeScanner().stopScan(scanCallback); } Log.e(TAG, "\n----------------------------\n name="+result.getDevice().getName()+"\n"+"address="+result.getDevice().getAddress()); } @Override public void onBatchScanResults(List<ScanResult> results) { super.onBatchScanResults(results); } @Override public void onScanFailed(int errorCode) { super.onScanFailed(errorCode); } }; mBluetoothAdapter.getBluetoothLeScanner().startScan(scanCallback); }
mBluetoothAdapter.getBluetoothLeScanner().stopScan(scanCallback);
這個回調接口類負責設備的所有數據交互
public BluetoothGattCallback btCallback(){ return new BluetoothGattCallback() { @Override public void onPhyUpdate(BluetoothGatt gatt, int txPhy, int rxPhy, int status) { super.onPhyUpdate(gatt, txPhy, rxPhy, status);
//PHY觸發的回調,或者遠程設備更改PHY,通常咱們不須要這個回調。 } @Override public void onPhyRead(BluetoothGatt gatt, int txPhy, int rxPhy, int status) { super.onPhyRead(gatt, txPhy, rxPhy, status);
//PHY的讀取,通常咱們不須要這個回調。 } @Override public void onConnectionStateChange(BluetoothGatt gatt, int status, int newState) { super.onConnectionStateChange(gatt, status, newState);
//設備狀態變化回調,鏈接成功後會首先觸發回調 回調參數分別爲 1.藍牙網關 2.藍牙狀態 3.鏈接狀況 } @Override public void onServicesDiscovered(BluetoothGatt gatt, int status) { super.onServicesDiscovered(gatt, status);
//發現服務回調,使用發現服務後會首先觸發這個回調,咱們在這裏能夠得到對應UUID的藍牙服務(Services)和特徵(Characteristic) } @Override public void onCharacteristicRead(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, int status) { super.onCharacteristicRead(gatt, characteristic, status);
//讀取特徵的回調 } @Override public void onCharacteristicWrite(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, int status) { super.onCharacteristicWrite(gatt, characteristic, status);
//寫入特徵數據的回調,寫入後會回調一次這個方法,你能夠讀取一次你寫入的數據以確認寫入數據無誤。 } @Override public void onCharacteristicChanged(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic) { super.onCharacteristicChanged(gatt, characteristic);
//特徵變化回調,通常是設置特徵通知後,指定的特徵在主動藍牙設備上給手機app回調數據時觸發的回調 } @Override public void onDescriptorRead(BluetoothGatt gatt, BluetoothGattDescriptor descriptor, int status) { super.onDescriptorRead(gatt, descriptor, status);
//讀取描述符的回調 } @Override public void onDescriptorWrite(BluetoothGatt gatt, BluetoothGattDescriptor descriptor, int status) { super.onDescriptorWrite(gatt, descriptor, status);
//寫入描述符的回調 } @Override public void onReliableWriteCompleted(BluetoothGatt gatt, int status) { super.onReliableWriteCompleted(gatt, status);
//可信寫入回調,當你寫入非法範圍的值(好比溫度範圍10-40,可是你輸入了一個50)時能夠調用對應方法,切換到這個回調中。處理後續邏輯。 } @Override public void onReadRemoteRssi(BluetoothGatt gatt, int rssi, int status) { super.onReadRemoteRssi(gatt, rssi, status);
//讀取信號強度的回調 rssi爲信號強度值() } @Override public void onMtuChanged(BluetoothGatt gatt, int mtu, int status) { super.onMtuChanged(gatt, mtu, status);
//藍牙網卡變化回調 } }; }
(1) notification對應onCharacteristicChanged;
gatt.setCharacteristicNotification(characteristic, true);
該方法通常是在發現服務後,進行設置的,設置該方法的目的是讓硬件在數據改變的時候,發送數據給app,app則經過onCharacteristicChanged方法回調給用戶,從參數中可獲取到回調回來的數據。
(2) readCharacteristic對應onCharacteristicRead;
gatt.readCharacteristic(characteristic);
(3) writeCharacteristic對應onCharacteristicWrite;
gatt.wirteCharacteristic(mCurrentcharacteristic);
(4) 鏈接藍牙或者斷開藍牙 對應 onConnectionStateChange;
bluetoothDevice.connectGatt(this, false, mGattCallback);
或
gatt.disconnect();(斷開鏈接後務必記得gatt.close();)
(5) readDescriptor對應onDescriptorRead;
gatt.readDescriptor(descriptor);
(6) writeDescriptor對應onDescriptorWrite;
gatt.writeDescriptor(descriptor);
(7) readRemoteRssi對應onReadRemoteRssi;
gatt.readRemoteRssi();
(8) executeReliableWrite對應onReliableWriteCompleted;
gatt.executeReliableWrite();
(9) discoverServices對應onServicesDiscovered
gatt.discoverServices();//發現服務
開啓藍牙>搜索藍牙設備>鏈接設備>發現設備服務>獲取指定UUID服務>獲取指定服務下的UUID特徵>操做特徵發送數據或者讀取數據>設置長時間廣播監聽某項藍牙特徵回調>斷開設備>關閉藍牙
開啓藍牙和搜索藍牙/鏈接設備已經在上面有介紹了,不須要重複,下面咱們說下後續的步驟
@Override public void onConnectionStateChange(BluetoothGatt gatt, int status, int newState) { super.onConnectionStateChange(gatt, status, newState); if (status == BluetoothGatt.GATT_SUCCESS && BluetoothGatt.STATE_CONNECTED == newState){ Log.e(TAG, "onConnectionStateChange: 設備鏈接成功 status狀態="+status+"鏈接狀況="+newState); gatt.discoverServices();//發現服務 } // 其餘類型newState狀態 // BluetoothGatt.STATE_CONNECTED;//已經鏈接 // BluetoothGatt.STATE_CONNECTING;//正在鏈接 // BluetoothGatt.STATE_DISCONNECTED;//已經斷開 // BluetoothGatt.STATE_DISCONNECTING;//正在斷開 }
在設備狀態變化回調觸發後,就可使用gatt.discoverServices()發現設備服務了
@Override public void onServicesDiscovered(final BluetoothGatt gatt, int status) { super.onServicesDiscovered(gatt, status); //發現服務回調,使用發現服務後會首先觸發這個回調,咱們在這裏能夠得到對應UUID的藍牙服務(Services)和特徵(Characteristic) Log.e(TAG, "onServicesDiscovered: "); mGatt = gatt; BluetoothGattService service = gatt.getService(UUID.fromString("6e400001-b5a3-f393-e0a9-e50e24dcca9e"));//獲取對應uuid的服務 mCharacteristic = service.getCharacteristic(UUID.fromString("6e400002-b5a3-f393-e0a9-e50e24dcca9e"));//從服務裏獲取對應uuid特徵 }
讀取所有設備裏的所有服務與特徵(以及特徵狀態)
@Override public void onServicesDiscovered(final BluetoothGatt gatt, int status) { super.onServicesDiscovered(gatt, status); Log.e(TAG, "onServicesDiscovered: "); new Thread(new Runnable() { @Override public void run() { List<BluetoothGattService> serviceList = gatt.getServices();//獲取設備裏的所有服務List集合 for (BluetoothGattService service : serviceList){ Log.e(TAG, "service uuid = "+service.getUuid()+"----------------"); List<BluetoothGattCharacteristic> characteristicList = service.getCharacteristics();//獲取指定服務裏所有特徵的List集合 for (BluetoothGattCharacteristic characteristic : characteristicList){ int charaProp = characteristic.getProperties(); Log.e(TAG, "characteristic uuid="+characteristic.getUuid()); if ((charaProp | BluetoothGattCharacteristic.PERMISSION_READ)>0){ Log.e(TAG, "可讀"); } if ((charaProp | BluetoothGattCharacteristic.PERMISSION_WRITE)>0){ Log.e(TAG, "可寫"); } if ((charaProp | BluetoothGattCharacteristic.PROPERTY_NOTIFY)>0){ Log.e(TAG, "可通知"); } } } } }).start();
由於回調方法都在藍牙操做線程裏,爲了避免堵塞藍牙消息,因此耗時操做建議建立線程單獨操做.
public void sendData(View view){ Log.e(TAG, "sendData: 發送數據"); mCharacteristic.setValue(setValue()); mGatt.writeCharacteristic(mCharacteristic); } private byte[] setValue(){ data = new byte[20]; data[0] = (byte)0xAB; data[1] = (byte)0x00; data[2] = (byte)0x00; data[3] = (byte)0xff; data[4] = (byte)0x31; data[5] = (byte)0x09; data[6] = (byte)0x01; data[7] = (byte)0x00; data[8] = (byte)0x00; data[9] = (byte)0x00; data[10] = (byte)0x00; data[11] = (byte)0x00; data[12] = (byte)0x00; data[13] = (byte)0x00; data[14] = (byte)0x00; data[15] = (byte)0x00; data[16] = (byte)0x00; data[17] = (byte)0x00; data[18] = (byte)0x00; data[19] = (byte)0x00; return data; }
一次數據發送最多發送20個字節的數據,多了須要分包發送.另外若是有自定義數據協議,按照協議在指定位置插入對應數據.寫入數據後後會執行一次onCharacteristicWrite()回調方法(你能夠在這個方法再次確認寫入的數據,也能夠無論這個方法)
public void readData(){ BluetoothGattService service = mGatt.getService(SERVICE_UUID); BluetoothGattCharacteristic characteristic = service.getCharacteristic(CHARACTER_UUID); mGatt.readCharacteristic(characteristic); }
操做讀取數據後,數據會在onCharacteristicRead方法裏回調
@Override public void onCharacteristicRead(final BluetoothGatt gatt, final BluetoothGattCharacteristic characteristic, final int status) { Log.d(TAG, "callback characteristic read status " + status + " in thread " + Thread.currentThread()); if (status == BluetoothGatt.GATT_SUCCESS) { Log.d(TAG, "read value: " + characteristic.getValue()); } }
BLE app一般須要獲取設備中characteristic 變化的通知。下面的代碼演示了怎麼爲一個Characteristic 設置一個監聽。
@Override public void onServicesDiscovered(BluetoothGatt gatt, int status) { super.onServicesDiscovered(gatt, status); List<BluetoothGattService> list = gatt.getServices(); for (BluetoothGattService service : list){ Log.e(TAG,"uuid="+service.getUuid()); } UUID suuid = UUID.fromString("0000fff0-0000-1000-8000-00805f9b34fb"); BluetoothGattService bluetoothGattService = gatt.getService(suuid); BluetoothGattCharacteristic readCharacteristic = bluetoothGattService.getCharacteristic(UUID.fromString("0000fff3-0000-1000-8000-00805f9b34fb")); gatt.setCharacteristicNotification(readCharacteristic,true); List<BluetoothGattDescriptor> list1 = readCharacteristic.getDescriptors(); for (BluetoothGattDescriptor descriptor : list1){ Log.e(TAG,"descriptor uuid = "+descriptor.getUuid()); } //下面readCharacteristic.getDescriptor(UUID.fromString("00002902-0000-1000-8000-00805f9b34fb")) 的uuid就是上面遍歷描述符獲取的來的,也能夠在藍牙協議書上找到. //可是這裏有一個坑,若是藍牙協議書提供的不完整,你會下意識的覺得此處描述符的uuid就是讀取特徵的uuid.會發現BluetoothGattDescriptor descriptor怎麼獲取都是null,而且後續操做不會報錯.. //因此建議遍歷描述符目視區分和確認uuid,避坑 BluetoothGattDescriptor descriptor = readCharacteristic.getDescriptor(UUID.fromString("00002902-0000-1000-8000-00805f9b34fb")); descriptor.setValue(BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE); gatt.writeDescriptor(descriptor); //另外注意一個坑,若是下面的代碼你須要發送值給藍牙設備,那麼最好作異步.操做藍牙是不容許 寫/讀/設置 短期內同時操做的 }
值得注意的是,除了經過 BluetoothGatt#setCharacteristicNotification 開啓 Android 端接收通知的開關,還須要往 Characteristic 的 Descriptor 屬性寫入開啓通知的數據開關使得當硬件的數據改變時,主動往手機發送數據。
當咱們鏈接藍牙設備完成一系列的藍牙操做以後就能夠斷開藍牙設備的鏈接了。經過 BluetoothGatt#disconnect 能夠斷開正在鏈接的藍牙設備。當這一個方法被調用以後,系統會異步回調 BluetoothGattCallback#onConnectionStateChange 方法。經過這個方法的 newState 參數能夠判斷是鏈接成功仍是斷開成功的回調。
因爲 Android 藍牙鏈接設備的資源有限,當咱們執行斷開藍牙操做以後必須執行 BluetoothGatt#close 方法釋放資源。須要注意的是經過 BluetoothGatt#close 方法也能夠執行斷開藍牙的操做,不過 BluetoothGattCallback#onConnectionStateChange 將不會收到任何回調。此時若是執行 BluetoothGatt#connect 方法會獲得一個藍牙 API 的空指針異常。因此,咱們推薦的寫法是當藍牙成功鏈接以後,經過 BluetoothGatt#disconnect 斷開藍牙的鏈接,緊接着在 BluetoothGattCallback#onConnectionStateChange 執行 BluetoothGatt#close 方法釋放資源。
如下是代碼示例:
@Override public void onConnectionStateChange(final BluetoothGatt gatt, final int status, final int newState) { Log.d(TAG, "onConnectionStateChange: thread " + Thread.currentThread() + " status " + newState); if (status != BluetoothGatt.GATT_SUCCESS) { String err = "Cannot connect device with error status: " + status; // 當嘗試鏈接失敗的時候調用 disconnect 方法是不會引發這個方法回調的,因此這裏 // 直接回調就能夠了。 gatt.close(); Log.e(TAG, err); return; } if (newState == BluetoothProfile.STATE_CONNECTED) { gatt.discoverService(); } else if (newState == BluetoothProfile.STATE_DISCONNECTED) { gatt.close(); } }
當你覺得操做完上面的斷開設備的時候,你會覺得真的斷開了設備.其實...是斷開了設備,可是設備可能還被放入到藍牙記憶設備列表裏.下次你開啓服務或者開啓藍牙的時候你會發現,設備竟然搜索不到,實際上是由於設備已經自動根據記憶設備鏈接上了....
是否是很蛋疼....下面可使用這種反射方法刪除全部記憶設備,這樣下次藍牙就不會亂自動鏈接設備了
//獲得配對的設備列表,清除已配對的設備 public void removePairDevice(){ if(mBluetoothAdapter!=null){ Set<BluetoothDevice> bondedDevices = mBluetoothAdapter.getBondedDevices(); for(BluetoothDevice device : bondedDevices ){ unpairDevice(device); } } } //反射來調用BluetoothDevice.removeBond取消設備的配對 private void unpairDevice(BluetoothDevice device) { try { Method m = device.getClass() .getMethod("removeBond", (Class[]) null); m.invoke(device, (Object[]) null); } catch (Exception e) { Log.e("ytzn", e.getMessage()); } }