前台服务重启后多次接收BluetoothGattCallback [英] Receiving BluetoothGattCallback multiple times after foreground Service restart

查看:78
本文介绍了前台服务重启后多次接收BluetoothGattCallback的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在使用支持BLE的硬件,并使用Android的Foreground Service与该硬件进行通信. 前台服务负责处理BLE相关事件,并且在一段时间内按照要求运行得很好,但是由于某种原因,如果前台服务被杀死或BLE连接断开,则应用会尝试再次重新连接到BLE,然后BLE回调开始从BluetoothGattCallback获取重复事件,即使硬件将单个事件发送到Bluetooth,但Android BluetoothGattCallback接收到相同的多个回调,这在我们的实现中导致很多错误.

I am working with BLE enabled hardware and communicating with the hardware using Foreground Service of the Android. Foreground service is responsible for handling the BLE related events and it works quite good as per requirements for a while but somehow if the Foreground service is got killed or BLE connection is broken due to any reason then app tries to reconnect to the BLE again and then BLE callbacks start getting duplicate events from the BluetoothGattCallback, that is even though hardware sends a single event to Bluetooth but Android BluetoothGattCallback receives multiple callbacks for the same which leads to a lot of errors in our implementations.

作为参考,请按以下方式浏览日志

For reference please go through Logs as follows,

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
}

}

推荐答案

不要在onConnectionStateChange中设置mBluetoothGatt = gatt.而是根据connectGatt的返回值进行设置.否则,您可能会创建多个BluetoothGatt对象而不关闭先前的对象,因此会得到多个回调.

Don't set mBluetoothGatt = gatt in onConnectionStateChange. Instead set it from the return value of connectGatt. Otherwise you might create multiple BluetoothGatt objects without closing previous ones and therefore get multiple callbacks.

这篇关于前台服务重启后多次接收BluetoothGattCallback的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆