Rxandroid ble Руководство по обработке повторного подключения
[Обновить]
Я могу подключиться к устройству после отключения, но не могу прочитать или записать какие-либо характеристики. Logcat выдает это после повторного подключения, это мое приложение делает что-то не так или это из-за устройства ble?
08-09 15:05:45.109 9601-10364/com.project.app D/BluetoothGatt: setCharacteristicNotification() - uuid: cb67d1e1-cfb5-45f5-9123-3f07d9189f1b enable: false
08-09 15:05:45.111 9601-10352/com.project.app D/RxBle#ConnectionOperationQueue: STARTED DescriptorWriteOperation(54881118)
08-09 15:05:45.114 9601-10364/com.project.app D/RxBle#ConnectionOperationQueue: QUEUED DescriptorWriteOperation(191754430)
08-09 15:05:45.116 9601-9601/com.project.app D/BtleConnManager: RETRY 2/-1 :::: com.polidea.rxandroidble.exceptions.BleCannotSetCharacteristicNotificationException
08-09 15:06:15.117 9601-10352/com.project.app D/RxBle#ConnectionOperationQueue: FINISHED DescriptorWriteOperation(54881118)
08-09 15:06:15.118 9601-10365/com.project.app D/BluetoothGatt: setCharacteristicNotification() - uuid: cb67d0e1-cfb5-45f5-9123-3f07d9189f1b enable: false
08-09 15:06:15.120 9601-10352/com.project.app D/RxBle#ConnectionOperationQueue: STARTED DescriptorWriteOperation(88995281)
08-09 15:06:15.124 9601-10365/com.project.app D/RxBle#ConnectionOperationQueue: QUEUED DescriptorWriteOperation(108601267)
08-09 15:06:15.124 9601-10366/com.project.app D/BluetoothGatt: setCharacteristicNotification() - uuid: cb67d1e1-cfb5-45f5-9123-3f07d9189f1b enable: true
08-09 15:06:15.126 9601-9601/com.project.app D/BtleConnManager: RETRY 2/-1 :::: com.polidea.rxandroidble.exceptions.BleCannotSetCharacteristicNotificationException
08-09 15:06:15.131 9601-10366/com.project.app D/RxBle#ConnectionOperationQueue: QUEUED DescriptorWriteOperation(98838561)
08-09 15:06:45.126 9601-10352/com.project.app D/RxBle#ConnectionOperationQueue: FINISHED DescriptorWriteOperation(88995281)
[Обновить]
Использование rxandroidble1 и rxjava1
Привет, я новичок в концепции rxjava и ble соединений, но меня поместили в существующий проект с очень небольшим количеством документации, и у меня возникли проблемы с обработкой повторного соединения после потери соединения.
Я проверил пример приложения rxandroidble, но оно обрабатывает только соединение и не повторное соединение, если оно теряет его. Или библиотека должна справиться с этим самостоятельно или я что-то упустил.
Общая проблема может быть описана так:
Я подключаю телефонное приложение к своему устройству. Все работает, как и ожидалось, я получаю уведомления от моих подписок, когда температура меняется на моем устройстве.
Я теряю связь, либо поворачивая микросхему на устройстве, либо отключая Bluetooth на моем телефоне, либо выходя из зоны действия.
Я снова включаю Bluetooth либо на своем телефоне, либо на мобильном устройстве.
Мне удается восстановить соединение, но мои подписки не подписаны повторно, поэтому я не получаю никаких уведомлений на свой телефон, когда температура или другие значения меняются.
По словам моего работодателя, этот код должен был хорошо работать в прошлом, но я не могу заставить его работать после потери соединения. Так что любой из вас, ребята, может увидеть какие-либо ошибки в логике кода. Или может быть проблема с устройством ble? Или это общая ошибка или проблема с RxBleConnectionSharingAdapter или как? Я перепробовал все, но ничего не работает.
Или я что-то упускаю, например, метод UnSibcribe, или что-то в этом роде?
Я предполагаю, что метод установления соединения является наиболее важной частью кода. Iv'e пытался повторно подписаться через характеристику после повторного подключения с помощью тестового метода, но тогда приложение просто зависало.
Это мой класс диспетчера соединений:
private static final String TAG = "HELLOP";
private static RxBleClient rxBleClient;
private RxBleConnection rxBleConnection;
private static final int MAX_RETRIES = 10;
private static final int SHORT_RETRY_DELAY_MS = 1000;
private static final int LONG_RETRY_DELAY_MS = 30000;
private final Context mContext;
private final String mMacAddress;
private final String gatewayName;
private final RxBleDevice mBleDevice;
private PublishSubject<Void> mDisconnectTriggerSubject = PublishSubject.create();
private Observable<RxBleConnection> mConnectionObservable;
private final ProjectDeviceManager mProjectDeviceManager;
private BehaviorSubject<Boolean> connectionStatusSubject = BehaviorSubject.create();
private boolean isAutoSignIn = false;
private BondStateReceiver bondStateReceiver;
private boolean isBonded = false;
//gets the client
public static RxBleClient getRxBleClient(Context context) {
if (rxBleClient == null) {
// rxBleClient = /*MockedClient.getClient();*/RxBleClient.create(this);
// RxBleClient.setLogLevel(RxBleLog.DEBUG);
// super.onCreate();
rxBleClient = RxBleClient.create(context);
RxBleClient.setLogLevel(RxBleLog.DEBUG);
}
return rxBleClient;
}
public BtleConnectionManager(final Context context, final String macAddress, String name) {
mContext = context;
mMacAddress = macAddress;
gatewayName = name;
mBleDevice = getRxBleClient(context).getBleDevice(macAddress);
mProjectDeviceManager = new ProjectDeviceManager(this);
}
@Override
public final Context getContext() {
return mContext;
}
@Override
public final ProjectDeviceManager getProjectDeviceManager() {
return mProjectDeviceManager;
}
@Override
public final boolean isConnected() {
return mBleDevice.getConnectionState() == RxBleConnection.RxBleConnectionState.CONNECTED;
}
@Override
public String getConnectionName() {
if (gatewayName != null && !gatewayName.isEmpty()) {
return gatewayName;
} else {
return mMacAddress;
}
}
final RxBleDevice getBleDevice() {
return mBleDevice;
}
public final synchronized Observable<RxBleConnection> getConnection() {
if (mConnectionObservable == null || mBleDevice.getConnectionState() == RxBleConnection.RxBleConnectionState.DISCONNECTED
|| mBleDevice.getConnectionState() == RxBleConnection.RxBleConnectionState.DISCONNECTING) {
establishConnection();
}
return mConnectionObservable;
}
public void goBack() {
Intent intent = null;
try {
intent = new Intent(mContext,
Class.forName("com.Project.dcpapp.BluetoothActivity"));
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK);
((Activity) mContext).startActivity(intent);
((Activity) mContext).finish();
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
public void setAutoSignIn(boolean value) {
this.isAutoSignIn = value;
}
public boolean getAutoSignIn() {
return this.isAutoSignIn;
}
@Override
public void pause() {
}
@Override
public void resume() {
}
@Override
public Observable<Boolean> observeConnectionStatus() {
return connectionStatusSubject;
}
@Override
public Calendar getLastConnectionTime() {
return mProjectDeviceManager.getLastUpdateTime();
}
public void disconnect() {
BluetoothDevice bluetoothDevice = getBleDevice().getBluetoothDevice();
Log.d("BtleConnManager", " disconnect " + bluetoothDevice.getBondState());
Handler handler = new Handler(Looper.getMainLooper());
handler.postDelayed(new Runnable() {
@Override
public void run() {
mDisconnectTriggerSubject.onNext(null);
mConnectionObservable = null;
}
}, 700);
}
public void removeBond() {
Method m = null;
BluetoothDevice bluetoothDevice = getBleDevice().getBluetoothDevice();
Log.d("BtleConnManager", " removeBond " + bluetoothDevice.getBondState());
if (bluetoothDevice.getBondState() == BluetoothDevice.BOND_BONDED) {
try {
m = bluetoothDevice.getClass().getMethod("removeBond", (Class[]) null);
m.invoke(bluetoothDevice, (Object[]) null);
} catch (NoSuchMethodException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
}
}
}
public void bond() {
BluetoothDevice bluetoothDevice = getBleDevice().getBluetoothDevice();
Log.d("BtleConnManager ", "bond state " + bluetoothDevice.getBondState());
if (bluetoothDevice.getBondState() == BluetoothDevice.BOND_NONE
&& Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
bondStateReceiver = new BondStateReceiver();
final IntentFilter filter = new IntentFilter(BluetoothDevice.ACTION_BOND_STATE_CHANGED);
getContext().registerReceiver(bondStateReceiver, filter);
bluetoothDevice.createBond();
}
}
public void setBonded(boolean value) {
this.isBonded = value;
}
public boolean isBonded() {
return this.isBonded;
}
private class BondStateReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
final String action = intent.getAction();
if (action.equals(BluetoothDevice.ACTION_BOND_STATE_CHANGED)) {
final int state = intent.getIntExtra(BluetoothDevice.EXTRA_BOND_STATE, BluetoothDevice.ERROR);
switch (state) {
case BluetoothDevice.BOND_BONDED:
setBonded(true);
Log.d("BtleConManager", "Bonded ");
break;
case BluetoothDevice.BOND_BONDING:
Log.d("BtleConManager", "Bonding ");
break;
case BluetoothDevice.BOND_NONE:
Log.d("BtleConManager", "unbonded ");
setBonded(false);
final int prevState = intent.getIntExtra(BluetoothDevice.EXTRA_PREVIOUS_BOND_STATE, BluetoothDevice.ERROR);
if (prevState == BluetoothDevice.BOND_BONDING) {
Toast.makeText(getContext(), R.string.error_bluetooth_bonding_failed, Toast.LENGTH_LONG).show();
}
break;
}
}
}
}
private void establishConnection() {
Log.d("BtleConnManager", " establishConnection");
mConnectionObservable = mBleDevice
.establishConnection(false)
.observeOn(AndroidSchedulers.mainThread())
.doOnNext(rxBleConnection -> {
// Save connection to use if retry is done when already connected
this.rxBleConnection = rxBleConnection;
// Notify observers that connection is established
connectionStatusSubject.onNext(true);
})
.onErrorResumeNext(error -> {
// Return the saved connection if already connected
if (error instanceof BleAlreadyConnectedException && rxBleConnection != null) {
return Observable.just(rxBleConnection);
} else {
return Observable.error(error);
}
})
//.retryWhen(getRetryRule()) Do not retry connect here - retry when using getConnection instead (otherwise a double retry connection will be done)
.takeUntil(mDisconnectTriggerSubject)
.doOnError(throwable -> {
this.rxBleConnection = null;
if (!isConnected()) {
// Notify observers that connection has failed
connectionStatusSubject.onNext(false);
}
}).doOnUnsubscribe(() -> {
Log.d("BtleConnManager", "establishConnection Unsubscribe ");
connectionStatusSubject.onNext(false);
}).doOnCompleted(() -> Log.d("BtleConnManager", "establishConnection completed"))
.doOnSubscribe(() -> {
})
//.subscribeOn(AndroidSchedulers.mainThread())
//.compose(bindUntilEvent(PAUSE))
.compose(new ConnectionSharingAdapter());
}
public void test(){
mConnectionObservable
.flatMap(rxBleConnection -> rxBleConnection.setupNotification(UUID.fromString("cb67d1c1-cfb5-45f5-9123-3f07d9189f1b")))
.flatMap(notificationObservable -> notificationObservable)
.observeOn(AndroidSchedulers.mainThread())
.retryWhen(errors -> errors.flatMap(error -> {
if (error instanceof BleDisconnectedException) {
Log.d("Retry", "Retrying");
return Observable.just(null);
}
return Observable.error(error);
}))
.doOnError(throwable -> {
Log.d(TAG, "establishConnection: " + throwable.getMessage());
})
.subscribe(bytes -> {
Log.d(TAG, "establishConnection: characteristics changed" + new String(bytes));
// React on characteristic changes
});
}
public RetryWithDelay getRetryRule() {
return new RetryWithDelay(MAX_RETRIES, SHORT_RETRY_DELAY_MS);
}
public RetryWithDelay getInfiniteRetryRule() {
return new RetryWithDelay(RetryWithDelay.NO_MAX, LONG_RETRY_DELAY_MS);
}
public class RetryWithDelay implements
Func1<Observable<? extends Throwable>, Observable<?>> {
public static final int NO_MAX = -1;
private final int maxRetries;
private final int retryDelayMs;
private int retryCount;
public RetryWithDelay(final int maxRetries, final int retryDelayMs) {
this.maxRetries = maxRetries;
this.retryDelayMs = retryDelayMs;
this.retryCount = 0;
}
@Override
public Observable<?> call(Observable<? extends Throwable> attempts) {
return attempts
.flatMap(new Func1<Throwable, Observable<?>>() {
@Override
public Observable<?> call(Throwable throwable) {
++retryCount;
if (mConnectionObservable == null) {
// If manually disconnected return empty observable
return Observable.empty();
} else if (throwable instanceof BleAlreadyConnectedException) {
return Observable.error(throwable);
} else if (retryCount < maxRetries || maxRetries == NO_MAX) {
Log.d("BtleConnManager", " RETRY " + retryCount + "/" + maxRetries + " :::: " + throwable.getClass().getName());
// When this Observable calls onNext, the original
// Observable will be retried (i.e. re-subscribed).
return Observable.timer(retryDelayMs, TimeUnit.MILLISECONDS);
} else {
//Last try
Log.d("BtleConnManager", " LAST RETRY " + retryCount + "/" + maxRetries + " :::: " + throwable.getClass().getName());
return Observable.error(throwable);
}
}
});
}
}
1 ответ
В установлении соединения вы устанавливаете для параметра autoConnect значение false, что предотвратит автоматическое переподключение. Если вы установите для него значение true, оно должно автоматически переподключиться. См. /questions/37683448/kakoj-pravilnyij-flag-autoconnect-v-connectgatt-ble/37683459#37683459 и http://polidea.github.io/RxAndroidBle/ разделе "Автоматическое подключение".
Обратите внимание, что это не будет работать, если Bluetooth выключен / перезапущен на телефоне / планшете. Так что вам, вероятно, также понадобится прослушиватель широковещательной рассылки изменения состояния Bluetooth, чтобы перезапустить все, когда это произойдет.