I'm Really new on this but What I'm trying to do is
Do two call on parallel both are objects, then I need create a new object using the properties of both
This is a pseudo example that I need
OperatorINeed(service.callOne(), service.callTwo())
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.concatMap(new BiFunction<ObjectA, ObjectB, ObjectC>() {
public ObjectC apply(ObjectA objectA, ObjectB objectB) {
// do things
return objectC;
}
})
onErrorResumeNext(...)
but I can't find any concrete example of this thing
use zip() operator, it will collect both emissions from the two service Observables that will run in parraell, and there you provide the BiFunction to map both Objects properties to a new single Object, afterwards you can apply concatMap() with objectC :
Observable.zip(service.callOne(), service.callTwo(),
new BiFunction<ObjectA, ObjectB, ObjectC>() {
#Override
public ObjectC apply(ObjectA objectA, ObjectB objectB) throws Exception {
// do things
return objectC;
}
})
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.concatMap(objectC -> { //create an Observable using objectC})
.onErrorResumeNext(...)
Related
I have a DAO method like this, which is working fine:
#Query("SELECT name FROM Weather")
Single<List<String>> getCity();
And a method in my activity:
mDatabase.getWeatherDao().getCity()
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.flatMap(new Function<List<String>, SingleSource<String>>() {
#Override
public SingleSource<String> apply(List<String> strings) throws Exception {
return ....;
}
})
.distinct()
next filter and so on.
How can I emit each item from List<String> strings in apply method, so that I can delete repeating (distinct()) items, filter and then use method toList()
You have to flatMap an Observable, like this:
.flatMapObservable(new Function<ArrayList<String>, ObservableSource<? extends String>>() {
#Override
public ObservableSource<? extends String> apply(ArrayList<String> source) throws Exception {
return Observable.fromIterable(source);
}
})
If you can use lambdas and method references you could replace all this ceremony with one of these:
// Lambda version
.flatMapObservable(source -> Observable.fromIterable(source))
// Method reference version
.flatMapObservable(Observable::fromIterable)
You could try something like this as well:
mDatabase.getWeatherDao().getCity()
.toObservable()
.flatMapIterable(r -> r)
.distinct()
.toList()
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(..., ...)
This will work "as is" with strings, for more complex objects you'll need to make sure hashes and equals are working correct (of the entities inside the list).
Here's flow data in my app:
In view I got method onClick were I call presenter.Method(). In this method on presenter I pass the call to model(Model got his own layer of abstracion -> interface modelHelper. It's getting injected via dagger 2 in Conctructor Presenter).
In Model i got method for Network call :
#Override
public void networkCallForData(String request) {
request = "volumes?q=" + request;
compositeDisposable.add(
api.getBook(request)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(
books -> {
items.clear();
items.addAll(books.items);
}
, Throwable::printStackTrace
, () -> {
}
)
);
}
}
I got 2 questions :
1. In MVP architecture should Model layer got injected instance of abstracted presenter and connect it to model just like with view ? if not how should i send data from model to presenter ?
I try connect presenter to model via RxJava2 but got problem with synchronization. In model i create observable from :
private List<Items> items = new ArrayList<>();
and getter method to it :
public Observable<List<Items>> getItemsObservable() {
return itemsObservable;
}
here i create observable :
private Observable<List<Items>> itemsObservable = Observable.fromArray(items);
In presenter i got :
private void getDataFromModel() {
compositeDisposable.add(
findActivityModel.getItemsObservable()
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(
books -> {
view.setRecycler(books);
}, Throwable::printStackTrace
, () -> {
view.setRecyclerVisible();
}
)
);
}
}
When i click on button to search i got first empty response because i observe on list with got not updated yet(Its getting updated via method network call). If I press button 2nd time thats when I got need data from 1 request. How should i chain those 2 RxJava method from different class ?
1. In MVP architecture should Model layer got injected instance of abstracted presenter and connect it to model just like with view?
No. Model layer should not be directly accessing View nor Presentation layer.
Also note that it doesn't make much sense to put .observeOn(AndroidSchedulers.mainThread()) in any of your Model layer implementation.
if not how should i send data from model to presenter ?
Model should just respond to Presenters' queries. It could be a simple function call. Model does not need to hold Presenter instances to handle that.
2. ...How should i chain those 2 RxJava method from different class ?
Consider this implementation:
Model
#Override
public Observable<List<Items>> networkCallForData(String request) {
request = "volumes?q=" + request;
return api.getBook(request);
}
Presenter
// Call this once in the beginning.
private void getDataFromModel() {
compositeDisposable.add(
findActivityModel.networkCallForData(request)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(
books -> {
view.setRecycler(books);
}, Throwable::printStackTrace
, () -> {
view.setRecyclerVisible();
}
)
);
}
Above implementation should be sufficient if you are getting the data only once per screen. But you mentioned something like a refresh button. For that you can make use of BehaviorSubject or BehaviorProcessor.
Presenter
private BehaviorSubject<List<Item>> items =
BehaviorSubject.create(); // You may move this line to the Model layer.
// Call this once in the beginning to setup the recycler view.
private void getDataFromModel() {
// Instead of subscribing to Model, subscribe to BehaviorSubject.
compositeDisposable.add(
items.subscribe(books -> {
// Any change in BehaviorSubject should be notified
view.setRecycler(books);
view.setRecyclerVisible();
});
}
// Trigger this on button clicks
private void refreshData() {
compositeDisposable.add(
findActivityModel.networkCallForData(request)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(books -> {
// Refresh BehaviorSubject
items.onNext(books);
});
}
Even this might not perfectly fit your need but hope you get the idea. Also, few side notes:
Observable.fromArray() returns an observable that emits array items one at a time. Therefore, it is not very useful in this scenario.
It seems you have an object and an observable that wraps the object. If you have these two things in the same place that is usually a sign of a bad design.
private Observable> itemsObservable;
private List items;
This is not the right way to use observable but also violates single source of truth.
One possible way of refactoring this:
private BehaviorSubject<List<Items>> itemsObservable;
// private List<Items> items; // Remove this line
public Observable<List<Items>> getItemsObservable() {
return itemsObservable.hide();
}
// Call this whenever you need to update itemsObservable
private void updateItems(List<Item> newItems) {
itemsObservable.onNext(newItems);
}
I have something like:
private Single<List<Data>> getFirstApiResponse() {
return Single.just(....)
/////
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread());
}
private Single<AnotherData> getSecondApiResponse() {
return Single.just(....)
/////
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread());
}
public void execute() {
//Here I need to run both observables one by one, and show result of each in View
// Code exetuting both
.subscribe(......);
}
How can I run two observables and subscribe on them in last method. In other words, I need to run method execute which will display in UI result of each Observables.
By the way, Observable not connected, they fetch different data (so I can run them asynchronous)
One way to do that is with flatMap:
public void execute() {
getFirstApiResponse()
.flatMap(response1 -> {
// getFirstApiResponse has completed
// ...
return getSecondApiResponse();
})
.subscribe(response2 -> {
// getSecondApiResponse has completed
// ...
}, error -> {
// One of the other operation has failed
});
}
You could look into the zip operator as well, depending on your needs. The downside to this solution is you are forced to combine your responses into a pair or another suitable datastructure, which may not make sense for you.
public void execute() {
Single.zip(getFirstApiResponse(), getSecondApiResponse(),
(first, second) -> {
//now you have both
return Pair.of(first, second);
}).subscribe(pair -> {/**do stuff**/});
}
Hi i have created implementation that uses flatmap to chain two requests together with the final outcome being a response object returned from the second request and wondering if it is possible to mock these two chained response objects?
Here is the main code
delegator.requestOne(requestData)
.flatMap ({ response ->
if(response.isSuccessful){
cookieStorage.saveSessionCookies(response.header(cookieStorage.COOKIE_HEADER_NAME)!!)
}
delegator.requestTwo
})
.observeOn(AndroidSchedulers.mainThread())
.subscribeOn(Schedulers.io())
.subscribe(object : SingleObserver<ResponseTwo>() {
#Override
fun onSubscribe(Disposable d) {
}
#Override
fun onSuccess(responseTwo :ResponseTwo) {
callback.onSuccess(responseTwo)
}
#Override
public void onError(Throwable e) {
}
});
If this did not have a flatmap and handled just one request/response i would write the below using mockito
Mockito.when(network.makeReq()).thenReturn(Single.just(responseOne));
But how can i do something like this:
Mockito.when(foodHygieneController.getLocalAuthorities()).thenReturn(Single.just(requestOne)).thenReturn(requestTwo)??
assuming requestOne and RequestTwo are hard coded mock values of my choosing
You simply mock every request (call to a mocked object) that is part of your Rx chain.
In your case:
Mockito.when(delegator.requestOne(...)).thenReturn(...)
Mockito.when(delegator.requestTwo(...)).thenReturn(...) / Mockito.when(delegator.requestTwo(responseOne)).thenReturn(...)
You can then test that the 'output' (emitted items) from that chain are what you expect them to be, for example with a TestSubscriber, or in your example, that callback is called with the ResponseTwo you expect / have mocked.
The Rx chain will operate in your test exactly as it does when running the code 'normally'.
What you cannot do is mock the behaviour of the Rx chain, e.g. you cannot mock how flatMap{} operates.
I've found nice example of usage RxJava at this article:
Subscription subscription = Single.create(new Single.OnSubscribe() {
#Override
public void call(SingleSubscriber singleSubscriber) {
String value = longRunningOperation();
singleSubscriber.onSuccess(value);
}
})
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new Action1() {
#Override
public void call(String value) {
// onSuccess
Snackbar.make(rootView, value, Snackbar.LENGTH_LONG).show();
}
}, new Action1() {
#Override
public void call(Throwable throwable) {
// handle onError
}
});
But since I am using Retrofit I would like to create RetrofitService and use Single class to combine the result of two requests to backend into one dataset, as described:
When subscribing to a Single, there is only an onSuccess Action and an
onError action. The Single class has a different set of operators than
Observable, with several operators that allow for a mechanism of
converting a Single to an Observable. For example, using the
Single.mergeWith() operator, two or more Singles of the same type can
be merged together to create an Observable, emitting the results of
each Single to one Observable.
Is it possible to achieve this (and how)?
Yes, see Retrofit Adapters
Only worked with Retrofit2.0