Android Retrofit appends unwanted characters - android

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}")

Related

Making multiple asynchronous request using Retrofit

In my android application I have a screen where I have 3 spinners that need to be
filled from APIs call.
static List<TripCode> tripCodeList = new ArrayList<>();
static List<Fleet> truckList = new ArrayList<>();
static List<Trailer> trailerList = new ArrayList<>();
And I don't want to inflate the layout unless I get the response from all the 3 different API calls so this is what I'm doing
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
context = this;
if (MyApplication.isConnected()) {
getTripCodes();
} else {
Toast.makeText(this, "No internet Connection", Toast.LENGTH_LONG).show();
setContentView(R.layout.no_internet_connection);
}
}
Basically , I removed setContentView(R.layout.activity_create_trip);
from onCreate() And I called getTripCodes()
here's the code for getTripCodes()
public void getTripCodes() {
MyApplication.showProgressDialog(getString(R.string.please_wait), this);
IMyAPI iMyAPI = MyApplication.getIMyAPI();
Call<List<TripCode>> call = iMyAPI.getTripCodes();
call.enqueue(new Callback<List<TripCode>>() {
#Override
public void onResponse(Call<List<TripCode>> call, Response<List<TripCode>> response) {
if (response.isSuccessful() && response.body() != null) {
tripCodeList = response.body();
Log.d("test", "getTripCodes success = " + tripCodeList.size());
getTrucks();
} else {
MyApplication.dismissProgressDialog();
}
}
#Override
public void onFailure(Call<List<TripCode>> call, Throwable t) {
MyApplication.dismissProgressDialog();
}
});
}
So in the success of the call I'm calling the other function getTrucks() which also get result from API and in the success it will call getTrailers()
But I think it's a waste of time, because I can call the three function all together in parallel, and then check if all the list are filled or not.
But I don't know how to do it. How can I check if all the calls are success? And if one of them has failed, how will I know which one exactly failed?
I Believe for your problem you can easily use Retrofit 2.6.0 which has coroutine support and you can declare all the function's as suspended function's and dispatch them with async/launch dispatcher and if you want to wait for some result in some case use await() to wait for the result.
And use RxJava/liveData for responsive UI
sample code for you will look like
//maybe from Activity for ViewModel you can use ViewModelScope
GlobalScope.launch{
result1= async{ getTripCodes() }
result2= async{ getTrucks() }
result3= async{ getTrailers() }
doSomethingWithTripCodes(result1.await())
doSomethingWIthTrucks(result2.await())
doSomethingTrailers(result3.await())
}
Reference:
post1

Can we use Retrofit inside a loop in android studio?

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.

Retrofit non-api method

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!

Retrofit custom error handler + sync & asyc requests

I have a custom error handler that checks RetrofitError it gets passed and rethrows it as custom exceptions
private static ErrorHandler getErrorHandler() {
return new ErrorHandler() {
#Override
public Throwable handleError(RetrofitError cause) {
switch (cause.getKind()) {
case NETWORK: return new NetworkException(cause);
case HTTP: return new ApiException(cause);
default: return cause;
}
}
};
}
If this is my endpoint
#GET(USERS_GET_URL)
User getUsers() throws NetworkException, ApiException;
while executing synchronous request I try...catch and handle each custom exception as I want. When it is done asynchronously using
#GET(USERS_GET_URL)
void getUsers(Callback<User> cb) throws NetworkException, ApiException;
the handled exception gets rethrown as RetrofitError. The following snippet of code is from CallbackRunnable class of Retrofit which executes the request
try {
final ResponseWrapper wrapper = obtainResponse();
callbackExecutor.execute(new Runnable() {
#Override public void run() {
callback.success((T) wrapper.responseBody, wrapper.response);
}
});
} catch (RetrofitError e) {
Throwable cause = errorHandler.handleError(e);
final RetrofitError handled = cause == e ? e : unexpectedError(e.getUrl(), cause);
callbackExecutor.execute(new Runnable() {
#Override public void run() {
callback.failure(handled);
}
});
}
As it can be seen, my custom exceptions are getting rethrown as RetrofitError which makes me loose valuable information. Is there any way I can bypass custom error handling for just the async requests?
In your ErrorHandler you pathing original RetrofitError as cause, so as result in your Callback#failure(RetrofitError error) to get actual information you need to write next code: error.getCause().getCause(). This error will contain response that server send with all the data.
But error handler was created for sync request and after some time square team decided to close this gap this way. For more info you can read: https://gist.github.com/benvium/66bf24e0de80d609dac0
As for me, I don't recommend to use ErrorHander for async way, because I don't find any good solution to handle different types of error. It was much easier to get data right from initial RetrofitError.

Login function with ASync networking request?

I'm using 'Retrofit' for making asynchronous network requests, how might i right a function for handling logins? For instance i've currently attempted:
public UserAuthResponse Login(String username, String password) {
try {
Callback<UserAuthResponse> getAuthCallback = new Callback<UserAuthResponse>() {
#Override
public void failure(RetrofitError arg0) {
if (arg0 != null) {
if (arg0.getMessage() != null
&& arg0.getMessage().length() > 0) {
Log.e("KFF-Retrofit", arg0.getMessage());
}
}
}
#Override
public void success(UserAuthResponse listItem,
retrofit.client.Response arg1) {
Log.e("dg", listItem.getUser().getFirstname());
}
};
service.authUser(username, MD5(password), getAuthCallback);
return response;
} catch (RetrofitError e) {
e.printStackTrace();
return null;
}
}
But this is flawed: there is no way of returning the 'UserAuthResponse' from the function? How can i pass back the result?
It seems like i need a synchronous call to the web service but then i'm hit with a 'NetworkOnMainThreadException'
What is the best practice for things like this? Sorry about the poor explanation, struggling to form the right words.
Well the things is that when you're using the Callback as your means of getting the results from Retrofit you automatically giving away the possibility of having the response returned inline. There's a few ways this can be solved. I suppose it's up to you to choose which one fits best with your design.
You could decide to not use the Callback approach and use the inline result from Retrofit but then you'd need to handle the scheduling yourself otherwise you'll hit the Exception of NetworkOnMainThreadException like you mentioned.
You could also pass in a listener to your login method. This listener could then be called by the result Callback. This could be useful if you're trying to hide Retrofit behind some sort of service layer and expose a simple login interface.
interface OnLoginListener {
onLoginSuccessful(UserAuthResponse response);
onLoginFailed(Throwable t);
}
public void Login(String username, String password, final OnLoginListener listener) {
Callback<UserAuthResponse> getAuthCallback = new Callback<UserAuthResponse>() {
#Override
public void failure(RetrofitError e) {
// You can handle Retrofit exception or simply pass them down to the listener as is
listener.onLoginFailed(e);
}
#Override
public void success(UserAuthResponse listItem,
retrofit.client.Response arg1) {
// handle successful case here and pass down the data to the listener
listener.onLoginSucessful(listItem);
}
};
service.authUser(username, MD5(password), getAuthCallback);
}
use this line i Manifest
<uses-permission android:name="android.permission.INTERNET"/>
or use this before network operation (not suggestible)
if (Build.VERSION.SDK_INT>= 10) {
ThreadPolicy tp = ThreadPolicy.LAX;
StrictMode.setThreadPolicy(tp);
}

Categories

Resources