BLE Android - onConnectionStateChange 未被调用
BLE Android - onConnectionStateChange not being called
我在尝试连接外围设备时遇到问题。有时 BluetoothDevice#connectGatt(...)
之后不会调用回调 onConnectionStateChange(...)
。我想要实现的是由用户操作触发的快速和短连接。
如果事先没有采取特定措施,这种情况大约每 10 次就会发生 1 次。它持续大约 20 到 30 秒,或者直到应用程序被终止并重新打开。我遵循的正常步骤顺序是:
- 扫描设备以查找外围设备。
- 呼叫
BluetoothDevice#connectGatt(...)
。如果连接时间超过1秒,则表示连接为"stuck",因此不会连接,因此再次调用BluetoothDevice#connectGatt(...)
。此操作限制为 5 次尝试。
onConnectionStateChange(...)
使用 newState
CONNECTED 调用并开始服务发现。
- 其余的操作没有问题。
- 断线后调用
BluetoothGatt#close()
。
问题出现在第3点,有时onConnectionStateChange(...)
没有被调用。我注意到大多数时候问题都是从特定行为开始的。在调用 BluetoothDevice#connectGatt(...)
之后,onConnectionStateChange(...)
被调用 newState
CONNECTED,但几乎紧接着(~40 毫秒)再次调用 newStatus
DISCONNECTED。由于状态变化的时间很短,我可以推断出设备甚至没有尝试建立连接并将状态更改为 DISCONNECTED。
问题在以下时间结束:
- 20-30 秒过去了。在此期间
onConnectionStateChange(...)
永远不会被调用。当问题结束时,onConnectionStateChange(...)
被称为应用程序尝试连接的次数。例如,如果 BluetoothDevice#connectGatt(...)
被调用 15 次,则 onConnectionStateChange(...)
被调用 15 次且 newState
等于 DISCONNECTED。这很奇怪,因为在任何这些连接尝试中,状态都不会更改为 CONNECTED。
- 应用被终止并重新启动。
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);
}
}
}
我认为外设无关,因为iOSapp一直可以连接,没有这个问题
有什么想法吗?提前致谢。
编辑!
This回答说:
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.
所以在这 30 秒的时间间隔内有一个挂起的连接请求,并在第二个 30 秒时超时。这不太可能,但是,我可以做些什么来缩短这个时间吗?或者也许有一个我没有看到的连接失败的解释。谢谢。
编辑 2016 年 2 月 3 日
可能有帮助的新信息。当问题开始时(在使用 newState=CONNECTED
调用约 40 毫秒后使用 newState=DISCONNECTED
调用 onConnectionStateChange(...)
时),状态为 62 = 0x03E。查看 here 状态代码表示 GATT_CONN_FAIL_ESTABLISH。当我检测到此状态时,我正在关闭 gatt 连接,但问题仍然存在。我也试过断开和关闭。想法?谢谢。
Android蓝牙偶尔需要回收,遇到这个timeount有没有试过在设备上重启BLE?
这是我用来在奇怪的事情开始发生时重新启动 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);
}
}
}
我不确定您是否仍在寻找这个问题的答案。就个人而言,我不建议为低能耗设备制作 "fast and short connections triggered by user action"。相反,您可以在 connectGatt 方法中将 autoConnect 选项设置为 "true"。
device.connectGatt(mContext, true, mGattCallback); [而不是假]
希望对您有所帮助!
如果有人遇到类似问题,最后通过更换外设(arduino)使用的BLE芯片解决了问题。在此更改之前,我发现的一个解决方法是在每次连接后关闭和打开 BLE。解决方案并不完美,但是连接率提高了很多。
我在尝试连接外围设备时遇到问题。有时 BluetoothDevice#connectGatt(...)
之后不会调用回调 onConnectionStateChange(...)
。我想要实现的是由用户操作触发的快速和短连接。
如果事先没有采取特定措施,这种情况大约每 10 次就会发生 1 次。它持续大约 20 到 30 秒,或者直到应用程序被终止并重新打开。我遵循的正常步骤顺序是:
- 扫描设备以查找外围设备。
- 呼叫
BluetoothDevice#connectGatt(...)
。如果连接时间超过1秒,则表示连接为"stuck",因此不会连接,因此再次调用BluetoothDevice#connectGatt(...)
。此操作限制为 5 次尝试。 onConnectionStateChange(...)
使用newState
CONNECTED 调用并开始服务发现。- 其余的操作没有问题。
- 断线后调用
BluetoothGatt#close()
。
问题出现在第3点,有时onConnectionStateChange(...)
没有被调用。我注意到大多数时候问题都是从特定行为开始的。在调用 BluetoothDevice#connectGatt(...)
之后,onConnectionStateChange(...)
被调用 newState
CONNECTED,但几乎紧接着(~40 毫秒)再次调用 newStatus
DISCONNECTED。由于状态变化的时间很短,我可以推断出设备甚至没有尝试建立连接并将状态更改为 DISCONNECTED。
问题在以下时间结束:
- 20-30 秒过去了。在此期间
onConnectionStateChange(...)
永远不会被调用。当问题结束时,onConnectionStateChange(...)
被称为应用程序尝试连接的次数。例如,如果BluetoothDevice#connectGatt(...)
被调用 15 次,则onConnectionStateChange(...)
被调用 15 次且newState
等于 DISCONNECTED。这很奇怪,因为在任何这些连接尝试中,状态都不会更改为 CONNECTED。 - 应用被终止并重新启动。
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);
}
}
}
我认为外设无关,因为iOSapp一直可以连接,没有这个问题
有什么想法吗?提前致谢。
编辑!
This回答说:
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.
所以在这 30 秒的时间间隔内有一个挂起的连接请求,并在第二个 30 秒时超时。这不太可能,但是,我可以做些什么来缩短这个时间吗?或者也许有一个我没有看到的连接失败的解释。谢谢。
编辑 2016 年 2 月 3 日
可能有帮助的新信息。当问题开始时(在使用 newState=CONNECTED
调用约 40 毫秒后使用 newState=DISCONNECTED
调用 onConnectionStateChange(...)
时),状态为 62 = 0x03E。查看 here 状态代码表示 GATT_CONN_FAIL_ESTABLISH。当我检测到此状态时,我正在关闭 gatt 连接,但问题仍然存在。我也试过断开和关闭。想法?谢谢。
Android蓝牙偶尔需要回收,遇到这个timeount有没有试过在设备上重启BLE?
这是我用来在奇怪的事情开始发生时重新启动 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);
}
}
}
我不确定您是否仍在寻找这个问题的答案。就个人而言,我不建议为低能耗设备制作 "fast and short connections triggered by user action"。相反,您可以在 connectGatt 方法中将 autoConnect 选项设置为 "true"。
device.connectGatt(mContext, true, mGattCallback); [而不是假]
希望对您有所帮助!
如果有人遇到类似问题,最后通过更换外设(arduino)使用的BLE芯片解决了问题。在此更改之前,我发现的一个解决方法是在每次连接后关闭和打开 BLE。解决方案并不完美,但是连接率提高了很多。