public interface ASarTaLineApi {
#FormUrlEncoded
#POST("GetWarDee.php")
Observable<GetWardeeResponse> getWardee(#Field("access_token") String access_token);
#FormUrlEncoded
#POST("GetMealShop.php")
Observable<GetMealShopResponse> getMealShop(#Field("access_token") String access_token);}
How to call parallel with flatMap in RxJava.I want two Object at the same time.Please answer me details and I don't understand kotlin.Thanks.
You can use zipWith for a parallel operation like this:
String token = "hello";
api.getMealShop(token)
.zipWith(api.getWardee(token), new BiFunction<GetMealShopResponse, GetWardeeResponse, Pair<GetMealShopResponse, GetWardeeResponse>>() {
#Override
public Pair<GetMealShopResponse, GetWardeeResponse> apply(GetMealShopResponse getMealShopResponse, GetWardeeResponse getWardeeResponse) throws Exception {
return new Pair<>(getMealShopResponse, getWardeeResponse);
}
});
Or lambda'd:
api.getMealShop(token)
.zipWith(api.getWardee(token), Pair::new);
You get back an Observable<Pair<GetMealShopResponse, GetWardeeResponse>>. If that's what not you want, just put something else in the zipper parameter.
Related
So the scenario is one where we need to make 2 retrofit calls,
#GET("/")
Observable<Search> searchMovies(#Query("apikey") String apiKey, #Query("s") String searchKey);
#GET("/")
Observable<Details> getMovie(#Query("apikey") String apiKey, #Query("t") String searchKey);
the first one is to get a list and then for each item in that list we will make a new call in order to get further information about that movie.
So the first question I have is how can I chain these 2 calls together inside ond rxjava method ?
The second question that i have follows up from this one in that I want to chain these 2 calls together inside and Rxjava method but then return a new observable pojo object based upon a few fields from each of the response. So for example say that request 1 contains "Name" and then request 2 contains "plot". I want to compose what would be a list of MovieInformation pojo objects based on these 2 fields and wrap that into an observable.
You should use flatMap operator to chain these two calls.
Normally getMovie() should take in parameteres the result of searchMovies(). I suppose that apiKey,searchKey are the result of getMovies(apikey,searchKey) :
yourretrofitservice.searchMovies(apikey,searchKey)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
//waiting for response of yourretrofitservice.searchMovies()
.flatMap(new Func1<Search, Observable<Details>>() {
#Override
public Observable<Details> call(Search search_result) {
return yourretrofitservice.searchMovies(apiKey, searchKey); //maybe searchKey = search_result (you should define how to set the result of the First Retrofit call in the second call
}
})
//waiting for the response of yourretrofitservice.getMovie()
.flatMap(new Func1<Search, Observable<YourPojoClass>>() {
#Override
public Observable<YourPojoClass> call(Search search_result) {
//do something with pojo_object
return pojo_object; second call
}
})
.subscribe(new Action1<YourPojoClass>() {
#Override
public void call(YourPojoClass pojo_object) {
//Your final action
}
});
FlatMap operator : http://reactivex.io/documentation/operators/flatmap.html
I receive (Step 1) a soapString from a server and I would like to forward (Step 2) this String to another server.
public interface StepTwoRestAdapter {
#Headers({"Content-Type:text/xml"})
#POST("/services/step2/forwardSoap")
Observable<String> order(#Body SoapString soapString);
}
In this case above, the afgter my.server.com which is "/webservices/step2/forwardSoap" is constant always. How can I make this part variable?
The trick here is, that the second server (for step 2) is specified in the response of the first reponse.
EDIT:
Now, I use the proposal from #Tabish Hussain
public interface StepTwoRestAdapter {
#Headers({"Content-Type:text/xml"})
#POST("/{urlPath}")
Observable<String> order(#Body SoapString soapString, #Path("urlPath") String urlPath);
}
and then I call
restAdapter.create(StepTwoRestAdapter.class).order(new TypedString(soapString), uriPath)
whereas my uriPath is "services/step2/forwardSoap"
But retrofit then calls:
https://my.server.com/services%2Fstep2%2FforwardSoap
As you can see '/' was replaces by "%2F"
Do it like this
public interface StepTwoRestAdapter {
#Headers({"Content-Type:text/xml"})
#POST("/services/{soapString}/forwardSoap")
Observable<String> order(#Path("soapString") SoapString soapString);
}
Check this links, you should be able to find answer there:
https://futurestud.io/tutorials/retrofit-2-how-to-use-dynamic-urls-for-requests
https://futurestud.io/tutorials/retrofit-2-how-to-change-api-base-url-at-runtime-2
I think the easiest way is:
public interface StepTwoRestAdapter {
#Headers({"Content-Type:text/xml"})
#POST
Observable<String> order(#Url String url, #Body SoapString soapString);
}
If you are using Retrofit 1 check this one:
https://medium.com/#kevintcoughlin/dynamic-endpoints-with-retrofit-a1f4229f4a8d#.7atwl0o3t
I have a list of Observables like so:
List<Observable<MyObj>> listObservables = new ArrayList<Observable<MyObj>>();
I'd like to combine all Observable in a single one, I can handle it if I know the number of Observable using zip(), for example we have 3 Observable:
Observable<MyObj1> obs1= MyRestClient.getSomeData1();
Observable<MyObj2> obs2= MyRestClient.getSomeData2();
Observable<MyObj3> obs3= MyRestClient.getSomeData3();
I have a wrapper obj:
class MyWrapperObj {
private MyObj1 onj1;
private MyObj2 onj2;
private MyObj3 onj3;
public MyWrapperObj(MyObj1 onj1, MyObj2 onj2, MyObj3 onj3) {
this.onj1 = onj1;
this.onj2 = onj2;
this.onj3 = onj3;
}
}
So I can combine them like so:
Observable<MyWrapperObj> combinedObservable = Observable.zip(obs1, obs2, obs3, new Func3<MyObj1, MyObj2, MyObj3, MyWrapperObj>() {
#Override
public MyWrapperObj call(MyObj1 obj1, MyObj2 obj2, MyObj3 obj3) {
return new MyWrapperObj(obj1, obj2, obj3);
}
});
combinedObservable.observeOn(AndroidSchedulers.mainThread())
.subscribeOn(Schedulers.io())
.subscribe(new Subscriber<MyWrapperObj>() {
#Override
public void onCompleted() {
}
#Override
public void onError(Throwable throwable) {
}
#Override
public void onNext(MyWrapperObj wrapperObj) {
}
});
Everything is working fine, so my problem is how to organize this combination to be for n Observable?
RESPONSE
as #maciekjanusz mentioned in it's answer I did:
Observable<MyWrapperObj> combinedObservable = Observable.zip(listObservables, new FuncN<MyWrapperObj>() {
#Override
public MyWrapperObjcall(Object... args) {
return null;
}
});
If you want to zip n Observables, put them in a list and apply the public static <R> Observable<R> zip(#NotNull java.lang.Iterable<? extends Observable<?>> ws, rx.functions.FuncN<? extends R> zipFunction) factory method.
List<Observable<String>> observables = Arrays.asList(Observable.just("String 1"), Observable.just("String 2"));
Observable.zip(observables, args -> {
// put your zipping code here
});
For example, if you want to create a list of strings for each emission from all observables:
Observable.zip(observables, Arrays::asList);
Or, if using RxJava on android without retrolambda:
Observable.zip(observables, args -> Arrays.asList(args));
Suppose you have the list:
List<Observable<MyObj>> listObservables
You might consider using Observable.concatDelayError
The advantage if it is finishing all Obbservable's even if any of them finishes with error (resulting in an error in such case).
Remember, that every Observable in this sequence must return the result to onNext method of Subscriber. The result also must have the same type.
Example:
Observable.concatDelayError(listObservables);
You can wait until all observables is complete by using
.zip(observable1, ..., observableN, funcN).first() operators. There is an overload, accepting Observable> argument (as in FlatMap).
First overload takes Iterable> - you can pass list of observables of arbitrary size and second argument - FuncN - receives list of values.
I was wondering about token authentication with Retrofit/RxJava.
I was refactoring my code to use a DataManager, such that the activity evokes a method in the presenter, the presenter subscribes to the datamanager.getTrips which is then responsible for the call to the api.
I want to make sure that my accesstoken is valid, and if it is not generate it first and then complete the task. Would doOnCompleted be a good way of achieving this or is there a better way?
/*In DataManager, service is the REST Interface*/
public Observable<VtTripResponseModel> getTrips(final String start, final String end, final String time, final String date){
if(accessToken.isValid()){
return service.getTrip(accessToken.getAccessString(),start,end,time,date,JSON_PARAM);
}
else{
generateAccessToken().doOnCompleted(new Action0() {
#Override
public void call() {
getTrips(start, end, time, date);
}
});
}
/*What do I return here? Since this will not be reached*/
}
To elaborate on #david.mihola 's answear you could do it like this:
Observable.just(accessToken.isValid())
.flatMap(valid -> {
if(valid){
return Observable.just(accessToken);
} else {
return generateAccessToken();
})
.flatMap(token -> service.getTrip(token.getAccessString(),start,end,time,date,JSON_PARAM))
So that the first flatMap generates token if it is not valid and if it is, then simply passes it on(this assumes that generateToken() returns Observable<AccessToken>). Second flatMap is just the thing that you wanted to do.
And to give some context to #MatBos's elaboration on my comment, especially with regard to your question
What do I return here? Since this will not be reached
It felt quite eye-opening for me when I realized that an Observable (or at least a cold-one, like the one we are talking about here, and the one that #MatBos described in his answer) is essentially a description of a future computation.
Returning an Observable just means that you return a blue-print for what should happen if and when someone (i. e. the code that called your getTrips method) actually subscribes to that Observable. Of course, an Observable is also an asynchronous stream of events, but I think that my description here is valid, too.
So, what do you return? A description that says:
If someone subscribes
1. First check if we have valid access token
2. a) If we do, just forward the access token for later use
b) If we don't, generate a new one access token and forward that
3. Take whatever access token you get - it is now guaranteed to be valid and use to retrieve the trips.
4. Forward them to the subscriber when they are ready.
And that description is exactly the Observable that #MatBos described.
Thank you for the input, In the meantime I was flying away and found a similar, but formulated in another way post: Retrofit with RxJava which had an answer in it.
My code now looks like:
/*In DataManager*/
public Observable<VtTripResponseModel> getTrips(String start, String end, String time, String date){
return service.getTrip(accessToken.getAccessString(),start,end,time,date,JSON_PARAM);
}
public Observable<VtResponseModel> getLocationByInput(String input){
return service.getLocationByInput(accessToken.getAccessString(),input,JSON_PARAM);
}
/*SF 26201420*/
public <T> Func1<Throwable,? extends Observable<? extends T>> refreshTokenAndRetry(final Observable<T> toBeResumed) {
return new Func1<Throwable, Observable<? extends T>>() {
#Override
public Observable<? extends T> call(Throwable throwable) {
// Here check if the error thrown really is a 401
if (isHttp401(throwable)) {
return service.getAccessToken(CREDENTIALS, DEVICE).flatMap(new Func1<AccessToken, Observable<? extends T>>() {
#Override
public Observable<? extends T> call(AccessToken token) {
accessToken = token;
return toBeResumed;
}
});
}
// re-throw this error because it's not recoverable from here
return Observable.error(throwable);
}
};
}
And the method in my presenter now looks like
public void loadRepositories(String search){
subscription = manager.getLocationByInput(search)
.onErrorResumeNext(manager.refreshTokenAndRetry(Observable.defer(() -> manager.getLocationByInput(search))))
.observeOn(AndroidSchedulers.mainThread())
.subscribeOn(application.defaultSubscribeScheduler())
.subscribe(new Subscriber<VtResponseModel>() {... etc}
Now when the first call is made after starting the application, it will generate an accesstoken since I recieve a 401. Then that accesstoken is stored within the manager, and reused until there is a new 401 (after it has expired).
I am using RXJava on Android for asynchronously access the database.
I want to save an object in my database.
In this way, I created a method which take a final parameter (the object I want to save) and returns an Observable.
At this point I don't care to emit anything so I will call subscriber.onComplete() at the end.
Here is my code:
public Observable saveEventLog(#NonNull final EventLog eventLog) {
return Observable.create(new Observable.OnSubscribe<Object>() {
#Override
public void call(Subscriber<? super Object> subscriber) {
DBEventLog log = new DBEventLog(eventLog);
log.save();
subscriber.onCompleted();
}
});
}
The thing is, I saw many answer using the final keyword for the parameter, but I would like to do this without it.
The reason is I don't really like the approach of declare a final variable in order to use it in another thread.
Is there any alternative? Thanks.
We usually suggest avoiding the use of create because it may seem simple to use it but they usually violate the advanced requirements of RxJava. Instead, you should use one of the factory methods of Observable. In your case, the just factory method will get what you wanted: no final parameter:
public Observable<?> saveEventLog(#NonNull EventLog eventLog) {
return Observable
.just(eventLog)
.doOnNext(e -> {
DBEventLog log = new DBEventLog(e);
log.save();
})
.ignoreElements();
}