Switching from rx.Observable to rx.Single - java

I'm using rx.Observable all over my code and when I moved to a newer version of RxJava I have the need to switch to rx.Single.
Here is an example of how I use rx.Observable in my code:
obj.doSomething()
.filter(res -> {
return res.getStatus() == 0 ? true : false;
})
.subscribe(res -> {
handleResult(res);
});
With the new rxJava version, I had to change doSomething() to return rx.Single, so I can't use .filter() anymore making me handle its code in the .subscribe().
I don't like it so much because it was nice having a special .filter block that used to handle the control and in case of false it would NOT reach the .subscribe().
Any workaround or better design to avoid handling the boilerplate code in .subscribe()?

Related

Conversion from Observable to Mono in Kotlin

Trying to insert in couchbase and that has observable return type but want mono,hence did this.
Its compiling but at run time its getting stuck forever at the conversion stage (i.e Mono.from { obs }).
fun saveScopeId(scopeId: ScopeId): Mono<ScopeId> {
val obs = scopeRepository.couchbaseOperations.insert(scopeId)
return Mono.from<ScopeId> { obs }
}
Observable can generate multiple values but if you can guarantee that it will be one item (I assume this is why you want to use Mono here) you can use Mono.fromDirect in this way:
Mono.fromDirect(yourObservable.toFlowable(BackpressureStrategy.BUFFER));
As you can see in example, there is toFlowable method used.
You should see the other backpressure strategies: here
This way we can achieve but not sure about the performance part.
As there Rx -> Rx -> Reactor conversion. Can someone tell me by looking into couchbase SDK 4.x (introduced recently), only if there are some conversion issue.
Mono.fromDirect(RxReactiveStreams.toPublisher(scopeRepository.couchbaseOperations.insert(scope)))
Try this but this thread blocking model.
public Mono<String> GetData(Observable<String> inputData) {
return Mono.fromCallable(() -> inputData.blockingFirst(""));
}

Sync two asynchronous API call with RxJava

In what way can we sync two asynchronous calls using RxJava? In the example below, the method contentService.listContents which is a API call must first finish before the processSchema method to take place for each schema.
schemaService.listSchema()
.toObservable()
.flatMapIterable(schemas -> {
schemas.forEach(schema -> {
// async call
contentService.listContents(schema.getName()).subscribe(contents -> {
doSomethingWithThe(contents);
});
});
// contentService.listContents` must complete first before
// processSchema should be called for each schema
return schemas;
}).subscribe(schema -> { processSchema(schema); },
error -> { Console.error(error.getMessage()); });
The problem with the code above the processSchema would not wait for the contentService.listContents since it is async not not synchronized with each other.
You have to use flatMap to process the schemas and since it is a list, you have to unroll it and flatMap again:
schemaService.listSchema()
.toObservable()
.flatMap(schemas ->
Observable.fromIterable(schemas)
.flatMap(schema ->
contentService.listContents(schema.getName())
.doOnNext(contents -> doSomethingWith(contents))
)
// probably you don't care about the inner contents
.ignoreElements()
// andThen will switch to this only when the sequence above completes
.andThen(Observable.just(schemas))
)
.subscribe(
schema -> processSchema(schema),
error -> Console.error(error.getMessage())
);
Note that you haven't defined the return types of the service calls so you may have to use flatMapSingle and doOnSuccess for example.
You are probably looking for flatMap.
From the docs
Continuations
Sometimes, when an item has become available, one would
like to perform some dependent computations on it. This is sometimes
called continuations and, depending on what should happen and what
types are involved, may involve various operators to accomplish.
Dependent
The most typical scenario is to given a value, invoke
another service, await and continue with its result:
service.apiCall()
.flatMap(value -> service.anotherApiCall(value))
.flatMap(next -> service.finalCall(next))
It is often the case also that later sequences would require values
from earlier mappings. This can be achieved by moving the outer
flatMap into the inner parts of the previous flatMap for example:
service.apiCall()
.flatMap(value ->
service.anotherApiCall(value)
.flatMap(next -> service.finalCallBoth(value, next))
)

RxJava - How to make flatmap continue if one of the observables causes an error?

I'm creating an app that takes a list of apps installed on the device and checks for version updates from the google play store.
This is my method to get the app information based on package name:
public Observable<DetailsResponse> getUpdates(#NonNull List<ApplicationInfo> apps) {
return Observable.fromIterable(apps)
.flatMap(appInfo -> googlePlayApiService.getDetails(appInfo.packageName));
}
It works fine if the package is actually on the google play store, but it returns retrofit2.adapter.rxjava2.HttpException: HTTP 404 if the package name is not found (ie: sideloaded app)
This is my method to handle the observables:
updatesViewController.getUpdates(apps)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.as(AutoDispose.autoDisposable(ViewScopeProvider.from(this)))
.subscribe(responseItem -> responseList.add(responseItem),
throwable -> responseList.add(null), //404's here, doesn't do the onComplete at all.
() -> { // onComplete
for (int i = 0; i < apps.size(); ++i) {
if (responseList.get(i) != null && apps.get(i).isLowerVersion(responseList.get(i)) {
doSomething();
}
});
If all the apps are on the playstore, this works as intended. I want to make it so that if one or more of the apps are not found in the playstore, it can still doSomething() on the apps that are found, while ignoring the apps that aren't. Any help is appreciated!
You add null (responseList.add(null)) to the list of responses when you hit one of those 404s (apps not registered on playstore).
Then as logically you are checking and doing something if the version of the app is lower so you can doSomething().
But the check also checks nulls (if (responseList.get(i) != null[...]), therefore you have nulls in the list and those will not doSomething.
Is doSomething dependant on some data in the item? If not, you could do something like:
if(responseList.get(i) == null || (responseList.get(i) != null && apps.get(i).isLowerVersion(responseList.get(i)))
This will call doSomething for all the apps that is lowerVersion OR the missing ones (e.g: ones resulted in 404s)
Remember the assumption above, that the doSomething() doesn't need actual data from this retrieval - otherwise subsequent actions in the doSomething() will fail.
My solution to this problem:
I added a .onErrorResumeNext(Observable.empty()); which effectively skips the items that cause errors.
public Observable<DetailsResponse> getUpdates(#NonNull List<ApplicationInfo> apps) {
return Observable.fromIterable(apps)
.flatMap(appInfo -> googlePlayApiService.getDetails(appInfo.packageName)
.onErrorResumeNext(Observable.empty()));
}
Then in onComplete, instead of looping through all my apps, I only loop through the ones that are in the responseList, which was mostly just a logic error, not an rxJava error.
You can use onErrorResumeNext(..)
public Observable<DetailsResponse> getUpdates(#NonNull List<ApplicationInfo> apps) {
return Observable.fromIterable(apps)
.flatMap(appInfo -> googlePlayApiService.getDetails(appInfo.packageName).OnErrorResumeNext(getFallbackStream()));
}
However, you have to keep in mind that HTTP 404 is not an error, as a successful HTTP call was made unless your HTTP connector maps a 404 to a throwable at some lower level. If not, then you'll have to do something like:
if(404 == appInfo.getResponseCode()) {
return getFallbackStream();
}
inside the flatMap.

Alternatives to the official (and ugly) Sequential composition

In vertx guides, the sequential composition for chaining async calls is shown below:
FileSystem fs = vertx.fileSystem();
Future<Void> startFuture = Future.future();
Future<Void> fut1 = Future.future();
fs.createFile("/foo", fut1.completer());
fut1.compose(v -> {
// When the file is created (fut1), execute this:
Future<Void> fut2 = Future.future();
fs.writeFile("/foo", Buffer.buffer(), fut2.completer());
return fut2;
}).compose(v -> {
// When the file is written (fut2), execute this:
fs.move("/foo", "/bar", startFuture.completer());
},
// mark startFuture it as failed if any step fails.
startFuture);
Is it just me or is this code really cumbersome and hard to read?
There should be another way without falling into the callback hell.
It's a pity there are so few blog posts on vertx, any ideas are much appreciated.
these days the de facto library for writing asynchronous, non-blocking code on the JVM is RxJava. if you're not familiar i'd say it's highly worth your while to take a look as one of the many benefits is the ability to write "flows" as compositional streams that aren't quite as callback hell-y as the JDK's Futures were.
luckily, Vert.x is integrated with RxJava. as an example, here is your snippet rewritten using RxJava artifacts:
#Override
public void start(Future<Void> startFuture) throws Exception {
final FileSystem fs = vertx.fileSystem();
fs.rxCreateFile("/foo")
.andThen(fs.rxWriteFile("/foo", Buffer.buffer()))
.andThen(fs.rxMove("/foo", "/bar"))
.subscribe(
() -> {
startFuture.complete();
},
error -> {
startFuture.fail(error);
}
);
}
much more concise and readable.
note:
use RxJava 2 as it has superceded RxJava 1
...both versions, however, are supported in Vert.x, with their respective artifacts living in separate namespaces:
io.vertx.rxjava for version RxJava 1 artifacts
io.vertx.reactivex for version RxJava 2 artifacts
hope that helps!

No option for ConcatMap with skip error - RxJava

Consider this example:
I have a file downloading in sequence. If one download fails, it should move to next.
Psudo code:
Observable.from(urls)
.concatMap(url -> downloadObservable(url))
There is no option for moving to next url if the download fails.
There is no way to skip with onErrorResumeNext() as I just want to move to next url. Can anyone help?
There is an operator for this: concatMapDelayError since 1.3. In general, if there is a reason errors could be delayed until all sources have been consumed fully, there is likely a opNameDelayError operator for it.
Observable.from(urls)
.concatMapDelayError(url -> downloadObservable(url))
.doOnError(error -> {
if (error instanceof CompositeException) {
System.out.println(((CompositeException)error).getExceptions().size());
} else {
System.out.println(1);
}
});
(The doOnError addendum comes from the updated OP's cross post on the RxJava issue list.)
If you are using RxJava 1, a quick and dirty solution is to return null when the download fails and then filter them out:
Observable
.from(urls)
.concatMap(url -> downloadObservable(url).onErrorReturn(null))
.filter(result -> result != null)
A nicer solution would be to create a wrapper for the result having a method like wasSuccessful() for checking in the filter and a method like getResult() for extracting the result from the wrapper. This way you don't have to handle nulls.
According to: https://github.com/ReactiveX/RxJava/issues/3870 there is no way to do this. Of course you can introduce some other error handling, i.e. handle error inside downloadObservable then filter null answers.
You have to think that is a pipeline so, in case you don't want to stop the emission of the pipeline, you have to control the error and return something in order to continue with the next emission.
The only way to use onErrorResumeNext and not stop the emission after that, is if it´s executed in a flatMap
Observable.from(urls)
.flatMap(url -> downloadObservable(url)
.onErrorResumeNext(t -> Observable.just("Something went wrong"))))
You can see an example here https://github.com/politrons/reactive/blob/master/src/test/java/rx/observables/errors/ObservableExceptions.java

Categories

Resources