I'm using a Builder pattern to build upon a model object which combines data from different network calls and I'm having a hard time understanding the best way to take the model object from the first network call and combine the data from the second network call into the original model object.
My actual subscription:
myFirstApiRepository.getFirstModelObjectBuilder()
.flatmap(firstModelObjectBuilder -> mySecondApiRepository.getSomeExtraData(firstModelObjectBuilder))
.observable.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(getMySubscriber());
First network call:
public Observable<FirstModelObject.Builder> getFirstModelObjectBuilder() {
return myFirstApiResource.getSomeData(...)
.flatMap(someData -> Observable.just(new FirstModelObject.Builder()
.setFirstAttribute(someData.getFirstAttribute())
.setSecondAttribute(someData.getSecondAttribute())));
}
Second network call:
public Observable<FirstModelObject> getSomeExtraData(FirstModelObject.Builder builder) {
return mySecondApiResource.getSomeData(...)
.flatMap(aString -> builder.setSomeStringValue(aString)
.build());
}
The problem here is that I have to pass the builder object into the second network call's observable. This makes it very rigid and I'd rather not have my SecondApiRepository rely on and reference a data type which it shouldn't need to reference. It also makes this second method here ".build()" the object, which is not good. So, how can I use the firstModelObject and add data to it from the second network call in a clean way?
If this is just bad design, please let me know. I'm still trying to learn more about RxJava best practices. :)
If your second request relies on first request's result, then check my answer - https://stackoverflow.com/a/41820372/7045114.
If not - just use zip operator:
Combines the emissions of multiple Observables together via a specified function and emit single items for each combination based on the results of this function.
Observable.zip(firstRequest, secondRequest, (firstResult, secondResult) -> {
//process results
})
Related
I still do not understand when to apply this method. In fact, it is similar to Mono.just, but I heard that callback is used for heavy operations if it needs to be performed separately from other flows. Now I use it like this, but is it correct.
Here is an example of use, I wrap sending a firebase notification in a callback since the operation is long
#Override
public Mono<NotificationDto> sendMessageAllDevice(NotificationDto notification) {
return Mono.fromCallable(() -> fcmProvider.sendPublicMessage(notification))
.thenReturn(notification);
}
maybe I still had to wrap up here in Mono.just ?
It depends which thread you want fcmProvider.sendPublicMessage(...) to be run on.
Either the one currently executing sendMessageAllDevice(...):
T result = fcmProvider.sendPublicMessage(notification);
return Mono.just(result);
Or the one(s) the underlying mono relies on:
Callable<T> callable = () -> fcmProvider.sendPublicMessage(notification);
return Mono.fromCallable(callable);
I would guess you need the latter approach.
If you use Mono.just(computeX()), computeX() is called immediately. No want you want(I guess).
If you use Mono.fromCallable(() -> computeX()), the computation is still not performed. I mean computeX() is only called when you subscribe to it. Maybe using .map, .flatMap, etc.
Important: if computeX() return Mono you doe not need to use Mono.fromCallable. It's only for blocking code
As you explained in the description, Mono.fromCallable is used when you want to compute a result with an async execution (mostly some heavy operation).
Since, you have already generated the Mono with Mono.fromCallable you do not have to wrap it again with Mono.just.
I am not able to process Mono<List<Object>> to List<Object>. I have read somewhere that flatmap can be used, but I am not able to do that either. I don't want to use .block() method for this scenario.
A code example could be:
PagePublisher<Address> someAddressPage = someService.getPagePublisherForAddress();
Mono<List<Address>> addressListMono =
Flux.from(someAddressPage.items()).collectList();
I need to get List<Address> from Mono<List<Address>>. The method return type is List<Address>.
I am not sure how to go about that and I am relatively new to reactive. Need help in that regard.
Using block() is actually the only way to get the object from a Mono when it is emitted. However, you should avoid it at all costs because it defeats the purpose of using the reactive stack given that it actually blocks the thread waiting for the object to be emitted from the Mono.
This means that while working with reactive programming you should actually work with Mono and Flux so that your application is really reactive. The consequence of this is that you must change the return type of your method to return Mono<List<Address>> instead.
Its impossible to get List<Address> from Mono<List<Address>>. If the List isn't available yet since Mono and Flux are asynchronous/non-blocking by their nature, you can't get it except by waiting until it comes in and that's what blocking is.
Ideally, your method should return Mono<List<Address>> or just Flux<Address> and the caller of this method should subscribe to the results. Since you are using Spring Webflux, complete chain should be reactive.
You can subscribe to the Mono from the calling method and the Subscriber you pass will get the List when it becomes available. E.g.
addressListMono.subscribe(
addressList -> doSomething(addressList),
error -> error.printStackTrace(),
() -> Console.out.println("completed without a value")
)
I have a retrofit api call that returns a javarx observable. Here is the retrofit interface
#GET("/myobjects")
Observable<List<MyObject>> getMyObjectsObservable();
Then I have a repository method:
public Observable<List<MyObject>> getMyObjectsObservable(){
return api.getMyObjectsObservable();
}
In my viewmodel, I have a method that subscribes to the javarx observable returned by the repository. Inside the method, the observable chain in which I subscribe in that method sets values for two MutableLiveData objects that are observed by my activity. One MutableLiveData, "succeededLiveData", holds a boolean which determines if the API call succeeded and displays this for the user in the activity, and the other "myObjectStringsListLiveData" is a list of strings with information from a MyObject object that will be displayed in a listview in the activity. Here is that observable chain:
Disposable disposable = myObjectsRepository.getMyObjectsObservable()
.concatMapIterable(myObject -> myObject)
.map(myObject -> myObject.getString1() + " And " + myObject.getString2())
.toList()
.doOnError(throwable -> {succeededLiveData.postValue(false);
myObjectStringsListLiveData.postValue(null);})
.retry(4)
.observeOn(AndroidSchedulers.mainThread())
.subscribe(myObjectStringsList -> {
succeededLiveData.setValue(true);
myObjectStringsListLiveData.setValue(myObjectStringsList);
},
throwable -> {
succeededLiveData.setValue(false);
myObjectStringsListLiveData.setValue(null);});
Now I realized that I actually need the original list of my MyObjects from the observable to be stored in List<MyObject> myObjectsList which is a field of my viewmodel, not just the List<String> that is stored in my LiveData "myObjectStringsListLiveData". This means that I need a way to extract the myObjectsList from the observable before all the mapping. I can't observe the myObjectsObservable twice, once before the mapping, and then one mapped like my code above, because that would cause two API calls which I obviously do not want. At least it is my understanding that that would happen, as observables execute the code separately for each subscriber.
So I can only personally think of two possible solutions as I am still a javarx noob. One would be to use a type of Subject that subscribes to the retrofit myObjectsObservable so that the API wouldn't be called twice and then subsequently subscribe to that subject twice in my viewmodel. Although I am not quite sure how to implement this, but I think I could figure it out.
The other option I can think of would be to subscribe only once, and inside the lambda I pass the subscribe() method, store the List<MyObject emitted by the Observable, and then instead of using the JavaRx map/concatMapIterable/toList() operators, use the java Stream library inside the lambda as well, and use that to map the myObjectsList to List<String> that will be posted to the myObjectStringsListLiveData. A downside to this would that this would occur on the main thread I believe. Although maybe I could change the "setValue" methods to "postValue" methods and observeOn a background thread. I am not really sure if that is okay to use the viewmodel in the background like that.
The second solution doesn't seem too elegant. Is it wise to do stuff with a viewmodel in the background? Maybe the first way with a Subject is better?
What do you all think is the best way?
Also, as a bonus question, that is somewhat unrelated to the first question, I was wondering if postValue in MutableLiveData will always execute in order, like how I do so in my lambdas in the observable chain, because I will get a NullPointerException in my activity if succeededLiveData.getValue() is called after OnChanged is called for the MyObjectStringsList that I observe in my activity. Will postValue always propagate to each respective LiveData observer in the order they are called?
Thanks for any responses!
I'm using RxJava, Room and Retrofit to accomplish Repository pattern in Android. Everything is working except this one thing. I have implemented SearchView for searching data. Now when search view text changes I call search method. here is how it looks like
mLocal.searchMovies(query) //returns Single
mRemote.searchMovies(query) //returns Single
public Single<List<MovieEntity>> search(String query) {
return mLocal.searchMovies(query)
.toMaybe()
.switchIfEmpty(mRemote.searchMovies(query)
.map(data -> {
mLocal.saveAll(data);
return data;
}));
}
mLocal queries the room database and mRemote calls retrofit to fetch data from REST API. The problem is, this only calls mLocal and when room returns empty row network call is not initiated. I have tried everything I possibly and read many articles but I cannot understand how to get it work.
Each of mLocal and mRemote works fine. Only chaining them does not accomplish what I want.
Your chain does not work because .toMaybe() from Single cannot produce Maybe.empty(), which is the result required to have .switchIfEmpty take effect.
You will either need to replace it with Single.onErrorResume so the error condition from a missing entry results in the remote query, or make it return Maybe to accurately represent the tristate of <Result,NotCached,Error>.
The explanation of my problem is the same as in this answer. I will copy relevant part and post my working piece of code after applying it.
Note: I changed to Flowable from Single/MayBe. am not sure how much it affects the original code, hadn't I changed the type of Observables.
Based on the OP's feedback, the allDatas source was infinite but
returned an empty List. The resolution was to apply at least take(1)
to get exactly one response from allDatas and then optionally filter
out the empty List so switchIfEmpty can switch over to the
alternatives
public Flowable<List<MovieEntity>> getMovies() {
return mLocal.allMovies().take(1)
.filter(list -> !list.isEmpty())
.switchIfEmpty(mRemote.allMovies()
.doOnNext(data -> mLocal.saveAll(data))
);
}
In Reactive Java, we're told that the .subscribe() call returns "a Subscription reference". But Subscription is an interface, not a class. So what kind of object are we handed that implements this interface? Do we have any control over this?
There is the class Subscriptions that can create and return several different kinds of Subscription, but what does one do with them? If I write
Subscription mSub = Subscriptions.create(<some Action0>);
mSub = someObservable.subscribe();
won't my just-created Subscription simply be overwritten by whatever the .subscribe() call returns? How do you use a Subscription you create?
(On a somewhat related note, what is the point of Subscriptions.unsubscribed(), which "returns a Subscription to which unsubscribe does nothing, as it is already unsubscribed. Huh?)
Short answer: You shouldn't care.
Longer answer: a subscription gives you two methods:
unsubscribe(), which causes the subscription to terminate.
isUnsubscribed(), which checks whether that has already happened.
You can use these methods to a) check whether an Observable chain terminated and b) to cause it to terminate prematurely, for example if the user switched to a different Activity.
That's it. You aren't exposed to the internals on purpose. Also, do you notice that there's no resubscribe method? That's because if you want to restart the operation, you need to resubscribe to the Observable, giving you a new Subscription.
As you know Subscriptions are used to keep references to ongoing Observables, mainly for resources' management. For example in Android applications, when you change an Activity (screen) you flush old Activity Observables. In this scenario, Subscription instances are given by .subscribe() (as you mentioned) and stored. So, for which reason would one create a Subscription directly, especially Subscriptions.unsubscribed()? I encountered two cases:
Default implementation; avoid declaration like Subscription mSub; that would be filled latter and could create an NPE. It's especially true if you use Kotlin that require property initialization.
Testing
On a somewhat related note, what is the point of Subscriptions.unsubscribed(), which "returns a Subscription to which unsubscribe does nothing, as it is already unsubscribed. Huh?
In 1.x, Subscriptions.unsubscribed() is used to return a Subscription instance the operation was completed (or never run in the first place) when the control is returned to your code from RxJava. Since being unsubscribed is stateless and a constant state, the returned Subscription is a singleton because just by looking at the interface Subscription there is no (reasonable) way to distinguish one completed/unsubscribed Subscription from another.
In 2.x, there is a public and internal version of its equivalent interface, Disposable. The internal version is employed mostly to swap out a live Disposable with a terminated one, avoiding NullPointerException and null checks in general and to help the GC somewhat.
what does one do with them?
Usually you don't need to worry about Subscriptions.create(); it is provided for the case you have a resource you'd like to attach to the lifecycle of your end-subscriber:
FileReader file = new FileReader ("file.txt");
readLines(file)
.map(line -> line.length())
.reduce(0, (a, b) -> a + b)
.subscribe(new Subscriber<Integer>() {
{
add(Subscriptions.create(() -> {
Closeables.closeSilently(file); // utility from Guava
});
}
#Override public void onNext(Integer) {
// process
}
// onError(), onCompleted()
});
This example, demonstrating one way of usage, can be expressed via using instead nonetheless:
Observable.using(
() -> new FileReader("file.txt"), // + try { } catch { }
file -> readLines(file).map(...).reduce(...),
file -> Closeables.closeSilently(file)
)
.subscribe(...)