一、前言
Android中的readCharacteristic
是蓝牙低功耗(BLE)通信中的一个重要操作,它允许客户端(如智能手机、智能手表等)读取远程BLE设备(如心率传感器、血压计等)中特定特征(Characteristic)的数据。这个过程遵循通用属性规范(GATT)协议,该协议负责BLE通信过程中的数据传输管理。
1.1. 概念与用途
- 概念:
readCharacteristic
是BLE API中的一个方法,用于从已连接的BLE设备的特定服务(Service)中获取特征(Characteristic)的数据。 - 用途:通过读取BLE设备上的特征,可以获取各种类型的数据,如健康数据(心率、步数等)、环境数据(温度、湿度等)或其他自定义数据。这些数据可用于实现数据交换、协议触发、状态监控等功能。
1.2. 使用场景
readCharacteristic
方法属于 BluetoothGatt
类,是 Android BLE API 的一部分,想要从连接的 BLE 设备中获取某个特征(Characteristic)的当前值时会使用这个方法。例如,想要读取一个心率监测器的当前心率值,或者从温度传感器获取温度读数。
boolean readCharacteristic(BluetoothGattCharacteristic characteristic)
-
characteristic
:BluetoothGattCharacteristic
类型的对象,代表想要读取的蓝牙特征。这个对象应该已经被发现并且属于一个已经连接的蓝牙设备。
1.3. 操作过程(以BLE通信为例)
- 建立连接:首先,客户端需要与BLE设备建立连接。这通常通过调用BluetoothGatt的connect()方法实现,并在BluetoothGattCallback的onConnectionStateChange()回调中处理连接状态变化。
- 发现服务:连接成功后,客户端需要调用BluetoothGatt的discoverServices()方法来发现BLE设备上的所有服务。这些服务包含了设备的一组相关功能,如心率监测、电池状态等。
- 查找特征:在onServicesDiscovered()回调中,客户端会遍历所有发现的服务,并使用每个服务的getCharacteristic(UUID characteristicUuid)方法来查找包含目标数据的特征。UUID是特征的唯一标识符。
- 检查属性:在尝试读取特征之前,客户端应检查特征的属性(通过调用BluetoothGattCharacteristic.getProperties()),以确保该特征支持读操作(如BluetoothGattCharacteristic.PROPERTY_READ)。
- 读取数据:如果特征支持读操作,客户端可以调用BluetoothGatt的readCharacteristic(BluetoothGattCharacteristic characteristic)方法来请求读取特征的数据。这是一个异步操作,结果将通过BluetoothGattCallback的onCharacteristicRead()回调返回。
- 处理结果:在onCharacteristicRead()回调中,客户端应检查返回的状态码。如果状态码为BluetoothGatt.GATT_SUCCESS,则表示读取操作成功,可以使用BluetoothGattCharacteristic.getValue()方法来获取特征的数据。数据以字节数组(byte[])的形式返回,可能需要根据特征的描述或设备的协议来解析。
-
错误处理: 如果读取操作失败,需要在onCharacteristicRead回调中处理错误情况。可能的错误原因包括设备不支持读取操作、设备已断开连接、或者发生了BLE协议栈错误等。
-
清理和后续操作:
-
读取完成后,可能需要更新UI以显示数据,或者根据读取到的数据执行其他操作。
-
如果有必要,记得在不再需要时关闭与BLE设备的连接,以释放资源。
1.4. 注意事项
- 设备连接:在调用
readCharacteristic
方法之前,确保应用与 BLE 设备建立了连接。 - 特征权限:确保有权限读取该特征。BLE 特征可能具有不同的属性(如只读、只写、通知等),需要检查特征的属性来确定是否可以读取它。
- 异步操作:读取操作是异步的。意味着
readCharacteristic
方法会立即返回,而实际的读取操作会在后台进行。需要通过实现BluetoothGattCallback
接口的onCharacteristicRead
方法来接收读取结果。 - 错误处理:如果读取操作失败(如,由于设备断开连接或特征不支持读取),
onCharacteristicRead
方法中的status
参数将包含错误代码。应该检查这个状态并相应地处理错误。
1.5. 示例
// 假设已经有了 BluetoothGatt 对象 gatt 和 BluetoothGattCharacteristic 对象 characteristic
if (gatt.readCharacteristic(characteristic)) {
// 请求开始,但结果将在 onCharacteristicRead 中异步返回
} else {
// 请求未能开始,可能是因为设备未连接或特征不支持读取
}
// 需要在BluetoothGattCallback中实现onCharacteristicRead方法来处理读取结果
@Override
public void onCharacteristicRead(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, int status) {
if (status == BluetoothGatt.GATT_SUCCESS) {
// 读取成功,处理 characteristic.getValue()
} else {
// 读取失败,处理错误
}
}
1.6. 小结
readCharacteristic
是Android BLE通信中用于读取远程设备数据的关键操作。通过遵循上述步骤和注意事项,我们可以高效地实现BLE设备之间的数据交换和状态监控功能。
二、源码分析
2.1. BluetoothGatt.readCharacteristic
/packages/modules/Bluetooth/android/app/src/com/android/bluetooth/gatt/GattService.java
public void readCharacteristic(int clientIf, String address, int handle, int authReq,
AttributionSource attributionSource, SynchronousResultReceiver receiver) {
try {
readCharacteristic(clientIf, address, handle, authReq, attributionSource);
receiver.send(null);
} catch (RuntimeException e) {
receiver.propagateException(e);
}
}
private void readCharacteristic(int clientIf, String address, int handle, int authReq,
AttributionSource attributionSource) {
GattService service = getService();
if (service == null) {
return;
}
service.readCharacteristic(clientIf, address, handle, authReq, attributionSource);
}
void readCharacteristic(int clientIf, String address, int handle, int authReq,
AttributionSource attributionSource) {
if (!Utils.checkConnectPermissionForDataDelivery(
this, attributionSource, "GattService readCharacteristic")) {
return;
}
if (VDBG) {
Log.d(TAG, "readCharacteristic() - address=" + address);
}
Integer connId = mClientMap.connIdByAddress(clientIf, address);
if (connId == null) {
Log.e(TAG, "readCharacteristic() - No connection for " + address + "...");
return;
}
try {
permissionCheck(connId, handle);
} catch (SecurityException ex) {
String callingPackage = attributionSource.getPackageName();
// Only throws on apps with target SDK T+ as this old API did not throw prior to T
if (checkCallerTargetSdk(this, callingPackage, Build.VERSION_CODES.TIRAMISU)) {
throw ex;
}
Log.w(TAG, "readCharacteristic() - permission check failed!");
return;
}
mNativeInterface.gattClientReadCharacteristic(connId, handle, authReq);
}
在Android系统中,readCharacteristic允许外部调用者请求读取某个蓝牙设备的特定GATT特征。涉及权限检查、连接管理、异常处理和本地方法调用等多个方面,以确保数据的安全传输和设备的正确交互。
执行读取操作:如果通过了所有检查,最终会调用mNativeInterface.gattClientReadCharacteristic方法,用于通过蓝牙协议栈发送读取GATT特征的请求。参数connId、handle和authReq分别代表连接ID、特征句柄和授权请求。
2.1.1. gattClientReadCharacteristic
/packages/modules/Bluetooth/android/app/src/com/android/bluetooth/gatt/GattNativeInterface.java
/**
* Read a characteristic by the given handle
*/
public void gattClientReadCharacteristic(int connId, int handle, int authReq) {
gattClientReadCharacteristicNative(connId, handle, authReq);
}
2.1.2. gattClientReadCharacteristicNative
static void gattClientReadCharacteristicNative(JNIEnv* /* env */,
jobject /* object */,
jint conn_id, jint handle,
jint authReq) {
if (!sGattIf) return;
sGattIf->client->read_characteristic(conn_id, handle, authReq);
}
2.2. btif_gattc_read_char
/packages/modules/Bluetooth/system/btif/src/btif_gatt_client.cc
static bt_status_t btif_gattc_read_char(int conn_id, uint16_t handle,
int auth_req) {
CHECK_BTGATT_INIT();
return do_in_jni_thread(Bind(&BTA_GATTC_ReadCharacteristic, conn_id, handle,
auth_req, read_char_cb, nullptr));
}
在 Bluedroid协议栈中,btif_gatt_client.cc
是文件是负责处理蓝牙 GATT 客户端(即设备作为蓝牙客户端与其他蓝牙设备通信时)相关操作的源代码文件之一。btif_gattc_read_char
函数是 Android 蓝牙堆栈中用于从远程蓝牙设备读取特征值的接口。它通过 JNI 线程安全地调用底层的 BTA_GATTC_ReadCharacteristic
函数来执行实际的读取操作,并在操作完成时通过回调函数 read_char_cb
通知上层。这种设计的目的是允许蓝牙协议栈的开发者在 Java 和 C/C++ 之间灵活地切换,同时保持代码的清晰和可维护性。
do_in_jni_thread(...)
:这个宏的作用是将给定的操作(读取特征的操作)放入 JNI 线程中执行。JNI(Java Native Interface)是 Java 和本地代码(如 C/C++)之间的桥梁。在 Android 的蓝牙协议栈中,很多与 Java 层交互的操作都需要在 JNI 线程中执行,以确保线程安全和正确的交互。Bind(&BTA_GATTC_ReadCharacteristic, ...)
:通过函数指针将BTA_GATTC_ReadCharacteristic
函数与必要的参数(conn_id
,handle
,auth_req
)以及回调函数read_char_cb
(当读取操作完成时会被调用)和nullptr
绑定在一起。BTA_GATTC_ReadCharacteristic
是负责实际执行读取操作的函数。
2.2.1. BTA_GATTC_ReadCharacteristic
/packages/modules/Bluetooth/system/bta/gatt/bta_gattc_api.cc
void BTA_GATTC_ReadCharacteristic(uint16_t conn_id, uint16_t handle,
tGATT_AUTH_REQ auth_req,
GATT_READ_OP_CB callback, void* cb_data) {
tBTA_GATTC_API_READ* p_buf =
(tBTA_GATTC_API_READ*)osi_calloc(sizeof(tBTA_GATTC_API_READ));
p_buf->hdr.event = BTA_GATTC_API_READ_EVT;
p_buf->hdr.layer_specific = conn_id;
p_buf->is_multi_read = fal