Android: 如何捕获BLE连接失败/断开连接?

7
所以在正常情况下我能够成功连接到BLE设备。我想要处理异常情况,例如当与设备的连接失败或已建立的连接丢失(可能是因为被扔下悬崖或被公交车撞击)时。
我正在使用Cypress BLE模块进行测试,并且我正在进行其中一个测试,即从模块中断电。但是,onConnectionStateChange 永远不会被调用!它只会响应成功的连接。显然,尝试连接数小时而没有放弃。我想延迟取消连接尝试,但没有办法取消蓝牙设备上的连接尝试(据我所知)!我认为它将一直尝试,直到电池耗尽。
这是我的onConnectionStateChange目前看起来像什么,在Gatt回调内部。请注意,我正在尝试捕获和记录任何涉及任何连接状态更改的任何类型的回调...除非连接成功,否则永远不会被调用。请注意,此代码不在活动本身中。它在由单例持有的对象中。(我希望在多个活动之间保持连接)
        @Override
        public void onConnectionStateChange(BluetoothGatt gatt, int status, int newState) {
            mGatt = gatt;
            Logy.CallPrint(LOGY_ENABLE, CLASSNAME, "Status: "+status+ " Newstate: "+newState);
            switch(status)
            {
                case BluetoothGatt.GATT_SUCCESS:
                    mReconnectAttempts = MAX_ATTEMPTS;

                    if(newState == BluetoothGatt.STATE_CONNECTED)
                    {
                        DispatchEvent(Event.Type.BT_ON_CONNECT);
                        bIsConnected = true;
                        gatt.discoverServices();
                    } else if (newState == BluetoothGatt.STATE_DISCONNECTED)
                    {
                        DispatchEvent(Event.Type.BT_ON_DISCONNECT);
                        bIsConnected = false;
                    }
                    break;
                default:
                    if(newState == BluetoothGatt.STATE_DISCONNECTED)
                    {
                        bIsConnected = false;
                        if(mReconnectAttempts > 0)
                        { // if we have attempts left, auto attempt to reconnect
                            DispatchEvent(Event.Type.BT_RECONNECTING);
                            mReconnectAttempts--;
                            gatt.connect();
                            bIsConnected = false;
                        }
                        else
                        {
                            mReconnectAttempts = MAX_ATTEMPTS;
                            DispatchEvent(Event.Type.BT_ON_CONNECT_FAIL);
                            bIsConnected = false;
                        }
                    } else {
                        Logy.CallPrint(LOGY_ENABLE, CLASSNAME, "onConnectionStateChange: Failed?");
                    }
            }

            super.onConnectionStateChange(gatt, status, newState);
        }

无法检测到断开连接是我代码中的一个问题,例如在显示进度对话框以指示应用程序正在连接到BLE设备时。然而,该对话框永远不会消失,因为从未触发“连接失败”事件。

3个回答

13
我认为你正在寻找的是“蓝牙监督超时”,该参数根据蓝牙LE规范定义为:“两个接收到的数据包PDU之间的最大时间,超过该时间则被视为连接丢失”。在Android上,默认的“监督超时”设置为20秒(取决于Android版本和设备)。例如,此处是Android 5.1上“监督超时”的值。没有API可以设置此参数,因此您需要等待20秒(取决于Android版本和设备),以便在关闭BLE模块后获得带有状态BluetoothGatt.STATE_DISCONNECTEDonConnectionStateChange回调。

8
这个答案与Emil的答案一致。
我编写了一个测试应用程序,在Moto G4 Play Android 6.0.1(Marshmellow:API23)上运行一个单一的活动,并使用基于Laird BL600的外围设备。
我本来要发布一些日志,但它们的格式不太好,所以我只描述结果。
像往常一样,外围设备进行了广告,中央进行了扫描,并获得了一个设备实例。
问题没有明确说明第一个连接是如何建立的,但让我们假设它是以“自动连接”为假设的形式。
mBluetoothGatt = device.connectGatt(mContext, false, mGattCallback);

(这是测试应用程序中唯一使用的连接方式)

这提供了一个BluetoothGatt实例,并且正如问题中所述,事件通过回调异步报告,但是在发生任何连接事件之前,还有一个BluetoothGatt实例可以调用disconnect()

API文档disconnect()进行了说明 - 特别是在“,”之后的部分:

BluetoothGatt.disconnect() 断开已建立的连接或取消当前正在进行的连接尝试。

为了释放GATT资源,在断开连接后,测试应用程序始终调用

mBluetoothGatt.close();

以下是一些不同的场景:

  1. device.connectGatt() -- 外围设备广告 - 连接成功 -- 然后执行 mBluetoothGatt.disconnect() 和 close()。

    结果:这是一个正常的连接成功,之后中央设备在代码中关闭了连接。回调事件按预期收到。当中央在代码中断开连接时,底层蓝牙服务断开连接并且外围设备会收到一个断开连接的事件。
    .

  2. device.connectGatt() -- 外围设备广告 - 连接成功 -- 然后外围设备关闭。

    结果:这是一个正常的连接成功,回调事件按预期收到,然后由外围设备断开连接。外围设备在 BleGapSvcInit 中指示了首选连接监督超时时间,并且中央在连接参数更新中进行了确认。在外围设备断开连接之后,中央在连接监督超时时间结束后立即收到一个断开连接的事件。(在8秒和4秒监督超时时间下重复测试)。
    .

  3. device.connectGatt() -- 但外围设备已停止广告,连接尝试被允许超时。

    结果:(这是一个特定的问题场景) 在30秒后,可能是手机上蓝牙服务的连接超时,会触发一个 onConnectionStateChange 事件,指示新的连接状态为已断开 - 带有(错误)状态133。仍然必须记住调用 mBluetoothGatt.close(),否则会发生接口泄漏,并且下一个客户端接口上进行的后续连接将会失败。
    .

  4. device.connectGatt() -- 外围设备仍在广告,但在200ms后使用 mBluetoothGatt.disconnect() 和 close() 取消连接。

    结果:(我认为这是最有趣的情况,但在实际应用中也最不可能发生) 有时候(大约1/10),底层蓝牙服务实际上确实连接到了外围设备;尽管应用程序没有在回调中看到这些事件。有时候,即使从应用程序的角度来看,应用程序已经断开连接,底层的蓝牙手机服务也连接到了外围设备,并且在我的测试中保持连接了几分钟,直到超时!- 并关闭了 BT 服务或外围设备。


2
#4 让我的生活非常困难。我一次连接最多7个设备,当你使用这么多设备时,1/10的问题总是会发生。我很高兴在这里看到所有这些类型都有文档记录。 - Bryan Bryce

3

首先,如果已建立的连接断开,当监督超时时间到达时,您应该会收到一个断开连接状态改变事件。否则,Android 中存在某些错误。

现在来谈谈连接尝试。

一旦您使用 connectGatt 创建了 BluetoothGatt 对象并将自动连接参数设置为 true,或者在现有的 BluetoothGatt 对象上执行 connect 方法,手机就会处于一种状态,即始终且无限期地尝试连接到设备,并重新连接到设备,直到您调用 gatt 对象上的 disconnect 或 close 为止。

因此,如果您想在一段时间后中止连接,只需设置任何类型的计时器,在触发时调用 gatt 对象上的 disconnect(如果您不再需要它,则调用 close)。

还要注意,当 newState 为 disconnected 时,onConnectionStateChange 的 status 参数未定义。在较旧的 Android 版本中,它通常包含 0 或 133,在较新的版本中,通常是蓝牙标准的断开原因错误代码。

此外,如果您在先前没有收到连接状态改变事件而收到断开连接状态改变事件,这通常表示在内部蓝牙堆栈中出现了问题(除非您使用非自动连接,在这种情况下,您始终会在一定时间后收到断开连接状态改变事件)。然后,我建议您关闭 gatt 对象并稍后重试。


1
我的担忧是在你只有BluetoothDevice并调用connect的时间段内。没有BluetoothGatt可以调用disconnect。在长时间“尝试连接设备”之后,没有onConnectionStateChange被调用,我也没有找到明显的方法来取消连接尝试,这让我担心如果我尝试通过启动与另一个设备的连接来连接到不同的设备,那么先前与其他设备的连接尝试最终也可能成功! - Magic Marbles

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接