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, но оно обрабатывает только соединение и не повторное соединение, если оно теряет его. Или библиотека должна справиться с этим самостоятельно или я что-то упустил.

Общая проблема может быть описана так:

  1. Я подключаю телефонное приложение к своему устройству. Все работает, как и ожидалось, я получаю уведомления от моих подписок, когда температура меняется на моем устройстве.

  2. Я теряю связь, либо поворачивая микросхему на устройстве, либо отключая Bluetooth на моем телефоне, либо выходя из зоны действия.

  3. Я снова включаю Bluetooth либо на своем телефоне, либо на мобильном устройстве.

  4. Мне удается восстановить соединение, но мои подписки не подписаны повторно, поэтому я не получаю никаких уведомлений на свой телефон, когда температура или другие значения меняются.

По словам моего работодателя, этот код должен был хорошо работать в прошлом, но я не могу заставить его работать после потери соединения. Так что любой из вас, ребята, может увидеть какие-либо ошибки в логике кода. Или может быть проблема с устройством 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, чтобы перезапустить все, когда это произойдет.

Другие вопросы по тегам