我正在使用支持BLE的硬件,并通过Android的前台服务与该硬件通信。
前台服务负责处理与BLE相关的事件,它按照要求可以很好地工作一段时间,但是如果前台服务由于任何原因被终止或BLE连接中断,则应用程序会尝试重新连接到BLE,此时BLE回调将开始从BluetoothGattCallback接收重复事件。这意味着尽管硬件只发送一个事件到蓝牙,但Android BluetoothGattCallback会为同一事件接收到多个回调,从而导致我们实现中出现大量错误。
请参考以下日志:
请参考以下日志:
Following are methods and callbacks from my foreground service,
BLEManagerService: *****onDescriptorWrite: 0*****<br>
BLEManagerService: *****onDescriptorWrite: 0*****<br>
BLEManagerService: *****onDescriptorWrite: 0*****<br>
BLEManagerService: Firmware: onCharacteristicRead true<br>
BLEManagerService: *****onDescriptorWrite: 0*****<br>
BLEManagerService: Firmware: onCharacteristicRead true<br>
BLEManagerService: *****onCharacteristicRead: 0*****<br>
BLEManagerService: *****onCharacteristicRead: 0*****<br>
override fun onCreate() {
super.onCreate()
mBluetoothGatt?.let { refreshDeviceCache(it) }
registerReceiver(btStateBroadcastReceiver, IntentFilter(BluetoothAdapter.ACTION_STATE_CHANGED))
}
/**
* Start BLE scan
*/
private fun scanLeDevice(enable: Boolean) {
if (enable && bleConnectionState == DISCONNECTED) {
//initialize scanning BLE
startScan()
scanTimer = scanTimer()
} else {
stopScan("scanLeDevice: (Enable: $enable)")
}
}
private fun scanTimer(): CountDownTimer {
return object : CountDownTimer(SCAN_PERIOD, 1000) {
override fun onTick(millisUntilFinished: Long) {
//Nothing to do
}
override fun onFinish() {
if (SCAN_PERIOD > 10000 && bleConnectionState == DISCONNECTED) {
stopScan("restart scanTimer")
Thread.sleep(200)
scanLeDevice(true)
SCAN_PERIOD -= 5000
if (null != scanTimer) {
scanTimer!!.cancel()
scanTimer = null
}
scanTimer = scanTimer()
} else {
stopScan("stop scanTimer")
SCAN_PERIOD = 60000
}
}
}
}
//Scan callbacks for more that LOLLIPOP versions
private val mScanCallback = object : ScanCallback() {
override fun onScanResult(callbackType: Int, result: ScanResult) {
val btDevice = result.device
if (null != btDevice) {
val scannedDeviceName: String? = btDevice.name
scannedDeviceName?.let {
if (it == mBluetoothFemurDeviceName) {
stopScan("ScanCallback: Found device")
//Disconnect from current connection if any
mBluetoothGatt?.let {it1 ->
it1.close()
mBluetoothGatt = null
}
connectToDevice(btDevice)
}
}
}
}
override fun onBatchScanResults(results: List<ScanResult>) {
//Not Required
}
override fun onScanFailed(errorCode: Int) {
Log.e(TAG, "*****onScanFailed->Error Code: $errorCode*****")
}
}
/**
* Connect to BLE device
* @param device
*/
fun connectToDevice(device: BluetoothDevice) {
scanLeDevice(false)// will stop after first device detection
//Stop Scanning before connect attempt
try {
if (null != scanTimer) {
scanTimer!!.cancel()
}
} catch (e: Exception) {
//Just handle exception if something
// goes wrong while canceling the scan timer
}
//Stop scan if still BLE scanner is running
stopScan("connectToDevice")
if (mBluetoothGatt == null) {
connectedDevice = device
if (Build.VERSION.SDK_INT >= 26)
connectedDevice?.connectGatt(this, false, mGattCallback)
}else{
disconnectDevice()
connectedDevice = device
connectedDevice?.connectGatt(this, false, mGattCallback)
}
}
/**
* Disconnect from BLE device
*/
private fun disconnectDevice() {
mBluetoothGatt?.close()
mBluetoothGatt = null
bleConnectionState = DISCONNECTED
mBluetoothManager = null
mBluetoothAdapter = null
mBluetoothFemurDeviceName = null
mBluetoothTibiaDeviceName = null
connectedDevice = null
}
/****************************************
* BLE Related Callbacks starts *
* Implements callback methods for GATT *
****************************************/
// Implements callback methods for GATT events that the app cares about. For example,
// connection change and services discovered.
private val mGattCallback = object : BluetoothGattCallback() {
/**
* Connection state changed callback
*/
override fun onConnectionStateChange(gatt: BluetoothGatt, status: Int, newState: Int) {
if (newState == BluetoothProfile.STATE_CONNECTED) {
mBluetoothGatt = gatt
//Stop Scanning before connect attempt
try {
if (null != scanTimer) {
scanTimer!!.cancel()
}
} catch (e: Exception) {
//Just handle exception if something
// goes wrong while canceling the scan timer
}
stopScan("onConnectionStateChange")// will stop after first device detection
} else if (newState == BluetoothProfile.STATE_DISCONNECTED || status == 8) {
disconnectDevice()
Handler(Looper.getMainLooper()).postDelayed({
initialize()
}, 500)
}
}
/**
* On services discovered
* @param gatt
* @param status
*/
override fun onServicesDiscovered(gatt: BluetoothGatt, status: Int) {
super.onServicesDiscovered(gatt, status)
}
override fun onDescriptorWrite(gatt: BluetoothGatt, descriptor: BluetoothGattDescriptor, status: Int) {
super.onDescriptorWrite(gatt, descriptor, status)
}
/**
* On characteristic read operation complete
* @param gatt
* @param characteristic
* @param status
*/
override fun onCharacteristicRead(gatt: BluetoothGatt, characteristic: BluetoothGattCharacteristic, status: Int) {
super.onCharacteristicRead(gatt, characteristic, status)
}
/**
* On characteristic write operation complete
* @param gatt
* @param characteristic
* @param status
*/
override fun onCharacteristicWrite(gatt: BluetoothGatt, characteristic: BluetoothGattCharacteristic, status: Int) {
super.onCharacteristicWrite(gatt, characteristic, status)
val data = characteristic.value
val dataHex = byteToHexStringJava(data)
}
/**
* On Notification/Data received from the characteristic
* @param gatt
* @param characteristic
*/
override fun onCharacteristicChanged(gatt: BluetoothGatt, characteristic: BluetoothGattCharacteristic) {
super.onCharacteristicChanged(gatt, characteristic)
val data = characteristic.value
val dataHex = byteToHexStringJava(data)
}
override fun onReadRemoteRssi(gatt: BluetoothGatt, rssi: Int, status: Int) {
super.onReadRemoteRssi(gatt, rssi, status)
val b = Bundle()
b.putInt(BT_RSSI_VALUE_READ, rssi)
receiver?.send(APP_RESULT_CODE_BT_RSSI, b)
}
}
/**
* Bluetooth state receiver to handle the ON/OFF states
*/
private val btStateBroadcastReceiver = object : BroadcastReceiver() {
override fun onReceive(context: Context, intent: Intent) {
val state = intent.getIntExtra(BluetoothAdapter.EXTRA_STATE, -1)
when (state) {
BluetoothAdapter.STATE_OFF -> {
//STATE OFF
}
BluetoothAdapter.STATE_ON -> {
//STATE ON
btState = BT_ON
val b = Bundle()
receiver?.send(APP_RESULT_CODE_BT_ON, b)
initialize()
}
BluetoothAdapter.STATE_TURNING_OFF -> {
//Not Required
}
BluetoothAdapter.STATE_TURNING_ON -> {
//Not Required
}
}
}
}
private fun handleBleDisconnectedState() {
mBluetoothGatt?.let {
it.close()
receiver?.send(DISCONNECTED, b)
Handler(Looper.getMainLooper()).postDelayed({
mBluetoothManager = null
mBluetoothAdapter = null
mBluetoothFemurDeviceName = null
mBluetoothTibiaDeviceName = null
mBluetoothGatt = null
}, 1000)
}
}
/****************************************
* BLE Related Callbacks End ***
****************************************/
/****************************************************
* Register Receivers to handle calbacks to UI ***
****************************************************/
override fun onDestroy() {
super.onDestroy()
try {
mBluetoothGatt?.let {
it.close()
mBluetoothGatt = null
}
unregisterReceivers()
scanTimer?.cancel()
} catch (e: Exception) {
e.printStackTrace()
}
}
override fun onTaskRemoved(rootIntent: Intent?) {
super.onTaskRemoved(rootIntent)
Log.e(TAG, "onTaskRemoved")
stopSelf()
}
/**
* Unregister the receivers before destroying the service
*/
private fun unregisterReceivers() {
unregisterReceiver(btStateBroadcastReceiver)
}
companion object {
private val TAG = BLEManagerService::class.java.simpleName
private var mBluetoothGatt: BluetoothGatt? = null
var bleConnectionState: Int = DISCONNECTED
}
}