RxAndroid ble处理重新连接的准则

时间:2018-08-09 11:09:03

标签: android bluetooth bluetooth-lowenergy rxandroidble

[更新]

断开连接后,我能够重新连接到ble设备,但似乎无法读取或写入任何特征。 Logcat在重新连接后将其喷出,这是我的应用程序做错了还是由于设备故障?

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. 我将电话应用程序连接到我的ble设备。一切正常,当ble设备上的温度发生变化时,我会收到来自订阅的通知。

  2. 我可以通过打开设备上的ble芯片或关闭手机上的蓝牙或超出范围来断开连接。

  3. 我在手机或ble设备上再次打开了蓝牙。

  4. 我设法重新连接,但是没有重新订阅我的订阅,因此当温度或其他值发生变化时,我不会收到任何通知回我的手机。

根据我的雇主的说法,该代码在过去应该可以正常工作,但是在断开连接后,我似乎无法使其正常工作。你们中的任何人都可以看到代码逻辑中的任何错误。或ble设备可能有问题?还是这是RxBleConnectionSharingAdapter的常见错误或问题?我已经尝试了一切,但似乎无济于事。

还是我缺少像onUnsibcribeMethod之类的东西?

我想建立连接方法是代码中最相关的部分。通过测试方法重新连接后,我尝试过通过特征重新订阅,但是应用程序随后崩溃了。

这是我的连接管理器类:

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 个答案:

答案 0 :(得分:2)

在建立连接中,将autoConnect参数设置为false,这将防止自动重新连接。如果将其设置为true,它将自动重新连接。请参见“自动连接”下的https://stackoverflow.com/a/40187086/556495http://polidea.github.io/RxAndroidBle/

请注意,如果手机/平板电脑上的蓝牙功能已关闭/重新启动,则此功能将无效。因此,您可能还需要一个蓝牙状态更改广播侦听器,以在发生这种情况时重新启动一切。