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
BLE Android - onConnectionStateChange not being called
提问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 秒或直到应用程序被终止并重新打开。我遵循的正常步骤顺序是:
- Scan devices to find the peripheral.
- 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, soBluetoothDevice#connectGatt(...)
is called again. This is done with a limit of 5 attempts. onConnectionStateChange(...)
is called withnewState
CONNECTED and begins the services discovery.- The rest of the operations are performed without problems.
- After disconnection
BluetoothGatt#close()
is called.
- 扫描设备以查找外围设备。
- 打电话
BluetoothDevice#connectGatt(...)
。如果连接时间超过 1 秒,则表示连接“卡住”,因此无法连接,因此BluetoothDevice#connectGatt(...)
再次调用。这是通过 5 次尝试的限制来完成的。 onConnectionStateChange(...)
使用newState
CONNECTED调用并开始服务发现。- 其余的操作都没有问题。
- 断线后
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 newState
CONNECTED, but almost immediately afterwards (~40 milliseconds) is called again with newStatus
DISCONNECTED. 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(...)
使用newState
CONNECTED调用,但几乎立即(~40 毫秒)再次使用newStatus
DISCONNECTED调用。由于状态变化的时间很短,我可以推断该设备甚至没有尝试建立连接并将状态更改为 DISCONNECTED。问题在以下情况下结束:
- 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, ifBluetoothDevice#connectGatt(...)
is called 15 times,onConnectionStateChange(...)
is called 15 times withnewState
equal to DISCONNECTED. This is curious because never in any of those connection attempts the status changed to CONNECTED. - The app is killed and started again.
- 20-30 秒过去了。在此期间
onConnectionStateChange(...)
从不调用。当问题结束时,onConnectionStateChange(...)
称为应用程序尝试连接的次数。例如,如果BluetoothDevice#connectGatt(...)
调用 15 次,onConnectionStateChange(...)
则调用 15 次newState
等于 DISCONNECTED。这很奇怪,因为在任何这些连接尝试中,状态都不会更改为 CONNECTED。 - 该应用程序被杀死并重新启动。
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=DISCONNECTED
after ~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!
希望能帮助到你!