How Could I Achieve Maximum Thread Safety With A Read/write Ble Gatt Characteristic?
Solution 1:
In SDK27, the onNotify()
callback function in BluetoothGatt.java was updated to call BOTH BluetoothGattCharacteristic.setValue()
and BluetoothGattCallback.onCharacteristicChanged()
in the Runnable'srun()
.
This change allows us to force all calls to BluetoothGattCharacteristic.setValue()
- both for our outbound writing to the characteristic and the inbound notifications - onto the same thread, which eliminates the race condition corrupting the BluetoothGattCharacteristic.mValue
;
- Create a
HandlerThread
- Create a
Handler
attached to yourHandlerThread
- Pass your
Handler
intoBluetoothDevice.connectGatt()
- congratulations, when a notify is received thesetValue()
andonCharacteristicChanged()
will be called on yourHandlerThread
. - When you want to write to the characteristic, post your
setValue()
andwriteCharacteristic()
to yourHandlerThread
via your Handler
Now all the function calls that were involved in the race condition are being executed on the same thread, eliminating the race condition.
Solution 2:
Before SDK27, it appears to be impossible to have reliable full duplex communication over a single characteristic - using the public API.
Quite vexing.
There appears to be a way, though, if you're willing to cheat a little. (There is probably more than one way to cheat; the one described below is fairly low-impact.)
The problem is the use of the mValue
field in BluetoothGattCharacteristic for two conflicting purposes - send and receive. It is fairly bad API design; one API-level fix would have been to introduce another field, so that there would be one for each direction. Another would have been to not use the setValue() method when sending data, but instead provided the data in question as a parameter to writeCharacteristic() (though this is complicated by the fact that the value field may be encoded during get/set, supporting several data types). However, it appears that the API maintainers have chosen a different path.
Anyway - to fix the problem, ensure that the received value and the value-to-be-sent are stored in different locations. More specifically, in the value fields of two different instances of BluetoothGattCharacteristic.
Now, cloning a BGC is not possible using the official API.
Using the public constructor, you can get an instance which has all fields set except service
and instanceId
. These have public getters - to set them, use reflection to access setService()
and setInstanceId()
- this is the cheaty part.
I have tested this approach[1], and it appears to work as desired. :-)
[1] A variation of this approach, in fact; in newer SDK versions, the class in question is Parcelable, so when possible I clone the object through Parcel-serialization, just in case future implementations add more fields. This takes care of everything except the service
field, which you still need to set using reflection.
Post a Comment for "How Could I Achieve Maximum Thread Safety With A Read/write Ble Gatt Characteristic?"