蓝牙开发其实分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()); } }