问题
It's my code to enable characteristic notification.
private void enableNotification(BluetoothGattCharacteristic characteristic) {
boolean s = bluetoothGatt.setCharacteristicNotification(characteristic, true);
Log.d(TAG, "enableNotification: setCharacteristicNotification " + s);
List<BluetoothGattDescriptor> descriptors = characteristic.getDescriptors();
if (null != descriptors && descriptors.size() > 0) {
for (BluetoothGattDescriptor descriptor : descriptors) {
Log.d(TAG, "enableNotification: " + descriptor.getUuid());
descriptor.setValue(BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE);
boolean s1 = bluetoothGatt.writeDescriptor(descriptor);
Log.d(TAG, "enableNotification: writeDescriptor " + s1);
}
} else {
Log.d(TAG, "enableNotification: descriptors is null");
}
}
The following are the logs
BluetoothGatt: setCharacteristicNotification() - uuid: 00002a4d-0000-1000-8000-00805f9b34fb enable: true
BleService: enableNotification: setCharacteristicNotification true
BleService: enableNotification: 00002902-0000-1000-8000-00805f9b34fb
BleService: enableNotification: writeDescriptor true
BluetoothGatt: onConnectionUpdated() - Device=5C:B6:CC:00:1E:23 interval=40 latency=4 timeout=600 status=0
As we can see, writeDescriptor return true, but the onDescriptorWrite() method is not trigger, and the BluetoothGatt shows the log onConnectionUpdated(). If someone could tell why my notify have no trigger. the following is my onDescriptorWrite code in BluetoothGattCallback, if the code executed, in any case there will have some logs.
@Override
public void onDescriptorWrite(BluetoothGatt gatt, BluetoothGattDescriptor descriptor, int status) {
super.onDescriptorWrite(gatt, descriptor, status);
if (status == BluetoothGatt.GATT_SUCCESS) {
Log.d(TAG, "onDescriptorWrite: GATT_SUCCESS");
connectCallback.readMeterId();
} else if (status == BluetoothGatt.GATT_FAILURE) {
Log.d(TAG, "onDescriptorWrite: GATT_FAILURE");
} else {
Log.d(TAG, "onDescriptorWrite: something");
}
}
my BluetoothGattCallback code as follows
private BluetoothGattCallback bluetoothGattCallback = new BluetoothGattCallback() {
@Override
public void onConnectionStateChange(BluetoothGatt gatt, int status, int newState) {
super.onConnectionStateChange(gatt, status, newState);
if (newState == BluetoothProfile.STATE_CONNECTED) {
bluetoothGatt.discoverServices();
} else if (newState == BluetoothProfile.STATE_DISCONNECTED) {
gatt.close();
Log.d(TAG, "onConnectionStateChange: DISCONNECTED");
} else {
Log.d(TAG, "onConnectionStateChange: FAIL");
}
}
@Override
public void onServicesDiscovered(BluetoothGatt gatt, int status) {
super.onServicesDiscovered(gatt, status);
if (status == BluetoothGatt.GATT_SUCCESS) {
//print all the service and characteristic
for (BluetoothGattService service : bluetoothGatt.getServices()) {
Log.d(TAG, "onServicesDiscovered: service ->" + service.getUuid());
for (BluetoothGattCharacteristic characteristic : service.getCharacteristics()) {
Log.d(TAG, "onServicesDiscovered: characteristic ->" + characteristic.getUuid());
}
}
BluetoothGattService service = bluetoothGatt.getService(UUID.fromString(BleConstant.SERVICE_UUID.SERVICE));
characteristics = service.getCharacteristics().subList(0, 2);
// enableNotification(characteristics.get(0));
for (BluetoothGattCharacteristic characteristic : characteristics) {
Log.d(TAG, "onServicesDiscovered: Properties -> " + characteristic.getUuid() + " " + characteristic.getProperties());
enableNotification(characteristic);
}
} else {
Log.d(TAG, "onServicesDiscovered received: " + status);
}
}
@Override
public void onCharacteristicChanged(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic) {
Log.d(TAG, "onCharacteristicChanged: " + Arrays.toString(characteristic.getValue()));
Log.d(TAG, "onCharacteristicChanged: string -> " + characteristic.getStringValue(0));
String callbackDataString = characteristic.getStringValue(0);
byte[] callbackDataByte = characteristic.getValue();
// boolean checkData = CRC16Util.getInstance().verification(callbackDataString);
dosomething with the response date
if (CRC16Util.getInstance().verification(callbackDataString)) {
connectCallback.crcError();
}
}
@Override
public void onCharacteristicRead(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, int status) {
Log.d(TAG, "onCharacteristicRead: " + Arrays.toString(characteristic.getValue()));
}
@Override
public void onCharacteristicWrite(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, int status) {
Log.d(TAG, "onCharacteristicWrite: " + Arrays.toString(characteristic.getValue()));
}
@Override
public void onDescriptorWrite(BluetoothGatt gatt, BluetoothGattDescriptor descriptor, int status) {
super.onDescriptorWrite(gatt, descriptor, status);
if (status == BluetoothGatt.GATT_SUCCESS) {
Log.d(TAG, "onDescriptorWrite: GATT_SUCCESS");
connectCallback.readMeterId();
} else if (status == BluetoothGatt.GATT_FAILURE) {
Log.d(TAG, "onDescriptorWrite: GATT_FAILURE");
} else {
Log.d(TAG, "onDescriptorWrite: something");
}
}
};
in this code connectCallback
is a interface like this
public interface ConnectCallback {
/**
* It needs to be triggered after notify successful
*/
void readMeterId();
/**
* callback of CRC ERROR
*/
void crcError();
/**
* callback of Transmission completed
*/
void onComplete();
/**
* callback of Read MeterId Error
*/
void onReadMeterIdError();
}
I only send command to ble when I onDescriptorWrite success(notify success)
The following is my connect code
public boolean connectToDevice(final String address, ConnectCallback connectCallback) {
if (null == bluetoothAdapter || null == address) {
Log.d(TAG, "BluetoothAdapter not initialized or unspecified address.");
return false;
}
if (null != bluetoothGatt) {
bluetoothGatt = null;
}
final BluetoothDevice device = bluetoothAdapter.getRemoteDevice(address);
if (device == null) {
Log.d(TAG, "Device not found. Unable to connect.");
return false;
}
Log.d(TAG, "Trying to create a new connection.");
this.connectCallback = connectCallback;
bluetoothGatt = device.connectGatt(this, false, bluetoothGattCallback);
return true;
}
I write all the ble code in Service and use it in an activity like this
private void bindBleService() {
serviceConnection = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
BleService.LocalBinder binder = (BleService.LocalBinder) service;
bleService = binder.getService();
if (!bleService.init()) {
Log.e(TAG, "Unable to initialize Bluetooth");
finish();
}
boolean isConn = bleService.connectToDevice(deviceAddress, new BleService.ConnectCallback() {
@Override
public void readMeterId() {
notifySuccess = true;
runOnUiThread(() -> {
waitBleDialog.setTitle(getString(R.string.edit_read_meter_id));
waitBleDialog.show();
});
bleService.sendDataToBle(READ_METER_ID.OCP);
meterIdUsed.put(READ_METER_ID.OCP, true);
}
@Override
public void crcError() {
if (waitBleDialog.isShowing()) {
waitBleDialog.cancel();
}
runOnUiThread(() -> {
Toast.makeText(BleResultActivity.this, getString(R.string.ble_crc_error), Toast.LENGTH_SHORT).show();
finish();
});
}
@Override
public void onComplete() {
if (recordList.size() == 0) {
if (waitBleDialog.isShowing()) {
waitBleDialog.cancel();
}
runOnUiThread(() -> Toast.makeText(BleResultActivity.this, getString(R.string.edit_no_new_record), Toast.LENGTH_SHORT).show());
recordTestButton.setOnClickListener(v -> runOnUiThread(() -> Toast.makeText(BleResultActivity.this, getString(R.string.edit_no_new_record), Toast.LENGTH_SHORT).show()));
} else {
runOnUiThread(() -> {
recordTestButton.setEnabled(true);
Toast.makeText(BleResultActivity.this, getString(R.string.edit_transmit_finish), Toast.LENGTH_SHORT).show();
});
recordTestButton.setOnClickListener(recordTest);
}
}
@Override
public void onReadMeterIdError() {
boolean haveCommendNotUsed = true;
for (String command : meterIdUsed.keySet()) {
Boolean commandUsed = meterIdUsed.get(command);
if (null == commandUsed) {
commandUsed = false;
}
if (!commandUsed) {
haveCommendNotUsed = true;
bleService.sendDataToBle(command);
meterIdUsed.put(command, true);
break;
} else {
haveCommendNotUsed = false;
}
}
if (!haveCommendNotUsed) {
waitBleDialog.cancel();
runOnUiThread(() -> Toast.makeText(BleResultActivity.this, getString(R.string.edit_read_meter_id_failed), Toast.LENGTH_SHORT).show());
finish();
}
}
});
if (!isConn) {
Log.d(TAG, "onServiceConnected: false");
}
}
@Override
public void onServiceDisconnected(ComponentName name) {
bleService = null;
}
};
final Intent intent = new Intent(this, BleService.class);
bindService(intent, serviceConnection, Service.BIND_AUTO_CREATE);
}
What I know now is that after run the enableNotification() method and the writeDescriptor() return true, the onConnectionUpdated() happened and I lose my connection. The onConnectionUpdated() is hide in souce code, and I don't know why it occurs and how to deal with it.
回答1:
It seems your app is not connected to your BLE device.
To know whether the connection is succesful, you need to check the given status
is equal to BluetoothGatt.GATT_SUCCESS
in onConnectionStateChange()
.
Also, check for possible GATT_CONN_TIMEOUT:
@Override
public void onConnectionStateChange(BluetoothGatt gatt, int status, int newState) {
super.onConnectionStateChange(gatt, status, newState);
if ((newState == BluetoothProfile.STATE_CONNECTED) && (status == BluetoothGatt.GATT_SUCCESS)) {
bluetoothGatt.discoverServices();
} else if ((newState == BluetoothProfile.STATE_DISCONNECTED) && (status == BluetoothGatt.GATT_SUCCESS)) {
gatt.close();
Log.d(TAG, "onConnectionStateChange: DISCONNECTED");
} else if (status == GATT_CONN_TIMEOUT) {
Log.e(TAG, "GATT_CONN_TIMEOUT !");
} else {
Log.e(TAG, "onConnectionStateChange: FAIL");
}
}
来源:https://stackoverflow.com/questions/58555820/ble-ondescriptorwrite-is-not-trigger-when-writedescriptor-receive-true