- iOS/Objective-C 元类和类别
- objective-c - -1001 错误,当 NSURLSession 通过 httpproxy 和/etc/hosts
- java - 使用网络类获取 url 地址
- ios - 推送通知中不播放声音
我正在使用支持 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
}
最佳答案
不要在 onConnectionStateChange 中设置 mBluetoothGatt = gatt。而是从 connectGatt 的返回值中设置它。否则,您可能会创建多个 BluetoothGatt 对象而不关闭之前的对象,从而获得多个回调。
关于android - 前台服务重启后多次接收BluetoothGattCallback,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/56642287/
我正在关注 Android guide在媒体播放器应用程序中使用 MediaBrowserServiceCompat,但该服务在应用程序退出时被销毁。
我使用System.Windows.Window.IsActive来检测窗口是否位于前台,并且在某些情况下有效。但我发现了一些情况,它没有,我想知道是否有任何方法可以检测到它。 最佳答案 除非仅后台进
我需要为 EditText 的一部分设置样式。我希望文本为白色,背景为灰色。看起来很简单,但事实并非如此。 spanRange.setSpan(new BackgroundColorSpan(Colo
我决定在一个帖子中发布两个问题,因为这是完全相同的问题。 我需要知道屏幕何时打开或关闭,以便我可以打开 LED。第二个我需要知道我的应用程序是在后台还是在前台,以便在应用程序处于后台时管理在某些操作上
关于双高红色录音状态栏有很多问题( here , here ),但是当应用程序退出到后台时,所有这些问题都引用闪烁。我得到了一个闪光,我假设来自 AVCaptureSession设置,而应用程序在前台
我有一个奇怪的案例。我的 swift ios 应用程序已连接到 Cloudkit。如果应用程序未运行(后台状态),我每次都会收到通知徽章和警报!如果应用程序正在运行,则不会收到任何通知!我知道它没有击
我是 firebase 和 android 的新手,我想在我的应用程序中包含实时聊天。但我对 firebase 有以下疑问。请帮忙。 1) 如果应用程序在前台,系统托盘中是否会有默认通知,还是我必须在
我正在为 Excel 编写 VSTO 加载项,我注意到,如果我锁定工作表并使用密码保护它(因此只有我的加载项可以写入它,但用户可以查看它),如果用户尝试编辑工作表时,他们收到“此工作表已锁定”弹出窗口
我正在开发一个 iOS 应用程序,此应用程序允许其用户添加他稍后必须执行的任务。完成添加此任务后,它将发送到服务器以保存在服务器端。现在我对某些情况感到困惑:我的用户在输入任务详细信息时有什么电话..
我正在阅读内存不足 (OOM) killer ,以及 Android 如何确定进程的优先级 (https://developer.android.com/guide/components/proces
我已经在我的新应用程序中启动了一项服务。该服务是前台的,带有通知。当它在 AVD 2.1 API Level 7 中运行时,一切正常。但是当它在运行 Gingerbread 的 Samsung Gal
我的 Laravel 应用程序的结构需要帮助。 我想要的基本上是这个结构: 应用程序接口(interface) 管理面板 公共(public)网站 我开始构建我认为非常正确的文件夹结构: app/
我正在尝试用 CardView 填充我的 RecyclerView,CardView 使用 Android 数据绑定(bind)来设置属性,例如 TextView 中的文本。在未完成喷射的项目上,我想
我想使用 Window Script Host(WSH) 查找当前事件(具有焦点)的窗口的标题,因为我希望我的 WSH 脚本仅在所需窗口处于事件状态时才发送键。 注意*我无法使用替代方案,即在调用 s
如何调试 react-scripts 启动? 这工作正常,我不知道发生了什么变化(我没有改变任何东西) 看来 react-scripts start 无法作为前台进程继续运行。 我的 Dockerfi
我想制作像endonmondo那样的秒表。当我们启动应用程序时,它应该计算时间并更新主 Activity 中的textView(实际上它是一个 fragment )。 我做了后台服务(如 here )
我遇到了一个需求,但我无法获得正确的实现方式,因此需要您的帮助。 我想做什么? - 我想根据收到的通知执行操作,如下所示: 当应用程序打开并位于前台时,即对用户可见并且我收到通知时,我只是显示一个弹出
在 Android 10 中,我注意到我从操作系统收到一条 Toast 消息,说明“此 NFC 标签不支持应用程序”或“此 NFC 标签不支持应用程序”(取决于设备): 奇怪的是,我在 enableR
我是一名优秀的程序员,十分优秀!