I have these two Observables in Kotlin where is just act as a timer and another one is HTTP network call response Observer.
timerDisposable = Observable.timer(daleyABCControlResetSeconds, TimeUnit.SECONDS, AndroidSchedulers.mainThread())
.doOnNext {
if (getABCUpdate() != null) {
Log.d("ABC", "Media status reset after 3 seconds: ")
updateABCResponse(getABCUpdate())
}
}.subscribe()
disposable = audioApi.setABCUpdate(abcUpdate)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe({
timerDisposable.dispose()
updateABCResponse(it)
Log.d("ABC", "Media Status updated:")
}, {
Log.d("ABC", "Error updating Media Status: " + it.message)
isABCControlChangeRequested = false
})
I am not satisfied with this approach, can anyone please direct me right direction to use the rx's full potential. Thanks in advance.
EDIT
Observable.combineLatest(Observable.timer(daleyABCControlResetSeconds, TimeUnit.SECONDS, AndroidSchedulers.mainThread())
.doOnNext {
if (getABCUpdate() != null) {
Log.d("ABC", "Media status reset after 3 seconds: ")
updateABCResponse(getABCUpdate())
}
},
audioApi.setABCUpdate(abcUpdate)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread()),
BiFunction<Long, ABCStatusUpdate, ABCStatusUpdate> { _, abcStatusUpdate ->
abcStatusUpdate
})
.subscribe({
timerDisposable.dispose()
updateABCResponse(abcStatusUpdate)
Log.d("ABC", "Media Status updated:")
}, {
Log.d("ABC", "Error updating Media Status: " + abcStatusUpdate.vol)
isABCControlChangeRequested = false
})
You can use combinelatest, zip or merge for combinig. I think in your case combinelatest is suitable
Observable.combineLatest(
Observable.timer(daleyABCControlResetSeconds, TimeUnit.SECONDS, AndroidSchedulers.mainThread())
.doOnNext {
if (getABCUpdate() != null) {
Log.d("ABC", "Media status reset after 3 seconds: ")
updateABCResponse(getABCUpdate())
}
},
audioApi.setABCUpdate(abcUpdate)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread()),
BiFunction<Long, YourApiResponseType, YourApiResponseType> { _, response ->
response})
.subscribe({
timerDisposable.dispose()
updateABCResponse(it)
Log.d("ABC", "Media Status updated:")
}, {
Log.d("ABC", "Error updating Media Status: " + it.message)
isABCControlChangeRequested = false
})
UPD:
You can change your code like this:
Observable.timer(5, TimeUnit.SECONDS, AndroidSchedulers.mainThread()).startWith(-1L)
.doOnNext {
if (it == -1L) return#doOnNext
//your condition
}
Related
I have a Observable with a chained Completeable with a chained Single that is called at an interval of 5 seconds.
public void getCoinPrices() {
disposable = Observable
.interval(5, TimeUnit.SECONDS)
.flatMapCompletable(n -> {
Timber.d("Called flatmap completeable: " + n);
boolean isFirstTime = sharedPrefManager.isFirstTimeOpeningApp();
if (isFirstTime) {
Timber.d("Is first time.");
return insertFavoritesUseCase.insertStartingCoins()
.andThen(Completable.fromAction(() -> {
sharedPrefManager.setFirstTimeOpeningApp(false);
}));
} else {
Timber.d("Not first time.");
return Completable.complete();
}
})
.andThen(getFavoritesUseCase.getFavoriteCoins())
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.flatMap(listFavorites -> {
Timber.d("List favorites called...");
return getCoinsUseCase.getCoinPrices(listFavorites);
})
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(listCoinsWithPrice -> {
Timber.d("Called.......");
}, err -> {
Timber.e("Failed to get price of coins: " + err);
});
}
I can't figure out why the .andThen(getFavoritesUseCase.getFavoriteCoins()) is never fired.
public Single<List<CoinStatus>> getFavoriteCoins() {
Timber.d("Get Favorite Coins Fired");
return localRepository.getFavoriteCoins();
}
I have tested the flag isFirstTime with true and false, but the .andThen() is still never fired.
What am I doing incorrectly here?
I got it the interval to work by using akarnokd's advice removing the Completeable. I used a Single instead and continued the chain flow by passing a value with Single.just(). Please let me know if there's a better way or cleaner way to do it!
disposable = Observable.interval(0, 15, TimeUnit.SECONDS)
.flatMapSingle(n -> {
boolean isFirstTime = sharedPrefManager.isFirstTimeOpeningApp();
if (isFirstTime) {
return insertFavoritesUseCase.insertStartingCoins().doOnSuccess(insertResult -> {
sharedPrefManager.setFirstTimeOpeningApp(false);
});
} else {
return Single.just("Did not insert coins, continuing stream");
}
})
.flatMapSingle(insertResult -> getFavoritesUseCase.getFavoriteCoins())
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.flatMapSingle(listFavorites -> {
//update ui
return getCoinsUseCase.getCoinPrices(listFavorites);
})
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(listCoinsWithPrice -> {
//update ui
}, err -> {
Timber.e("Failed to get price of coins: " + err);
});
I am developing an Android app using RxJava.
I have some API call chains.
verify
consume
val verify = Completable.error(Exception("TEST"))
.doOnSubscribe { Log.d(TAG, "1. verify") }
.doOnComplete{ Log.d(TAG, "1. verify - success") }
.doOnError { Log.e(TAG, "1. verify - failed: ${it.message}") }
.retryWhen { attempts ->
attempts.zipWith(
Flowable.range(1, 3), BiFunction<Throwable, Int, Long> { t, i ->
if (i <= 3) {
1L
} else {
throw t
}
}
).flatMap {
Flowable.timer(it, TimeUnit.SECONDS)
}
}
// 2. consume
val consume = Single.just("SUCCESS")
.doOnSubscribe { Log.d(TAG, "2. consume") }
.doOnSuccess { Log.d(TAG, "2. consume - success") }
.doOnError { Log.e(TAG, "2. consume - failed: ${it.message}", it) }
disposable.add(
verify.andThen (consume)
.subscribeOn(ioScheduler)
.observeOn(uiScheduler)
.subscribe({
Log.d(TAG, "done")
}, { t ->
Log.e(TAG, "failed: ${t.message}", t)
})
);
What I excepted is...
"verify" should be called 3 times every 1 seconds.
After 3 retries failed, it should be done with Error.
But in my case, "consume" was run too.
Why?
I want to skip "consume" if "verify" is failed!
How can I do it?
It's because your code is not failing.
With Flowable.range(1, 3) you create a range from 1 to 3 so the else part of your code is never reached.
Try with Flowable.range(1, 4) and you will see the correct behaviour.
I'm trying to retrieve a value from a Bluetooth Device.
if (rxBleDevice.connectionState != RxBleConnection.RxBleConnectionState.CONNECTED) {
rxBleDevice!!.establishConnection(false) ? .subscribe({
rxBleConnection ->
Log.d("Device: ", "Connection Established")
val stringDeviceUUID = rxBleDevice.bluetoothDevice.uuids[0].toString()
val charUUID = UUID.nameUUIDFromBytes(stringDeviceUUID.toByteArray())
val count = rxBleConnection.readCharacteristic(charUUID)
println("OUTPUT: ${count}")
}, {
throwable -> Log.d("Device: ", "$throwable")
})
}
I'm using the following dependencies, mainly RxJava and a reactive Bluetooth library called RxAndroidBLE:
implementation 'io.reactivex.rxjava2:rxkotlin:2.1.0'
implementation "com.polidea.rxandroidble2:rxandroidble:1.8.1"
implementation "io.reactivex.rxjava2:rxjava:2.2.7"
My output:
I/System.out: OUTPUT: io.reactivex.internal.operators.single.SingleFlatMap#bf9162d
I have no how to process this object. I believe I should be receiving a simple ByteArray from the Bluetooth device.
An example for the value I should see is datc00099, indicating a count of 99.
You are supposed to subscribe to Single. Following the examples provided by RxAndroidBle, something like this might work in your case:
if (rxBleDevice.connectionState != RxBleConnection.RxBleConnectionState.CONNECTED) {
// Have your charUUID ready. Might need extra null checks for rxBleDevice
val charUUID = rxBleDevice.bluetoothDevice.uuids[0].uuid
rxBleDevice!!.establishConnection(false) ?
.doOnNext {
_ -> Log.d("Device: ", "Connection Established")
} ?
.flatMapSingle {
rxBleConnection -> rxBleConnection.readCharacteristic(charUUID)
} ? .subscribe({
count ->
// count should be in bytes
println("OUTPUT: $count")
}, {
throwable ->
Log.d("ERROR: ", "$throwable")
})
}
just asking if I am doing it correct, because I don't know why the doOnComplete is calling while the doOnNext is not yet finish?
So that's why, I am asking on how to wait all the task inside the doOnNext
before calling the doOnComplete?
The other task inside the doOnNext is the inserting of data into the database.
private val disposable = CompositeDisposable()
val branchUser : Observable<BranchUserResponse> = getApi().getBranchUser()
val areaUser : Observable<AreaUserResponse> = getApi().getAreaUser()
val regionalUser : Observable<RegionalUserResponse> = getApi().getRegionalUser()
disposable.add(
Observable.merge(branchUser, areaUser, regionalUser)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.doOnNext { it ->
when (it) {
is BranchUserResponse -> {
branchUserViewModel.addAll()
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(
{
Log.i(TAG, "addAll success")
// the doOnComplete is already called before the Log here is call.
// Same with the other condition.
// What I want is to call this first before the doOnComplete or doFinally.
},
{
Log.e(TAG, "addAll failed", it)
}
)
}
is AreaUserResponse -> {
// some stuff here...
}
is RegionalUserResponse -> {
// some stuff here...
}
}
}
.doOnComplete {
Log.i(TAG, "Complete")
}
.doFinally {
Log.i(TAG, "Finally")
}
.subscribe()
)
Any help is appreciated, Thanks.
If you are going to do rx-stuff in all branches specified in doOnNext you have to change doOnNext to flatMap:
private val disposable = CompositeDisposable()
val branchUser : Observable<BranchUserResponse> = getApi().getBranchUser()
val areaUser : Observable<AreaUserResponse> = getApi().getAreaUser()
val regionalUser : Observable<RegionalUserResponse> = getApi().getRegionalUser()
disposable.add(
Observable.merge(branchUser, areaUser, regionalUser)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.flatMap { it ->
when (it) {
is BranchUserResponse -> {
branchUserViewModel.addAll()
}
is AreaUserResponse -> {
// some stuff here...
}
is RegionalUserResponse -> {
// some stuff here...
}
}
}
.doOnComplete {
Log.i(TAG, "Complete")
}
.doFinally {
Log.i(TAG, "Finally")
}
.subscribe()
)
I'm doing an app that works as a remote control to a ventilator using RxAndroidBle. I have a problem with the unsubscribe because when I use
.flatMap(rxBleConnection -> rxBleConnection.writeCharacteristic(Uuids.UUID_RX, flaktCommandConcat.getBytes()))
and after that I use subscription.unsubscribe(); the writeCharacteristics doesn´t work because the unsubscribe runs always first and the connection disconect before the data was sent.
What I need is:
When I click the button I want to connect to the ventilator
Then send all values
And then disconnect.
If I repeat the procedure, it will need to do the same thing over and over again.
Can some one help me with some idea? I tried to use .delay(1000, Time.MILISECONDS) and it worked but it took a long time to send the information to the ventilator.
This is my code:
public void writeRxCharacteristics(String flaktCommandConcat){
rxBleDevice = rxBleClient.getBleDevice(Uuids.DEVICE_ADDRESS);
subscription = rxBleDevice.establishConnection(true) //false
.observeOn(AndroidSchedulers.mainThread())
.flatMap(rxBleConnection -> rxBleConnection.createNewLongWriteBuilder()
.setCharacteristicUuid(Uuids.UUID_RX)
.setBytes(flaktCommandConcat.getBytes())
.build())
.subscribe(
byteArray -> {
Log.d("CharacteristicValue","WRITE: " + Arrays.toString(byteArray));
},
throwable -> {
Log.d("CharacteristicValue","Throwable: " + throwable.toString());
rxBleActivity.onScanFailure(throwable, getContext());
}
);
rxBleDevice.observeConnectionStateChanges()
.observeOn(AndroidSchedulers.mainThread())
.delay(1000, TimeUnit.MILLISECONDS)
.subscribe(
rxBleConnectionState -> {
Log.d("RxBleConnectionState", " CON_STATUS: " + rxBleConnectionState);
disconnect();
},
throwable -> {
Log.d("ConnectionStateChanges","Throwable: " + throwable.toString());
}
);
}
public void disconnect() {
if (subscription != null && !subscription.isUnsubscribed()) {
subscription.unsubscribe();
subscription = null;
}
Log.d("CONNECTION2", " CON_STATUS: " + rxBleDevice.getConnectionState().toString());
}
it looks that you don't need a long write here. Is your data longer than 20 bytes?
Anyway, the library releases the connection when the Observable<RxBleConnection> is unsubscribed. What I'd do if I were you is to:
public void writeRxCharacteristics(String flaktCommandConcat){
rxBleDevice = rxBleClient.getBleDevice(Uuids.DEVICE_ADDRESS);
rxBleDevice.establishConnection(true) //false
.flatMap(rxBleConnection -> rxBleConnection.createNewLongWriteBuilder()
.setCharacteristicUuid(Uuids.UUID_RX)
.setBytes(flaktCommandConcat.getBytes())
.build()
)
.take(1)
.observeOn(AndroidSchedulers.mainThread())
.subscribe(
byteArray -> {
Log.d("CharacteristicValue","WRITE: " + Arrays.toString(byteArray));
},
throwable -> {
Log.d("CharacteristicValue","Throwable: " + throwable.toString());
rxBleActivity.onScanFailure(throwable, getContext());
}
);
Please make sure you're not overusing the long write. It has a known bug (unrelated) in 1.2.0 which was recently fixed in 1.3.0-SNAPSHOT.