RxJava2 - 向上游传输下游错误

时间:2018-04-26 21:53:44

标签: android rx-java rx-java2

我正在尝试在Android上使用RxJava库进行蓝牙处理。我的要求是:

  1. 监视连接状态的observable
  2. 连接会在出现任何错误时自动重新启动
  3. 可观察到来自连接的消息
  4. 这是我到目前为止所做的,但我不禁认为这是令人费解的。首先,我担心的是我从下游强制错误导致创建新连接的方式。具体来说,当我收到读/解析错误时,我使用mCommErrors发布主题。

    我认为所有这些都是必要的,因为我的客户端将始终连接到observeIsConnected()中创建的observable,这会阻止refCount()在出现任何错误时变为0。

    感谢您提供的任何见解。我有一个涉及refCount()操作符的死锁的问题要大得多,所以我试图清除我所做的任何潜在的“坏事”。

    public class MyBluetoothDevice
    {   
        private final MessageParser mParser;
        private final Observable<MyBluetoothConnection> mConnectionObservable;
        // The comms errors will be piped in so we can restart our connection as needed. These errors
        // can originate post-connection when reading / sending messages
        private final PublishSubject<Throwable> mCommsErrors = PublishSubject.create();
    
    public MyBluetoothDevice(BluetoothDevice device, MessageParser parser, Scheduler scheduler)
    {
        mParser = parser;
    
        mConnectionObservable = mCommsErrors
                .switchMap(err -> Observable.<BluetoothSocket> error(err))
                .mergeWith(createConnection(device, SPP_UUID))
                .subscribeOn(scheduler)
                .map(MyBluetoothConnection::new)
                .retryWhen(err -> err.delay(5, TimeUnit.SECONDS))
                .replay(1)
                .refCount();
    }
    
    // Copy this from RxBluetooth since they forget to close the bluetooth socket if an error occurs
    private Observable<BluetoothSocket> createConnection(final BluetoothDevice device, final UUID uuid)
    {
        return Observable.create(emitter -> {
            try {
                final BluetoothSocket bluetoothSocket = device.createRfcommSocketToServiceRecord(uuid);
                emitter.setCancellable(() -> silentlyCloseSocket(bluetoothSocket));
                // We don't have to handle closing the socket if connect fails, because setCancellable
                // will cause the socket to be closed when the error is emitted
                bluetoothSocket.connect();
                emitter.onNext(bluetoothSocket);
            } catch (IOException e) {
                emitter.onError(e);
            }
        });
    }
    
    private void silentlyCloseSocket(BluetoothSocket socket)
    {
        try {
            socket.close();
        } catch (IOException ignore) {
        }
    }
    
    public Observable<Boolean> observeIsConnected()
    {
        return mConnectionObservable
                .switchMap(MyBluetoothConnection::observeIsConnected);
    }
    
    public Observable<TruPulseMessage> observeMessages()
    {
        return mConnectionObservable
                .switchMap(connection -> connection
                        .observeStringLineStream()
                        .switchMap(mParser::parse)
                        .doOnError(err -> connection.closeConnection())
                        .doOnError(err -> mCommsErrors.onNext(err))
                        .onErrorResumeNext(Observable.empty())
                        );
    }
    
    public Completable send(String command)
    {
        return mConnectionObservable
                .take(1)
                .concatMapCompletable(connection -> connection.sendCommand(command))
                .doOnError(mCommsErrors::onNext);
    }
    }
    

    这是MyBluetoothConnection

    public class MyBluetoothConnection
    {
    private final BluetoothSocket mSocket;
    private final InputStream mInputStream;
    private final OutputStream mOutputStream;
    private final BehaviorSubject<Boolean> mIsConnected;
    private Observable<String> mOutObservable;
    
    MyBluetoothConnection(BluetoothSocket socket) throws Exception
    {
        if (socket == null) {
            throw new InvalidParameterException("Bluetooth socket can't be null");
        }
    
        this.mSocket = socket;
        this.mIsConnected = BehaviorSubject.createDefault(Boolean.TRUE);
    
        try {
            mInputStream = socket.getInputStream();
            mOutputStream = socket.getOutputStream();
        } catch (IOException e) {
            closeConnection();
            throw new IOException("Can't get stream from bluetooth socket", e);
        }
    }
    
    public Observable<Boolean> observeIsConnected()
    {
        return mIsConnected;
    }
    
    public Observable<String> observeStringLineStream()
    {
        if (mOutObservable == null) {
            mOutObservable = Observable.create((ObservableOnSubscribe<String>) emitter -> {
                BufferedReader reader = new BufferedReader(new InputStreamReader(mInputStream));
                try {
                    String line = "";
                    while (!emitter.isDisposed() && line != null) {
                        line = reader.readLine();
                        if (line != null)
                            emitter.onNext(line);
                        else
                            emitter.onComplete();
                    }
                } catch (IOException e) {
                    closeConnection();
                    if (!emitter.isDisposed())
                        emitter.onError(e);
                }
            }).share();
        }
        return mOutObservable;
    }
    
    public Completable sendCommand(String command)
    {
        return Completable.create(emitter -> {
            if (!mIsConnected.getValue()) {
                emitter.onError(new IOException("BluetoothConnection is disconnected"));
                return;
            }
    
            String line = command + "\r\n";
            try {
                mOutputStream.write(line.getBytes());
                mOutputStream.flush();
                emitter.onComplete();
            } catch (IOException e) {
                // Error occurred. Better to close terminate the connection
                closeConnection();
                if (!emitter.isDisposed())
                    emitter.onError(new IOException("Can't send Bluetooth command", e));
            }
        });
    }
    
    /**
     * Close the streams and socket connection.
     */
    public void closeConnection()
    {
        if (!mIsConnected.getValue())
            return;
    
        try {
            if (mInputStream != null) {
                mInputStream.close();
            }
    
            if (mOutputStream != null) {
                mOutputStream.close();
            }
    
            if (mSocket != null) {
                mSocket.close();
            }
        } catch (IOException ignored) {
        } finally {
            mIsConnected.onNext(Boolean.FALSE);
        }
    }
    }
    

1 个答案:

答案 0 :(得分:1)

目前尚不清楚为什么你有两个独立的观察链。如果主观察链建立了连接,从连接中读取字符串,抛出错误并完成,那么您可以将重试逻辑应用于一个观察者链。 using()运算符构造一个可观察的项目,该项目将在终止时自行关闭。 switchMap()运算符将在出错或终止时关闭连接,并在创建新套接字时打开一个新连接。

createConnection( device, id )
  .switchMap( socket -> Observable.using( 
                        () -> new MyBlueToothConnection( socket ),
                        connection -> connection.observeStringLineStream(),
                        connection -> connection.close() ) )
 .retryWhen( err -> err.delay(5, TimeUnit.SECONDS) )
 ...

只要你需要,这个可观察物就会从蓝牙装置发出弦。