Android 蓝牙4.0 BLE (onServicesDiscovered 返回 status 是 129,133时)
Android ble (Bluetooth Low Energy) 蓝牙4.0,也就是说android 4.3+, API level >= 18,且支持蓝牙4.0的手机才可以使用。
BLE是蓝牙4.0的核心Profile,主打功能是快速搜索,快速连接,无需配对,超低功耗保持连接和传输数据,弱点是数据传输速率低,由于BLE的低功耗特点,因此普遍用于穿戴设备。
官方demo:http://developer.android.com/guide/topics/connectivity/bluetooth-le.html
官方demo(csdn下载,感谢分享的人吧):http://download.csdn.net/detail/lqw770737185/8116019
1 <uses-permission android:name="android.permission.BLUETOOTH"/> 2 <uses-permission android:name="android.permission.BLUETOOTH_ADMIN"/>
1 <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" /> 2 <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
1 <uses-feature android:name="android.hardware.bluetooth_le" android:required="true"/>
if (!getPackageManager().hasSystemFeature(PackageManager.FEATURE_BLUETOOTH_LE)) { //不支持ble finish(); }
BluetoothAdapter.LeScanCallback
接口,BLE设备的搜索结果将通过这个callback返回1 private ExecutorService mExecutor; 2 3 // Device scan callback. 4 private BluetoothAdapter.LeScanCallback mLeScanCallback = 5 new BluetoothAdapter.LeScanCallback() { 6 7 @Override 8 public void onLeScan(final BluetoothDevice device, int rssi, byte[] scanRecord) { 9 // 不能做太多事情,特别周围ble设备多的时候。容易出现错误或者ANR,需要把结果处理放到另外的线程去。 10 if(mExecutor == null) { 11 mExecutor = Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors() + 1); 12 } 13 new ScanProcessor().executeOnExecutor(mExecutor, new ScanData(device, rssi, scanRecord)); 14 } 15 16 };
ScanData:
1 private class ScanData { 2 public ScanData(BluetoothDevice device, int rssi, byte[] scanRecord) { 3 this.device = device; 4 this.rssi = rssi; 5 this.scanRecord = scanRecord; 6 } 7 8 int rssi; 9 BluetoothDevice device; 10 byte[] scanRecord; 11 }
扫描结果处理:
1 private class ScanProcessor extends AsyncTask<ScanData, Void, MingbikeBeacon> { 2 3 public ScanProcessor() { 4 } 5 6 @Override 7 protected Object doInBackground(ScanData... params) { 8 if (params == null || params.length == 0) 9 return null; 10 ScanData scanData = params[0]; 11 // todo scanData 12 return new Object(); 13 } 14 15 @Override 16 protected void onPostExecute(Object obj) { 17 // todo obj 18 } 19 20 @Override 21 protected void onPreExecute() { 22 } 23 24 @Override 25 protected void onProgressUpdate(Void... values) { 26 } 27 }
(2)关闭BLE设备的搜索
1 mBluetoothAdapter.stopLeScan(mLeScanCallback);
(3)开启BLE设备的搜索
1 mBluetoothAdapter.startLeScan(mLeScanCallback);
*************注意:搜索时,你只能搜索传统蓝牙设备或者BLE设备,两者完全独立,不可同时被搜索。
(4)使用BluetoothGatt 来连接设备
1 private BluetoothGatt mBluetoothGatt;
1 final BluetoothDevice device = mBluetoothAdapter.getRemoteDevice(address); // 蓝牙mac地址 2 if (device == null) { 3 Log.w(TAG, "Device not found. Unable to connect."); 4 return false; 5 } 6 mBluetoothGatt = device.connectGatt(this, false, mGattCallback);
BluetoothDevice.connectGatt(Context context, boolean autoConnect, BluetoothGattCallback callback);
参数autoConnect,如果为 true
的话,系统就会发起一个后台连接,等到系统发现了一个设备,就会自动连上,通常这个过程是非常慢的。为 false
的话,就会直接连接,通常会比较快。同样,BluetoothGatt.connect()
只能发起一个后台连接,不是直接连接。所以这个地方需要小心。
(5)使用BluetoothGattCallback 来跟设备进行通信
1 private BluetoothGattCallback mGattCallback = new BluetoothGattCallback() { 2 @Override 3 public void onConnectionStateChange(BluetoothGatt gatt, 4 int status, int newState) { 5 super.onConnectionStateChange(gatt, status, newState);
9 if (newState == BluetoothProfile.STATE_CONNECTED) { 10 setState(State.CONNECTED); 11 gatt.discoverServices(); 12 } else { 13 setState(State.IDLE); 14 } 15 } 16 17 @Override 18 public void onServicesDiscovered(BluetoothGatt gatt, 19 int status) { 20 if(status == BluetoothGatt.GATT_SUCCESS) { 21 Log.v(TAG, "onServicesDiscovered: " + status); 22 } 23 } 24 }
连接时会走这个方法onConnectionStateChange,传过来的新状态是连接状态,这时在这个方法中调用一下这句:mBluetoothGatt.discoverServices(),
mBluetoothGatt是连接完成时的对象,调用这句后,会走回调函数的onServicesDiscovered方法。在这个方法中去获取设备的一些服务,蓝牙通道,然后通过这些通道去发送数据给外设。
有些设备,在 onServicesDiscovered 回调中,返回的 status 是 129,133时,在关闭,重新打开蓝牙,无法解决问题时,建议更换设备,这些问题大多是设备底层gatt 服务异常,重新连接,进行discoverServices();
1 // 出现129,133时。关闭蓝牙 2 mBluetoothAdapter.disable(); 3 // 关闭蓝牙后,延时1s,重新开启蓝牙 4 mBluetoothAdapter.enable();
在蓝牙设备中, 其包含有多个BluetoothGattService, 而每个BluetoothGattService中又包含有多个BluetoothGattCharacteristic。
当onServicesDiscovered()回调的 status == BluetoothGatt.GATT_SUCCESS, 可以进行获取service。
(1)获取到设备中的服务列表 mBluetoothGatt.getServices(); 或者通过uuid 来获取某一个服务:
public List<BluetoothGattService> getSupportedGattServices() { if (mBluetoothGatt == null) return null; return mBluetoothGatt.getServices(); // 或者通过uuid来获取某一个服务 mBluetoothGatt.getServices(uuid); }
(2)通过Gatt这个对像,就是蓝牙连接完成后获取到的对象,通过这个对象设置好指定的通道向设备中写入和读取数据。
在onServicesDiscovered(BluetoothGatt gatt, int status) 回调中, 获取服务对象,获取读取、写入、描述的特征。
1 // 获取指定uuid的服务 2 BluetoothGattService service = gatt.getService(BluetoothUUID.bleServerUUID); 3 // 获取读取特征 4 BluetoothGattCharacteristic readCharacteristic = service.getCharacteristic(BluetoothUUID.readDataUUID); 5 // 获取写入特征 6 writeCharacteristic = service.getCharacteristic(BluetoothUUID.writeDataUUID); 7 gatt.setCharacteristicNotification(readCharacteristic, true); 8 // 获取描述特征 9 BluetoothGattDescriptor descriptor = readCharacteristic.getDescriptor(BluetoothUUID.CLIENT_CHARACTERISTIC_CONFIG); 10 // 要看外围设备的设置 11 if (isNotify) { 12 descriptor.setValue(BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE); 13 } else { 14 descriptor.setValue(BluetoothGattDescriptor.ENABLE_INDICATION_VALUE); 15 } 16 gatt.writeDescriptor(descriptor);
服务中, 也可以通过调用 gattService.getCharacteristics():获得Characteristic 集合.
在 Characteristic中, 可以通过 mBluetoothGatt.readCharacteristic(characteristic); 来读取其里面的数据, 其结果在mGattCallback 回调函数中获取.
写如数据:
mCurrentcharacteristic.setValue(data);
mBluetoothGatt.wirteCharacteristic(mCurrentcharacteristic);
1 public void wirteCharacteristic(BluetoothGattCharacteristic characteristic) { 2 3 if (mBluetoothAdapter == null || mBluetoothGatt == null) { 4 Log.w(TAG, "BluetoothAdapter not initialized"); 5 return; 6 } 7 8 mBluetoothGatt.writeCharacteristic(characteristic); 9 }
1 public void readCharacteristic(BluetoothGattCharacteristic characteristic) { 2 if (mBluetoothAdapter == null || mBluetoothGatt == null) { 3 Log.w(TAG, "BluetoothAdapter not initialized"); 4 return; 5 } 6 mBluetoothGatt.readCharacteristic(characteristic); 7 }
当外围设备,调用:
BluetoothGattServer.class
notifyCharacteristicChanged(BluetoothDevice device,
BluetoothGattCharacteristic characteristic, boolean confirm);
就会触发中心设备的onCharractersticChanged(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic) 方法回调:
1 @Override 2 public void onCharacteristicChanged(BluetoothGatt gatt, 3 BluetoothGattCharacteristic characteristic) { 4 byte[] values = characteristic.getValue(); 5 // todo values 6 }
注意:BluetoothGattCharacteristic这个是指定的通道,蓝牙服务:
1 BluetoothGattCharacteristic characteristic = null; 2 characteristic = mGattCharacteristics.get(4).get(4);
这两个数字就是从指定的服务中找到你要发送数据的那个服务。
如果要进行多个连接,每次连接完成后可以将BluetoothGatt的对象放到一个list里面,获取到的服务也放到一个List里面,然后发送数据的时候调用不同的Gatt发送不同的通道数据即可。
参考: