我正在尝试在Android上使用RxJava库进行蓝牙处理。我的要求是:
这是我到目前为止所做的,但我不禁认为这是令人费解的。首先,我担心的是我从下游强制错误导致创建新连接的方式。具体来说,当我收到读/解析错误时,我使用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);
}
}
}
答案 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) )
...
只要你需要,这个可观察物就会从蓝牙装置发出弦。