I want to execute a Completable in flatMap and "map" it to a specific value I need. The subscribe block is never executed. Why?
I'm aware of the existence of flatMapCompletable and andThen, but these don't solve my problem. This code is also a little simplified, in my real code I need to apply more operators to the nested Observable (derived from Completable), so I really need the conversion to Observable.
disposables += myPublishSubject.withLatestFrom(myObservable).flatMap { (_, result) ->
myCompletable()
.toObservable<Unit>()
.map { result } // Return result of "parent" observable after Completable completes
}.subscribe { result ->
Timber.i("result: $result") // Not executed!
}
Completables have no items thus when converted back to Observable, that Observable is also empty and thus never calls map. Use andThen(Observable.just(result)),
disposables += myPublishSubject.withLatestFrom(myObservable).flatMap { (_, result) ->
myCompletable()
.andThen(Observable.just(result))
}.subscribe { result ->
Timber.i("result: $result") // Not executed!
}
or convert the Completable back to single with a default:
disposables += myPublishSubject.withLatestFrom(myObservable)
.flatMapSingle { (_, result) ->
myCompletable()
.toSingleDefault(result)
}.subscribe { result ->
Timber.i("result: $result") // Not executed!
}
Related
I have two functions, the first one that returns a Result with a model, and the second one that returns a Result with another model.
fun flow1(): Flow<Result<Model1>>
fun flow2(id: String): Flow<Result<Model2>>
What i want is went the flow1() call is success, then do the flow2() call and some logic when is success but at the end return the flow1() result.
And for the moment i just trying something like this:
flow1().flatMapLatest { flow1Result ->
flow1Result.onSuccess {
flow2(it.id).map { flow2Result ->
flow2Result.onSuccess {
//Some logic
}
}
}.onFailure {
// return error
}
}
I have two problems the first one that inside the flatMapLatest give an error because say that i return a Result instead of a Flow. And how i can return the Flow1 result?
Thank you!
Trying something similar to this response Chain kotlin flows depends on Result state
I guess you need something like this
fun main() {
flow1().flatMapLatest { flow1Result ->
// should return flow with flow1Result element emited
flow1Result
.onSuccess {
// flow<flow1Result>, that we return
flow2(it.id).map { flow2Result ->
flow2Result
.onSuccess{
TODO("some logic")
}.onFailure{
// only emit flow1Result when flow2Result = Success
throw RuntimeError()
}
// convert flow<flow2Result> to flow <flow1Result>
flow1Result
}
}
.onFailure {
// there can be other flow<flow1Result>
throw RuntimeError()
}
}
}
I have a method which takes a list of object (Widget) -- which contains some properties (header) and nested list(component). I want to flatten the list into a single list and have the below code for same:
#SuppressLint("CheckResult")
fun flatten(fatList: Single<List<Widget>>) {
val flatList: MutableList<IUiData> = mutableListOf()
fatList.map {
Observable.fromIterable(it).map { widget ->
if (widget.header.isNotEmpty()) {
flatList.add(ProductHeaderUi(widget.header))
}
widget.componentList.map { component ->
when (component.type) {
TILE_TEXT -> {
flatList.add(HeaderUi(component))
}
TILE_IMAGE -> {
flatList.add(ImageTileUi(component))
}
TILE_FOOTER -> {
flatList.add(FooterUi(component))
}
UNKNOWN -> {
//Do Nothing
}
}
}
}
}
}
I intend to return a Single of List: Single<MutableList<IUiData>> from this method, this purpose can be served right now, but I am looking for a cleaner way
You're using both Rx's Observable map and Kotlin's Iterable map in an unintended way. They are for converting one type to another, not for iterating something.
You've also nested an unnecessary Observable iterable inside the outer-most map function.
You only need to map the output of the Single. Inside the map function, you iterate (not map) the original List to pull out the data you need for the MutableList.
I'm an Rx novice and didn't check this, so sorry about any syntax errors.
fun flatten(fatList: Single<List<Widget>>): Single<MutableList<IUData>> = fatList.map { widgetList ->
val flatList: MutableList<IUiData> = mutableListOf()
for (widget in widgetList) {
if (widget.header.isNotEmpty()) {
flatList.add(ProductHeaderUi(widget.header))
}
for (component in widget.componentList) {
when (component.type) {
TILE_TEXT -> flatList.add(HeaderUi(component))
TILE_IMAGE -> flatList.add(ImageTileUi(component))
TILE_FOOTER -> flatList.add(FooterUi(component))
// Else do nothing
}
}
}
flatList
}
But in keeping with typical Rx chaining syntax, I would make it an extension function, so I'd have to first line like this. Then you can put it right in the middle of an Rx call chain:
fun Single<List<Widget>>.flatten(): Single<MutableList<IUData>> = map { widgetList ->
You can also do this in a more concise, functional, but less efficient way by using Kotlin's flatMap:
fun Single<List<Widget>>.flatten(): Single<MutableList<IUData>> = map {
it.flatMap { widget ->
listOfNotNull(widget.header.takeIf(Header::isNotEmpty)?.let(::ProductHeaderUi))
+
widget.componentList.mapNotNull { component ->
when (component.type) {
TILE_TEXT -> HeaderUi(component)
TILE_IMAGE -> ImageTileUi(component)
TILE_FOOTER -> FooterUi(component)
else -> null
}
}.toMutableList()
}
...where Header is whatever type widget.header uses.
In the following code, I have a nested observable. The sendMessage in the flatMap calls the sendMessage function which is also an observable. If an exception occurs in this nested observable, the onExceptionResumeNext is suppose to catch the exception, process the exception and then continue on as though nothing happened. The exception does get caught but once the processing on the exception completes, no further emissions are made in the stream. Not even the doOnComplete is called. In essence, the onExceptionResume next just hangs.
I have tried onErrorReturnItem but have the same result. I have not found a single example in Stackoverflow or elsewhere for that matter that even shows onExceptionResumeNext or onErrorResumeNext or onErrorReturnItem inside a nested observable and after a day of working on it, I suspect that it may not be possible to support a nested error handler.
NOTE: In the onExceptionResumeNext I am currently just returning
Observable.empty<MessageToSend>()
In my actual code, I have code to process the exception and I tried returning an observable as well as just returning the data. Doesn't matter what I do - it always hangs.
fun postMessages() {
val msgToSendPublisher = BehaviorSubject.createDefault(MessageToSend())
msgToSendPublisher
.flatMap { _ ->
App.context.repository.getMessageToSend().toObservable()
}
.doOnError { error ->
if (error is EmptyResultSetException)
App.context.repository.setSendStatusToNotSendingForAllMessages()
}
.doOnNext { messageToSend ->
App.context.repository.updateMessage(messageToSend)
}
.flatMap { messageToSend ->
App.context.repository.sendMessage(messageToSend)
}
.doOnNext { messageToSend ->
messageToSend.dateSent = Date()
App.context.repository.updateDateLastMessageSent(messageToSend)
}
.doOnNext { messageToSend ->
if (messageToSend.totalMessagesToSend == 1)
App.context.repository.updateSendStatus(messageToSend, MessageSendStates.NOT_SENDING)
else
Observable.just(messageToSend)
}
.doOnNext {
msgToSendPublisher.onNext(it)
}
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(
{ messageToSend ->
},
{ ex ->
onMessagesSent()
},
{
onMessagesSent()
}
)
}
fun sendMessage(messageToSend: MessageToSend): Observable<MessageToSend> {
val obs = Observable.fromCallable {
if (messageToSend.totalMessagesToSend == 3)
throw Exception("Couldn't send to recipient.")
messageToSend
}.map {
storeMessageSent(messageToSend)
}.onExceptionResumeNext {
Observable.empty<MessageToSend>() // Hangs here.
).doOnComplete {
addNewMessageIfRequired(messageToSend, newMessage)
}
return obs
}
UPDATE:
I decided to test out a sample code I found that uses onExceptionResumeNext. It looks like this:
Observable.fromArray(1, 2, 3)
.doOnNext {
if (it == 2) {
throw (RuntimeException("Exception on 2"))
}
}
.onExceptionResumeNext(
Observable.just(10)
)
.subscribe(
{
var x = it
},
{
var x = it
},
{
var x = 0
x++
}
)
If you put a breakpoint on the line inside of the onExceptionResumeNext, it will get called every single time you run the observable for the first time and not just when the exception is thrown. This is clearly a behavior that is not identified in the RxJava documentation. Any developer will be under the impression that it will only get called when an exception is thrown. In the example above, setting the value to 10 is not really an issue. It's effectively just setting up the return value for the case when an exception occurs. However, if this was more elaborate code that stores stuff in the database (which my app does), it will get called when the observable is initialized - which is really bad. In spite of this discovery, it still does not solve my problem in that no further items are emitted. What I did discover in the sample code is that when onExceptionResumeNext is called, the onComplete is also called. Too bad the documentation doesn't mention that either.
You may want to use defer to defer execution of function calls that result in side-effects upon call:
Observable<Integer> createFallback() {
System.out.println("Why is this executing now?!");
return Observable.empty();
}
Observable.<Integer>error(new Exception())
.onExceptionResumeNext(createFallback())
.subscribe();
The createFallback runs because you specified it to run by invoking it. If the sequence is rewritten, it should become more apparent why:
Observable<Integer> fallback = createFallback();
Observable.<Integer>error(new Exception())
.onExceptionResumeNext(fallback)
.subscribe();
Now if you comment out the error-observable part, does it still execute createFallback()? Yes and RxJava is not even involved at that point yet.
If you want the side-effects to not happen to createFallback this way, you have to defer the execution of the entire method, there is an operator for that purpose: defer:
Observable.<Integer>error(new Exception())
.onExceptionResumeNext(Observable.defer(() -> createFallback()))
.subscribe();
I presume this looks something like this in Kotlin:
Observable.error(new Exception())
.onExceptionResumeNext(Observable.defer { createFallback() })
.subscribe()
I'm using the RxAndroidBle library with RxJava2 to read from a BLE Characteristic. I think this question is just an RxJava question, but including the detail that I'm using RxAndroidBle in case that is useful.
I get connection, and then use it to call readCharacteristic(), which itself returns a Single<ByteArray>. At this point, I don't just want to just get the one ByteArray though. I need to read from this characteristic several times, because the BLE device is set up to let me get a small file back, and characteristics can only send 20 bytes back at a time, hence my need to read repeatedly.
Is it possible to modify this code so that the switchMap() below returns an Observable that will emit many ByteArrays, instead of just the single one?
I'm new to RxJava.
val connection: Observable<RxBleConnection> = selectedDevice.record.bleDevice.establishConnection(false, Timeout(30, TimeUnit.SECONDS))
return connection
.subscribeOn(Schedulers.io())
.switchMap {
// I want to get an Observable that can read multiple times here.
it.readCharacteristic(serverCertCharacteristicUUID).toObservable()
}
.doOnNext {
Timber.e("Got Certificate bytes")
}
.map {
String(it as ByteArray)
}
.doOnNext {
Timber.e("Got certificate: $it")
}
.singleOrError()
To repeat a read multiple times until a specific value is emitted one needs to change this part:
// I want to get an Observable that can read multiple times here.
it.readCharacteristic(serverCertCharacteristicUUID).toObservable()
to something like what was suggested by the RxJava author in the first answer that google gives for phrase rxjava single repeat:
// this will repeat until a `checkRepeatIf` returns false
Observable.defer {
val successValue = AtomicReference<ByteArray>()
connection.readCharacteristic(serverCertCharacteristicUUID)
.doOnSuccess { successValue.lazySet(it) }
.repeatWhen { completes -> completes.takeWhile { checkRepeatIf(successValue.get()) } }
}
I was able to get this working by sending a signal to stop both the connectionObservable, and the read on the Bluetooth characteristic. Of note is that you need to call toObservable() AFTER repeat() or this doesn't work, although I don't know why exactly.
override fun readMultipartCharacteristic(macAddress: String): Single<String> {
val CERTIFICATE_TERMINATOR = 0x30.toByte()
val device = bluetoothService.getBleDevice(macAddress)
if (connectionObservable == null || !device.connectionState.equals(RxBleConnection.RxBleConnectionState.CONNECTED)) {
connectionObservable = device.establishConnection(false, Timeout(30, TimeUnit.SECONDS))
}
val stop: PublishSubject<Unit> = PublishSubject.create()
return connectionObservable!!
.subscribeOn(Schedulers.io())
.takeUntil(stop)
.switchMap {
it.readCharacteristic(UUID("my-uuid"))
.repeat()
.toObservable()
.takeUntil(stop)
}
.collectInto(ByteArrayOutputStream(), { buffer, byteArray ->
// Watch for the signal of the end of the stream
if (byteArray.size == 1 && byteArray.get(0).equals(CERTIFICATE_TERMINATOR)) {
stop.onComplete()
} else {
buffer.write(byteArray)
}
})
.map {
String(it.toByteArray())
}
}
You can use the notification to buffer your data.
device.establishConnection(false)
.flatMap(rxBleConnection -> rxBleConnection.setupNotification(characteristicUuid))
.flatMap(notificationObservable -> notificationObservable) // <-- Notification has been set up, now observe value changes.
.subscribe(
bytes -> {
// Given characteristic has been changes, here is the value.
},
throwable -> {
// Handle an error here.
}
);
This seems like it should be simple, but I'm struggling with the correct way of flowing the RxJava chain.
For instance, what isn't working is using flatMap to both persist data and check if another api query is needed:
return remote(amount = 2))
.subscribeOn(Schedulers.io())
.flatMap {
insertAll(it)
// Return an Observable<Boolean>, true if another api query is needed
shouldGetMore(it)
}
.flatMap {
if (it) remote(amount = 3)
// If another query is not needed just return an empty observable
else Observable.just(listOf())
}
.flatMapCompletable { insertAll(it) /* If another query was needed insert the result here */ }
.observeOn(AndroidSchedulers.mainThread())
With the above, insertAll() isn't called the first time because the function insetrtAll is a completable and is therefore not subscribed to in the flatMap. Instead only shouldGetMore() is executed. Now, here is my question:
For this to work I would need a flatMapCompletable at the beginning like so:
return remote(amount = 2))
.subscribeOn(Schedulers.io())
.flatMapCompletable { insertAll(it) }
// ...
But if I do this then I no longer have access to the results of the first api query to check if I shouldGetMore. My only thought is making insertAll not a Completable but rather an Observable that returns what was persisted after the fact, thus allowing me to complete the chain.
But this seems like an ugly approach, so I would be curious if anyone else had any other ideas?
Thanks.
The Completable offers the andThen operator to continue with any of the reactive base types:
return remote(amount = 2))
.subscribeOn(Schedulers.io())
.flatMap {
insertAll(it)
.andThen(shouldGetMore(it)) // <----------------------------------------
}
.flatMap {
if (it) remote(amount = 3)
else Observable.just(listOf())
}
.flatMapCompletable { insertAll(it) }
.observeOn(AndroidSchedulers.mainThread())