周辺機器に接続しようとして問題が発生しました。コールバックonConnectionStateChange(...)
がBluetoothDevice#connectGatt(...)
の後に呼び出されない場合があります。私が達成しようとしているのは、ユーザーアクションによってトリガーされる高速で短い接続です。
この状況は、事前の特定のアクションなしで約10回に1回発生します。約20〜30秒、またはアプリケーションが強制終了されて再度開かれるまで続きます。私が従う通常の手順は次のとおりです。
BluetoothDevice#connectGatt(...)
を呼び出します。接続に1秒以上かかる場合、接続が「スタック」しているため接続できないため、BluetoothDevice#connectGatt(...)
が再度呼び出されます。これは、5回の試行に制限されています。onConnectionStateChange(...)
はnewState
CONNECTEDを指定して呼び出され、サービスの検出を開始します。BluetoothGatt#close()
が呼び出されます。問題はポイント3で発生します。ときどきonConnectionStateChange(...)
が呼び出されません。ほとんどの場合、問題は特定の動作から始まります。 BluetoothDevice#connectGatt(...)
を呼び出した後、onConnectionStateChange(...)
がnewState
CONNECTEDで呼び出されますが、その直後(〜40ミリ秒)がnewStatus
DISCONNECTEDで再び呼び出されます。ステータスの変更が短時間であるため、デバイスが接続を試みず、状態をDISCONNECTEDに変更していないと推測できます。問題は次の場合に終了します。
onConnectionStateChange(...)
は呼び出されません。問題が終了すると、onConnectionStateChange(...)
が、アプリが接続を試行した回数だけ呼び出されます。たとえば、BluetoothDevice#connectGatt(...)
が15回呼び出された場合、newState
がDISCONNECTEDに等しい状態でonConnectionStateChange(...)
が15回呼び出されます。これらの接続のいずれにおいても、ステータスが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);
}
}
}
_
IOSアプリは常にこの問題なしに接続できるので、周辺機器は関係ないと思います。
何か案は?前もって感謝します。
編集!
これ 答えは次のように言います:
直接接続の間隔は60ミリ秒、ウィンドウの間隔は30ミリ秒なので、接続ははるかに速く完了します。さらに、一度に保留できる直接接続要求は1つだけで、30秒後にタイムアウトになります。 onConnectionStateChange()は、このタイムアウトを示すために、state = 2、status = 133で呼び出されます。
したがって、この30秒の間隔で保留中の接続要求があり、2番目の30でタイムアウトします。それはありそうにありませんが、この時間を短くするために私ができることはありますか?または、接続障害の説明が表示されていない可能性があります。ありがとう。
EDIT 02/03/2016
役立つかもしれない新しい情報。問題が発生したとき(_newState=DISCONNECTED
_で呼び出されてから約40ms後、onConnectionStateChange(...)
が_newState=CONNECTED
_で呼び出されたとき)、ステータスは62 = 0x03Eです。見る ここ そのステータスコードはGATT_CONN_FAIL_ESTABLISHを意味します。このステータスを検出すると、gatt接続を閉じていますが、問題は解決しません。また、切断して閉じてみました。アイデア?ありがとう。
誰かが同様の問題を抱えている場合、問題は周辺機器(arduino)で使用されるBLEチップを変更することで最終的に解決されました。その変更の前に、私が見つけた回避策は、各接続後にBLEをオフにしてからオンにすることでした。ソリューションは完璧ではありませんでしたが、接続速度を大幅に改善しました。
この質問に対する回答をまだ探しているかどうかはわかりません。個人的には、低エネルギーデバイスに対して「ユーザーアクションによってトリガーされる高速で短い接続」を作成することはお勧めしません。代わりに、connectGattメソッドでautoConnectオプションを「true」に設定できます。
device.connectGatt(mContext、true、mGattCallback); [偽の代わりに]
それが役に立てば幸い!
Android Bluetoothはたまにリサイクルする必要があります。この時間に遭遇したときにデバイスで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);
}
}
}