目录
一、前言
上一篇文章我们讲述了低功耗蓝牙设备开发过程中的广播数据解析,对于常见的应用场景而言,做到解析广播数据已经够了,况且一旦中心设备和外围设备建立连接的话,广播就会终止。但是对于蓝牙设备管理员而言,就需要连接上外围设备,然后进行设备参数的修改;
二、连接到GATT Server
连接到GATT Server,需要用到BluetoothGatt这个对象,它是蓝牙GATT配置文件的公共API.我们通过BluetoothGatt对象进行连接的建立和断开,Gatt Services数据的获取,Gatt Characteristics数据的获取和读写操作,Gatt Descriptor数据的读写操作等;
我们先看一下,BluetoothGatt的常用API:
- connect():重连方法。当外围设备和中心设备蓝牙断开连接时,用于进行重新连接。如果外围设备超出范围,则等在范围内时就会触发重连;
- connect(Boolean autoConnect, BluetoothGattCallback callback,Handler handler):
与支持蓝牙GATT的设备建立连接。第二个参数是否自动连接;
- disconnect():断开连接。可以是已经建立的连接或取消正在建立的连接;
- discoverServices():发现服务。一般用于当GATT连接建立之后,使用BluetoothGatt对象调用该方法来获取外围设备的Gatt Services,Gatt Characteristics,Gatt Descriptors数据。当服务数据发现完毕之后会回调BluetoothGattCallback的onServicesDiscovered方法,此时我们使用getServices就可以获取外围设备的Gatt Services数据。
- getService(UUID uuid):返回特定UUID的BluetoothGattService。前提条件是必须针对特定外围设备去获取Gatt Service,也就是说建立连接,获取到BluetoothGatt对象才可以使用此方法。同时需要注意可能返回null,如果存在多个具有相同UUID的Gatt Service,默认返回第一个。
- readCharacteristic(BluetoothGattCharacteristic characteristic):读取相关联外围设备的BluetoothGattCharacteristic.这是异步操作,该操作结果会在BluetoothGattCallback的onCharacteristicRead回调方法里返回。
- writeCharacteristic(BluetoothGattCharacteristic characteristic):将BluetoothGattCharacteristic 写入相关联外围设备。一旦写操作完成会在BluetoothGattCallback的onCharacteristicWrite方法中回调。
- readDescriptor(BluetoothGattDescriptor descriptor):读取相关联外围设备的BluetoothGattDescriptor。一旦读操作完成会在BluetoothGattCallback的onDescriptorRead方法进行回调。
- writeDescriptor(BluetoothGattDescriptor descriptor):将BluetoothGattDescriptor写入相关联外围设备。一旦写操作完成会在BluetoothGattCallback的onDescriptorWrite方法里进行回调。
- setCharacteristicNotification(BluetoothGattCharacteristic characteristic, boolean enable):
开启或禁用给定Gatt Characteristic的notifications(通知)/indications(指示)。一旦notifications设置为true的话,如果远程设备的Gatt Characteristic发生改变则BluetoothGattCallback的onCharacteristicChanged方法就会触发。
- readRemoteRssi():读取已连接的远程设备的RSSI。当RSSI的值被读取之后,会回调BluetoothGattCallback的onReadRemoteRssi方法。
- readPhy():读取连接的当前发送器PHY和接收器PHY。最终结果会在BluetoothGattCallback的onPhyRead方法里回调。
- requestMtu(int mtu):针对当前连接请求MTU大小。当我们使用BluetoothGatt对象调用writeCharacteristic()方法进行数据写入操作时,如果数据大小超过MTU的默认大小时,会被分拆为多个小包进行数据写入的操作。该方法允许开发者修改MTU的大小。会触发BluetoothGattCallback的onMtuChanged方法回调。
1.建立连接:
中心设备(手机等)有时候需要与外围设备(低功耗蓝牙)建立连接,进行参数的修改等操作。当然并不是所有的低功耗蓝牙设备都能够被连接上,这个就要看硬件开发者设计的产品允不允许被连接。这些我们不在考虑。建立连接首先需要扫描到设备,设备的扫描不是本篇文章主要的讨论对象,不清楚的童鞋可以阅读低功耗蓝牙开发之设备扫描。扫描到设备之后拿到设备的信息,用来进行连接的建立。示例代码如下:
/**
* Connects to the GATT server hosted on the Bluetooth LE device.
*
* @param address The device address of the destination device.
* @return Return true if the connection is initiated successfully. The connection result
* is reported asynchronously through the
* {@code BluetoothGattCallback#onConnectionStateChange(android.bluetooth.BluetoothGatt, int, int)}
* callback.
*/
@RequiresApi(Build.VERSION_CODES.JELLY_BEAN_MR2)
public boolean connect(@NonNull String address) {
MLogger.i(TAG, TAG + " .. connect()~");
if (TextUtils.isEmpty(address)) {
MLogger.e(TAG, "unspecify address");
return false;
}
if (mBluetoothAdapter == null) {
MLogger.d(TAG, "BluetoothAdapter is not initialized");
return false;
}
// Previously connected device. Try to reconnect.
if (!TextUtils.isEmpty(mBluetoothDeviceAddress) && address.equals(mBluetoothDeviceAddress)
&& mBluetoothGatt != null) {
MLogger.d(TAG, "Trying to use an existing mBluetoothGatt for connection.");
if (mBluetoothGatt.connect()) {
mConnectionState = BeaconContants.GATT_STATE_CONNECTING;
return true;
} else {
return false;
}
}
final BluetoothDevice device = mBluetoothAdapter.getRemoteDevice(address);
if (device == null) {
MLogger.w(TAG, "Device not found. Unable to connect.");
return false;
}
// We want to directly connect to the device, so we are setting the autoConnect
// parameter to false.
mBluetoothGatt = device.connectGatt(this, false, mGattCallback);
MLogger.d(TAG, "Trying to create a new connection.");
mBluetoothDeviceAddress = address;
mConnectionState = BeaconContants.GATT_STATE_CONNECTING;
return true;
}
在这里需要用到外围设备的Mac地址用来连接Gatt服务。
当连接建立成功之后,会触发BluetoothGattCallback的onConnectionStateChange方法。在回调方法里我们就可以使用BluetoothGatt的对象,调用mBluetoothGatt.discoverServices()就可以发现外围设备Gatt的各种数据信息。当服务数据发现完毕之后会回调BluetoothGattCallback的onServicesDiscovered方法,此时我们使用getServices就可以获取外围设备的Gatt Services数据。
2.读取Gatt Characterstic
我们提供一个BluetoothGattCharacteristic对象就可以读取它的特征值。示例代码如下:
/**
* Reads the requested characteristic from the associated remote device.
*
* <p>This is an asynchronous operation. The result of the read operation
* is reported by the {@link BluetoothGattCallback#onCharacteristicRead}
* callback.
*
* <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
*
* @param characteristic The characteristic to read from.
* @return true, if the read operation was initiated successfully
* @see BluetoothGatt#readCharacteristic(BluetoothGattCharacteristic)
*/
@RequiresApi(Build.VERSION_CODES.JELLY_BEAN_MR2)
public boolean readCharacteristic(@NonNull BluetoothGattCharacteristic characteristic) {
if (mBluetoothAdapter != null && mBluetoothGatt != null) {
if (mConnectionState == BeaconContants.GATT_STATE_CONNECTED) {
final boolean state = mBluetoothGatt.readCharacteristic(characteristic);
MLogger.d(TAG, "readCharacteristic..state : " + state);
return state;
} else {
MLogger.e(TAG, "GATT client has disconnected from GATT server!");
}
} else {
MLogger.d(TAG, "BluetoothAdapter not initialized");
}
return false;
}
当我们读取操作成功之后,会回调BluetoothGattCallback的onCharacteristicRead方法。
3.写入 Gatt Characteristics:
实际场景中我们会有修改外围设备参数的需求,我们只要通过特定的服务的UUID拿到需要服务对象就可以进行参数的修改。示例代码如下:
/**
* Writes a given characteristic and its values to the associated remote device.
*
* <p>Once the write operation has been completed, the
* {@link BluetoothGattCallback#onCharacteristicWrite} callback is invoked,
* reporting the result of the operation.
*
* <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
*
* @param characteristic Characteristic to write on the remote device.
* @return true, if the write operation was initiated successfully
* @see BluetoothGatt#writeCharacteristic(BluetoothGattCharacteristic)
*/
@RequiresApi(Build.VERSION_CODES.JELLY_BEAN_MR2)
public boolean writeCharacteristic(@NonNull BluetoothGattCharacteristic characteristic) {
if (mBluetoothAdapter != null && mBluetoothGatt != null) {
if (mConnectionState == BeaconContants.GATT_STATE_CONNECTED) {
final boolean state = mBluetoothGatt.writeCharacteristic(characteristic);
MLogger.d(TAG, "writeCharacteristic..state : " + state
+ "..writetype : " + characteristic.getWriteType());
return state;
} else {
MLogger.e(TAG, "GATT client has disconnected from GATT server!");
}
} else {
MLogger.d(TAG, "BluetoothAdapter not initialized");
}
return false;
}
当我们的写操作成功之后会触发BluetoothGattCallback的onCharacteristicWrite回调方法,如果成功修改会回调onCharacteristicChanged方法。
iBeacon设备的参数修改就是基于该方法进行修改,不了解iBeacon设备的参数的童鞋,请阅读:iBeacon参数。
4.打开通知功能:
如果我们需要修改外围设备的参数,最好还是需要打开蓝牙的notification功能,这样的话修改特定的BluetoothGattCharacteristic就可以接收到通知。同时安全起见,打开通知功能会触发Android系统的输入配对码进行配对的功能。示例代码如下:
/**
* Enable or disable notifications/indications for a given characteristic.
*
* @param characteristic The characteristic for which to enable notifications
* @param enable Set to true to enable notifications
* @param uuid TO get a descriptor with a given UUID out of the list of
* descriptors for this characteristic.
* @return true, if the requested notification status was set successfully
* @see BluetoothGatt#setCharacteristicNotification(BluetoothGattCharacteristic, boolean)
*/
@RequiresApi(Build.VERSION_CODES.JELLY_BEAN_MR2)
public boolean setCharacteristicNotification(@NonNull BluetoothGattCharacteristic characteristic,
@NonNull boolean enable, UUID uuid) {
if (mBluetoothAdapter != null && mBluetoothGatt != null) {
if (mConnectionState == BeaconContants.GATT_STATE_CONNECTED) {
mBluetoothGatt.setCharacteristicNotification(characteristic, enable);
final BluetoothGattDescriptor descriptor = characteristic.getDescriptor(uuid);
if (descriptor != null) {
MLogger.d(TAG, "It's going to set notifition value : " + enable);
descriptor.setValue(enable ? BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE :
BluetoothGattDescriptor.DISABLE_NOTIFICATION_VALUE);
final boolean state = mBluetoothGatt.writeDescriptor(descriptor);
MLogger.d(TAG, "setCharacteristicNotification..operation state : " + state);
return state;
} else {
MLogger.e(TAG, "The descriptor is null or no descriptor with the given UUID was found! ");
}
} else {
MLogger.e(TAG, "GATT client has disconnected from GATT server!");
}
} else {
MLogger.d(TAG, "BluetoothAdapter not initialized");
}
return false;
}
执行这段代码,会产生如下结果:
- 当打开通知功能时,会触发Android系统的蓝牙配对功能,如果配对码输入正确且没有超时时onBondStateChanged方法会有配对状态的变化,onDescriptorWrite方法会有设置的状态值的数组,和设置状态;当配对码输入错误,取消配对或者配对超时,onBondStateChanged会返回配对状态变化,onConnectionStateChange会返回连接状态的变化;(注意:配对码错误或者配对超时,会导致连接断开!)
- 当关闭Notify功能时,onDescriptorWrite会返回关闭Notify对应数组和状态;
5.关闭Gatt Client:
当我们使用完毕之后,需要关闭Gatt连接,同时释放掉资源。
/**
* After using a given BLE device, the app must call this method to ensure resources are
* released properly.
*/
public void close() {
MLogger.d(TAG, TAG + " .. close()~");
if (mBluetoothGatt == null) {
return;
}
mBluetoothGatt.close();
mBluetoothGatt = null;
}