I'm using Retrofit for my Rest API operation. Now my interface have 2 methods, 1 for Rest API call that I have declared using Retrofit syntax as usual and another one is just do a simple calculation so it doesn't need to make any Rest API call, as the following (just a quick examples):
public interface ITripService
{
int getPrice(LatLng start, LatLng end, String serviceType);
#GET("driver/nearbydrivers")
Call<List<NearbyDriver>> getNearbyDrivers(#Query("latitude")String latitude,
#Query("longitude")String longitude,
#Query("administrative_area_level_1")String administrative_area_level_1,
#Query("administrative_area_level_2")String administrative_area_level_2,
#Query("serviceType")String serviceType);
}
So now in my app I can call the Rest-method-version as follow:
ServiceFactory.getTripService().getNearbyDrivers(null, null, null, null, null).enqueue(new Callback<List<NearbyDriver>>()
{
#Override
public void onResponse(Call<List<NearbyDriver>> call, Response<List<NearbyDriver>> response)
{
}
#Override
public void onFailure(Call<List<NearbyDriver>> call, Throwable t)
{
}
});
Retrofit will do the rest for me, like network call, get API response,...
But with getPrice I need to do some custom operations inside. How can I do it?
Any ideal would be appreciate!
Related
TL;DR: I want to execute multiple Calls (Retrofit) like you can .zip() multiple Observables (RxJava2).
I have a retrofit2 function:
#GET("/data/price")
Call<JsonObject> getBookTitle(#Query("id") String id, #Query("lang") String lang);
I can execute it (async) in code with enquene():
ApiProvider.getBooksAPI().getBookTitle(bookId, "en").enqueue(new Callback<JsonObject>() {
#Override
public void onResponse(Call<JsonObject> call, Response<JsonObject> response) { }
#Override
public void onFailure(Call<JsonObject> call, Throwable t) { }
});
Now I want to execute multiple Calls at once (get multiple book titles) and be notified when all requests are done. Here is when I am missing knowledge.
I know I could start using Observable (RXJava2) instead of Call (Retrofit2):
#GET("/data/price")
Observable<JsonObject> getBookTitle(#Query("id") String id, #Query("lang") String lang);
and then merge calls like in below example. But this code seems much more complex and long (especially if I only need 1 book title). Isn't there any way I could merge Calls without using Observable?
List<Observable<JsonObject>> mergedCalls = new ArrayList<>();
mergedCalls.add(ApiProvider.getBooksAPI().getBookTitle(bookId1, "en"));
mergedCalls.add(ApiProvider.getBooksAPI().getBookTitle(bookId2, "en"));
mergedCalls.add(ApiProvider.getBooksAPI().getBookTitle(bookId3, "en"));
Observable<List<JsonObject>> observable = Observable.zip(calls, responses -> {
// merge responses, return List
...
})
.observeOn(AndroidSchedulers.mainThread())
.subscribeOn(Schedulers.io());
observer = new DisposableObserver<List<JsonObject>> () {
#Override
public void onNext(List<JsonObject> result) { // got all API results }
#Override
public void onError(Throwable e) { }
#Override
public void onComplete() { }
};
observable.subscribe(observer);
Using RxJava is the easy way of merging Retrofit Calls. Merging Calls manually by enqueuing all Calls and doing something when all of them invoke onResponse, will probably be more complex than simply using Observable.zip(...).
The other choice that you have is using Kotlin coroutines (now Retrofit has out of the box support for them). But that depends on the Kotlin presence in your code and your willingness of using coroutines.
EDIT:
(Answering your question from the comment)
If you really think about Calls and RxJava Observables you don't really have to do anything more when using RxJava. When using raw Calls you still have to:
Make sure you're on the right thread if you want to touch Views (observeOn(AndroidSchedulers.mainThread()))
Make sure you're touching network on the right thread (subscribeOn(Schedulers.io()))
Make sure you're not using the response when your Activity/Fragment/Something else is no longer present (disposing of the Disposable in RxJava handles that)
You can significantly simplify your example:
Don't create Observable & Observer. Simply use the subscribe method which returns Disposable. And then maintain just this one Disposable.
You probably don't need onComplete so you can use the simpler version of .subscribe(...)
You can remove the need for .subscribeOn(Schedulers.io()) by properly creating your RxJavaCallAdapterFactory.createWithScheduler(Schedulers.io()) when building the Retrofit instance.
BooksApi booksApi = ApiProvider.getBooksAPI();
List<Observable<JsonObject>> mergedCalls = new ArrayList<>();
mergedCalls.add(booksApi.getBookTitle(bookId1, "en"));
mergedCalls.add(booksApi.getBookTitle(bookId2, "en"));
mergedCalls.add(booksApi.getBookTitle(bookId3, "en"));
final Disposable disposable = Observable
.zip(mergedCalls, responses -> {
// merge responses, return List
})
.observeOn(AndroidSchedulers.mainThread())
.subscribe(list -> {
// got all API results
}, throwable -> {
});
Doing that for one call would be as simple as:
final Disposable disposable = booksApi
.getBookTitle(bookId1, "en")
.observeOn(AndroidSchedulers.mainThread())
.subscribe(title -> {
// got the result
}, throwable -> {
});
I know this kind of a weird question but I am trying to use my Retrofit call inside a for loop. What I am doing is sending my String[] elements one by one in the call with func like insertdata(seperated2[0], seperated2[1], email, tag);
But the loop are behaving weirdly when they are skipping the anonymous call for call.enqueue(......onResponse(...) onfailure(.....))
Instead of calling it with the loop control first finishes the loop and then comes to call.enqueue and always last element in loop. This is how loop looks like ....
separated = currentString.split("\n");
for (int i=1; i<separated.length; i++) {
seperated2 = separated[i].split(":");
for (String aSeperated2 : seperated2) {
Call<ServerResponse2> call = requestInterface.insertQrdata(seperated2[0], seperated2[1], email, tag);
call.enqueue(new Callback<ServerResponse2>() {
#Override
public void onResponse(Call<ServerResponse2> call, Response<ServerResponse2> response) {
ServerResponse2 serverResponse2 = response.body();
Toast.makeText(getActivity(), serverResponse2 != null ? serverResponse2.getMessage() : null, Toast.LENGTH_SHORT).show();
}
#Override
public void onFailure(Call<ServerResponse2> call, Throwable t) {
Toast.makeText(getActivity(), t.getLocalizedMessage(), Toast.LENGTH_SHORT).show();
}
});
}
}
here is an ex for seperated[] and seperated2[]
0 1
2 3
4 5
6 7
7 8
9 10
seperated[] is spliting them by line and seperated2 is spliting them by column.
The Problem
When I check my seperated2[0] and seperated2[1] value for each iteration in on Response method it should be
sep2[0]= 0 sep2[1] = 1
2 3
and so on... for each iteration
but in each iteration the value in onResponse is always the last, i.e.
sep2[0] = 9 sep2[1] = 10
untill the length (say 6) same value at each iteration.
I don't know if am doing anything wrong but values are showing correctly when i use them outside of onResponse().
I know using retrofit like is not good practice but I was curious to how it will react in this situation. Can anyone help or give any suggestions ?
THANKS IN ADVANCE !!
Here is an example of looping with retrofit library..if we use for loop the call.enqueue will not be called instantly for all iterations.so use this model.
private void UploadImagesTask(final String image, final int position) {
ApiInterface apiService = ApiClient.getClient().create(ApiInterface.class);
File file = new File(image);
RequestBody reqFile = RequestBody.create(MediaType.parse("image/*"), file);
MultipartBody.Part photoArray = MultipartBody.Part.createFormData("photo", file.getName(), reqFile);
HashMap<String, RequestBody> params = new HashMap<>();
params.put("token", Utils.createPartFromString(pref.getToken()));
params.put("app_id", Utils.createPartFromString(pref.getAppId()));
Call<ImageUploadResponse> imageUploadResponseCall = apiService.uploadImage(photoArray, params);
imageUploadResponseCall.enqueue(new Callback<ImageUploadResponse>() {
#Override
public void onResponse(#NonNull Call<ImageUploadResponse> call, #NonNull Response<ImageUploadResponse> response) {
if (response.isSuccessful()) {
urlList.add(Objects.requireNonNull(response.body()).getUrl());
completeData.remove(position);
completeData.add(position, getString(R.string.uploaded));
uploadAdapter.notifyDataSetChanged();
pref.setTempData(Constants.IMAGE_UPLOADED, gson.toJson(urlList));
if (position != uriData.size() - 1) {
int posi = position + 1;
CompressImages(posi);
} else {
uploadDone.setActivated(true);
uploadDone.setEnabled(true);
}
} else {
Utils.showSnackView(getString(R.string.error_occurred), snackView);
uploadDone.setActivated(true);
uploadDone.setEnabled(true);
}
}
#Override
public void onFailure(#NonNull Call<ImageUploadResponse> call, #NonNull Throwable t) {
Utils.showSnackView(getString(R.string.error_occurred), snackView);
uploadDone.setActivated(true);
uploadDone.setEnabled(true);
}
});
}
Instead of using for loop, you can use recursion too.
API will be called again and again but only after getting the response of the previous index.
In the onResponse() method you can call the method by incrementing the index value.
Because in for loop, the iteration will not wait for the completion of the execution of your API response, it will jump to the next iteration.
If you still want to use the loop then go for while loop
example for recursion:
int i=1;
separated = currentString.split("\n");
void callMethod(){
seperated2 = separated[i].split(":");
Call<ServerResponse2> call = requestInterface.insertQrdata(seperated2[0], seperated2[1], email, tag);
call.enqueue(new Callback<ServerResponse2>() {
#Override
public void onResponse(Call<ServerResponse2> call, Response<ServerResponse2> response) {
ServerResponse2 serverResponse2 = response.body();
Toast.makeText(getActivity(), serverResponse2 != null ? serverResponse2.getMessage() : null, Toast.LENGTH_SHORT).show();
i++;
callMethod();
}
#Override
public void onFailure(Call<ServerResponse2> call, Throwable t) {
Toast.makeText(getActivity(), t.getLocalizedMessage(), Toast.LENGTH_SHORT).show();
}
});
As per My suggestion do not use loops for this types of operation. Its bad idea If you can see performance wise.
You can do this things by following way:
Retrofit is just call server URL and passing your data to the server.
So whatever process doing server side that you can change to do once only rather than doing everytime.
You can pass whole loop data in json format to the server once and do all looping process in server side. It will be best for you.
Hope you clear my point of view.
Do let me know if you have any problem.
Hey if you are yet never found the answer I have a suggestion for you. Divide the whole response into parts after that apply loop on that data and send data using Asynctask to server and create one interface so you can do anything on a response back.
public interface GetResponse{
public void onSuccess(ResponseModel model);
publich void onFail();
}
In your async task
public class AsyncTaskForApiSync extends AsyncTask<Void, Void, Void> {
GetResponse mcallback;
public AsyncTaskForApiSync(GetResponse mcallBack){
this.mcallback=mcallBack;
}
#Override
protected Void doInBackground(Void... voids) {
//retrofit api call in on success
mcallback.onSuccess(response);
}
}
Sorry for my English. Also, let me know if you find any issue. Thanks.
I need to call 3 different api ...each api takes input of another api output in sequence..
eg : API1 -> output -> will be input for API2
API2 -> output -> will be input for API3
In my case , Spinner contain API1 ....On spinner selection i need to call API2 and so on
Currently i am writing a separate code for each API and call them using Observer...but i want to call APIs in sequence using RxJava,RxKotlin and Retrofit flatmap concepts.So is there any way using that, i can call this three APIs in sequence, without writing each of them seperatly
Added snippets from links shared by Surinder
public Single<List<Restaurant>> getRestaurants(int userId) {
return ddApi.getUserInfo(userId).flapMap(user -> {
return ddApi.getAvailableRestaurants(user.defaultAddress.lat,
user.defaultAddress.lng);
});
}
public class RestaurantFragment {
private CompositeDisposables disposables = new CompositeDisposables();
private RestaurantDataSource restaurantDataSource;
#Override
public void onResume() {
// subscribe to the Single returned by RestaurantApi
restaurantDataSource
.getRestaurants(userId)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new SingleObserver<Restaurant>() {
#Override
public void onSubscribe(Disposable d) {
disposables.add(d);
}
#Override
public void onSuccess(List<Restaurant> restaurants) {
// update the adapter with restaurants
}
#Override
public void onError(Throwable e) {
// display an error message
}
});
}
#Override
public void onPause() {
disposables.clear();
}
}
you can follow below link for chaining of API calling using flatMap
if using Kotlin then follow
Chaining Multiple Retrofit Call Using RxJava/ Kotlin
Here is my current code. The problem with this code is I need to wait get the data sequentially. The loading time is poor because of this. I want to use something like .enqueue() to get asynchronously several data at once, but I want to wait until I get all the data before continuing the process. Is it possible to do it with Retrofit?
List<Data> datas = new ArrayList<>();
for (long dataId : mDataIds) {
Response<T> response = resource.getData(dataId).execute();
if (response.isSuccessful()) {
datas.add(data.body());
}
}
//do something else
You can solve this problem very elegantly using RxJava.
If you never heard of RxJava before, it is a solution to many of your problems.
If you don't use java8 or retrolambda I recommend you to start using it, as it makes working with RxJava a piece of cake.
Anyway here's what you need to do:
// 1. Stream each value from mDataIds
Observable.from(mDataIds)
// 2. Create a network request for each of the data ids
.flatMap(dataId -> resource.getData(dataId))
// 3. Collect responses to list
.toList()
// Your data is ready
.subscribe(datas -> {}, throwable -> {});
1) First add RxJava2 dependencies to your project
2) Define retrofit api interface methods which return RxJava observable types
public interface DataApi {
#GET("dataById/")
Observable<Data> getData(#Query("id") String id);
}
3) Call api passing input data like below.
Observable.fromIterable(idList).subscribeOn(Schedulers.computation())
.flatMap(id -> {
return retrofitService.getData(id).subscribeOn(Schedulers.io());
}).toList().
.observeOn(AndroidSchedulers.mainThread()).subscribe( listOfData -> {// do further processing }, error -> { //print errors} );
For reference : http://www.zoftino.com/retrofit-rxjava-android-example
Define interface with callback Model type.
public interface LoginService {
#GET("/login")
Call<List<Login>> getLogin();
}
In you calling method override the callback method.
LoginService loginService = ServiceGenerator.createService(LoginService.class);
Call<List<Login>> call = loginService.getLogin();
call.enqueue(new Callback<List<Login>>() {
#Override
public void onResponse(Call<List<Login>> call, Response<List<Login>> response) {
if (response.isSuccessful()) {
// Login successful
} else {
// error response, no access to resource?
}
}
#Override
public void onFailure(Call<List<Login>> call, Throwable t) {
// something went completely south (like no internet connection)
Log.d("Error", t.getMessage());
}
}
I would recommend using RxJava and try it. You have something called FlatMap to combine the results.
To Start here is the tutorial start for RxJava2 and Retrofit2.
I am trying to do a GET request to a json file:
https://www.someurl.com/appconfiguration.json
So I created an Interface with the following GET method
#GET("appconfiguration.json}")
Call<AppConfigParent> loadAppConfigParent();
and call it like this:
final MMSATServices.AppConfigResponse appConfigResponse = new MMSATServices.AppConfigResponse();
appConfigResponse.appConfigParent = new AppConfigParent();
appConfigResponse.appConfigParent.configuration = null;
Call<AppConfigParent> call = api.loadAppConfigParent();
call.enqueue(new Callback<AppConfigParent>() {
#Override
public void onResponse(Call<AppConfigParent> call, Response<AppConfigParent> response) {
appConfigResponse.appConfigParent.configuration = response.body().configuration;
bus.post(appConfigResponse);
}
#Override
public void onFailure(Call<AppConfigParent> call, Throwable t) {
}
});
note that the api object is the instance of the interface, whichis defined in the super class.
The actual problem is that I get a 404 repsonse:
Request{method=GET, url=https://someurl.com/appconfiguration.json%7D, tag=Request{method=GET, url=https://someurl.com/appconfiguration.json%7D, tag=null}}
As you can see %7D is appended to the URL, which leads to the 404 error. How can I get rid of this behavior?
Remove } in #GET("appconfiguration.json}")