java BLE Android - onConnectionStateChange 未被调用

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/35103701/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-11-02 23:44:14  来源:igfitidea点击:

BLE Android - onConnectionStateChange not being called

javaandroidandroid-bluetoothbluetooth-lowenergy

提问by avmatte

I have a problem trying to connect to a peripheral. Sometimes the callback onConnectionStateChange(...)is not called after BluetoothDevice#connectGatt(...). What I'm trying to achieve is fast and short connections triggered by user action.

我在尝试连接到外围设备时遇到问题。有时在onConnectionStateChange(...)之后不调用回调BluetoothDevice#connectGatt(...)。我想要实现的是由用户操作触发的快速和短连接。

This situation occurs about 1 every 10 times without specific prior action. It lasts about 20 to 30 seconds or until the application is killed and reopened. The normal sequence of steps I follow is:

这种情况大约每 10 次发生 1 次,无需事先采取具体行动。它持续大约 20 到 30 秒或直到应用程序被终止并重新打开。我遵循的正常步骤顺序是:

  1. Scan devices to find the peripheral.
  2. Call BluetoothDevice#connectGatt(...). If it takes longer than 1 second to connect, it means that the connection is "stuck" and therefore it won't connect, so BluetoothDevice#connectGatt(...)is called again. This is done with a limit of 5 attempts.
  3. onConnectionStateChange(...)is called with newStateCONNECTED and begins the services discovery.
  4. The rest of the operations are performed without problems.
  5. After disconnection BluetoothGatt#close()is called.
  1. 扫描设备以查找外围设备。
  2. 打电话BluetoothDevice#connectGatt(...)。如果连接时间超过 1 秒,则表示连接“卡住”,因此无法连接,因此BluetoothDevice#connectGatt(...)再次调用。这是通过 5 次尝试的限制来完成的。
  3. onConnectionStateChange(...)使用newStateCONNECTED调用并开始服务发现。
  4. 其余的操作都没有问题。
  5. 断线后BluetoothGatt#close()调用。

The problem occurs at point 3. Sometimes onConnectionStateChange(...)is not called. I have noticed that most of the times the problem starts with a specific behavior. After calling BluetoothDevice#connectGatt(...), onConnectionStateChange(...)is called with newStateCONNECTED, but almost immediately afterwards (~40 milliseconds) is called again with newStatusDISCONNECTED. Due to the short time of the status change, I can deduce that the device does not even tried to make the connection and changed the state to DISCONNECTED. The problem ends when:

问题出现在第 3 点。有时onConnectionStateChange(...)不调用。我注意到大多数时候问题始于特定行为。调用后BluetoothDevice#connectGatt(...)onConnectionStateChange(...)使用newStateCONNECTED调用,但几乎立即(~40 毫秒)再次使用newStatusDISCONNECTED调用。由于状态变化的时间很短,我可以推断该设备甚至没有尝试建立连接并将状态更改为 DISCONNECTED。问题在以下情况下结束:

  1. 20-30 seconds have passed. During this time onConnectionStateChange(...)is never called. When the problem ends, onConnectionStateChange(...)is called the number of times that the app tried to connect. For example, if BluetoothDevice#connectGatt(...)is called 15 times, onConnectionStateChange(...)is called 15 times with newStateequal to DISCONNECTED. This is curious because never in any of those connection attempts the status changed to CONNECTED.
  2. The app is killed and started again.
  1. 20-30 秒过去了。在此期间onConnectionStateChange(...)从不调用。当问题结束时,onConnectionStateChange(...)称为应用程序尝试连接的次数。例如,如果BluetoothDevice#connectGatt(...)调用 15 次,onConnectionStateChange(...)则调用 15 次newState等于 DISCONNECTED。这很奇怪,因为在任何这些连接尝试中,状态都不会更改为 CONNECTED。
  2. 该应用程序被杀死并重新启动。

This error occurs in SDK18 and SDK 21.

此错误发生在 SDK18 和 SDK 21 中。

@Override
public void onLeScan(final BluetoothDevice device, int rssi, byte[] scanRecord) {
    String deviceName = device.getName();
    if (deviceName == null) return;
    Log.d("BLUETOOTH CONNECTION", "Device found: " + device.getName());
    if (mMode == SCAN_MODE) {
        mListener.deviceFound(device, rssi, scanRecord);
    }
    else {
        mDevices.put(device.hashCode(), device);
        stopScan();
        // Samsung devices with SDK 18 or 19 requires that connectGatt is called in main thread.
        mHandler.post(new Runnable() {
            @Override
            public void run() {
                Log.d("BLUETOOTH CONNECTION", "Executing first device.connectGatt()");
                BluetoothGatt gatt = device.connectGatt(mContext, false, mGattCallback);
                retryIfNecessary(device, gatt);
                mTryingToConnect = true;
            }
        });
    }
}
private void retryIfNecessary(final BluetoothDevice device, final BluetoothGatt gatt) {
    if (isRetryLimitReached()) {
        Log.d("BLUETOOTH CONNECTION", "Try count limit reached");
        finishConnection(gatt);
        mRetryCount = 0;
        mListener.error(TIMEOUT);
        return;
    }
    mRetryCount++;
    mHandler.postDelayed(new Runnable() {
        @Override
        public void run() {
            Log.d("BLUETOOTH CONNECTION", "Check if it is frozen.");
            if (isWorking()) {
                Log.d("BLUETOOTH CONNECTION", "Frozen, create new connection.");
                BluetoothGatt gatt = device.connectGatt(mContext, false, mGattCallback);
                retryIfNecessary(device, gatt);
            }
        }
    }, RETRY_INTERVAL_MS);
}
    @Override
    public void onConnectionStateChange(final BluetoothGatt gatt, int status, int newState) {
        Log.d("BLUETOOTH CONNECTION", "On connection state changed. Device: "+ gatt.getDevice().getAddress());
        if (!mConnected && BluetoothGatt.STATE_CONNECTED == newState) {
            Log.d("BLUETOOTH CONNECTION", "Connected");
            mTryingToConnect = false;
            mTryingToDiscoverServices = true;
            mConnected = true;
            gatt.discoverServices();
        }
        else if(BluetoothGatt.STATE_DISCONNECTED == newState) {
            Log.d("BLUETOOTH CONNECTION", "Disconnected and closing gatt.");
            mConnected = false;
            gatt.close();
            if (!mConnectionFinished && mRetryCount == 0) {
                finishConnection(gatt);
            }
        }
    }

I think that the peripheral is not relevant, because the iOS app can always connect without this problem.

我认为外围设备无关,因为iOS应用程序始终可以连接而不会出现此问题。

Any ideas? Thanks in advance.

有任何想法吗?提前致谢。

Edit!

编辑!

Thisanswer say that:

这个回答说:

Direct connection has interval of 60ms and window of 30ms so connections complete much faster. Additionally there can only be one direct connection request pending at a time and it times out after 30 seconds. onConnectionStateChange() gets called with state=2, status=133 to indicate this timeout.

直接连接的间隔为 60 毫秒,窗口为 30 毫秒,因此连接完成得更快。此外,一次只能有一个直接连接请求未决,并且在 30 秒后超时。onConnectionStateChange() 以 state=2, status=133 调用以指示此超时。

So in this 30 seconds interval there is a pending connection request and times out at the second 30. It's unlikely but, is there anything I can do to make this time shorter? Or maybe there is an explanation for the connection failure that I am not seeing. Thanks.

所以在这 30 秒的间隔中有一个挂起的连接请求并在第二个 30 处超时。这不太可能,但是,我可以做些什么来缩短这个时间?或者也许有我没有看到的连接失败的解释。谢谢。

EDIT 02/03/2016

编辑 02/03/2016

A new information that may help. When the problem starts (when onConnectionStateChange(...)is called with newState=DISCONNECTEDafter ~40ms of being called with newState=CONNECTED), the status is 62 = 0x03E. Looking herethat status code means GATT_CONN_FAIL_ESTABLISH. When I detect this status I'm closing the gatt connection, but the problem persists. I also tried disconnecting and closing. Ideas? Thanks.

可能有帮助的新信息。当问题开始时(在onConnectionStateChange(...)被调用newState=DISCONNECTED后约 40 毫秒被调用时newState=CONNECTED),状态为 62 = 0x03E。看这里是状态代码表示GATT_CONN_FAIL_ESTABLISH。当我检测到此状态时,我正在关闭 gatt 连接,但问题仍然存在。我也尝试断开连接和关闭。想法?谢谢。

采纳答案by avmatte

If someone is having a similar issue, the problem was finally solved by changing the BLE chip used by the peripheral (arduino). Before that change, a workaround I found was turning off and on the BLE after each connection. The solution was not perfect, but improved the connection rate a lot.

如果有人遇到类似问题,最终通过更换外设(arduino)使用的BLE芯片解决了问题。在此更改之前,我发现的解决方法是在每次连接后关闭和打开 BLE。解决方案并不完美,但提高了很多连接率。

回答by Michael

Android Bluetooth needs to be recycled occasionally, have you tried restarting the BLE on the device when you encounter this timeount?

安卓蓝牙偶尔需要回收,遇到这个timeout时有没有试过重启设备上的BLE?

Here's a snippet I've used to restart the BLE when strange things start happening.

这是我用来在奇怪的事情开始发生时重新启动 BLE 的片段。

static Handler mHandler = new Handler();
public static void restartBle() {
    final BluetoothManager mgr = (BluetoothManager) ApplicationBase.getAppContext().getSystemService(Context.BLUETOOTH_SERVICE);
    final BluetoothAdapter adp = mgr.getAdapter();
    if (null != adp) {
        if (adp.isEnabled()) {
            adp.disable();

            // TODO: display some kind of UI about restarting BLE
            mHandler.postDelayed(new Runnable() {
                @Override
                public void run() {
                    if (!adp.isEnabled()) {
                        adp.enable();
                    } else {
                        mHandler.postDelayed(this, 2500);
                    }
                }
            }, 2500);
        }
    }
}

回答by Subramanya Sheshadri

I am not sure if you're still looking for an answer for this question. Personally, I would not advise making "fast and short connections triggered by user action" for low energy devices. Instead you could set the autoConnect option to "true" in your connectGatt method.

我不确定你是否还在寻找这个问题的答案。就个人而言,我不建议为低能耗设备进行“由用户操作触发的快速和短连接”。相反,您可以在 connectGatt 方法中将 autoConnect 选项设置为“true”。

device.connectGatt(mContext, true, mGattCallback); [instead of false]

device.connectGatt(mContext, true, mGattCallback); [而不是假的]

Hope it helps!

希望能帮助到你!