Retrofit 2 calling same request so many unwanted times in my app - android

I have an android app already uploaded to store and working fine, sometimes when a lot of users open the app at the same time (eg. when sending a notification) our servers are getting overloaded and stops responding for about 10 to 15 minutes. I'm using Retrofit 2 to send requests to our server from the app, I've checked all the app requests there are no places where the request is happening when it shouldn't or needed, finally, I added a logging interceptor to retrofit to track the requests and I was surprised that each request is being called more than ten times for no apparent reason! as you can see in the following image:
This is happening with all requests called from the app for no apparent reason. My service generator for Retrofit 2 is the following:
private static OkHttpClient.Builder httpClient
= new OkHttpClient.Builder().connectTimeout(10000, TimeUnit.SECONDS)
.readTimeout(10000, TimeUnit.SECONDS).addInterceptor(new FakeInterceptor())
.addNetworkInterceptor(new StethoInterceptor())
.retryOnConnectionFailure(false);
private static Gson gson = new GsonBuilder()
.registerTypeAdapter(Date.class, new DateDeserializer())
.create();
private static Retrofit.Builder builder =
new Retrofit.Builder()
.baseUrl(API_BASE_URL));
public static <S> S createService(Class<S> serviceClass) {
HttpLoggingInterceptor logging = new HttpLoggingInterceptor();
logging.setLevel(HttpLoggingInterceptor.Level.BASIC);
httpClient.addInterceptor(logging);
Retrofit retrofit = builder.client(httpClient.build()).build();
return retrofit.create(serviceClass);
}
I am sure request is only called once from my code but I have no idea why Retrofit is making all these useless and unnecessary requests. What might be the problem? Thanks in advance, any help is much appreciated.

It's because you are creating multiple instances of the Retrofit client. You should declare a static instance of Retrofit in your class and change
Retrofit retrofit = builder.client(httpClient.build()).build();
to
if(retrofit == null) {
retrofit = builder.client(httpClient.build()).build();
}
Reference: https://github.com/square/retrofit/issues/1724

Related

Retrofit, OkHttp3 and Spring occasional error The request was rejected because the URL contained a potentially malicious String ";"

we are having a network error, and are not sure what could be causing it. We upload data from an Android app, and most of the time it works great.
Data are sent with Retrofit and OKHttp3 using an interface:
private static String BASE_URL = "https://backend.ourapp.com/api/";
private static String cert1 = ...;
private static String cert2 = ...;
private static String cert3 = ...;
public static Retrofit retrofit;
static Gson gson = new GsonBuilder()
.setLenient()
.create();
static CertificatePinner certificatePinner = new CertificatePinner.Builder()
.add("backend.ourapp.com", cert1)
.add("backend.ourapp.com", cert2)
.add("backend.ourapp.com", cert3)
.build();
public static HttpLoggingInterceptor logging =
new HttpLoggingInterceptor()
.setLevel(HttpLoggingInterceptor.Level.BODY);
public static OkHttpClient okHttpClient = new OkHttpClient.Builder()
.readTimeout(180, TimeUnit.SECONDS)
.connectTimeout(180, TimeUnit.SECONDS)
.certificatePinner(certificatePinner)
.addInterceptor(logging)
.build();
public static Retrofit getClient(String url) {
if (retrofit == null) {
retrofit = new Retrofit.Builder()
.baseUrl(url)
.addConverterFactory(GsonConverterFactory.create())
.client(okHttpClient)
.build();
}
return retrofit;
}
public static ApiInterface apiInterface() {
retrofit = null;
return ApiClient.getClient(BASE_URL).create(ApiInterface.class);
}
But then all of a sudden, and randomly, we start having issues. Error from backend log is:
ERROR org.apache.juli.logging.DirectJDKLog [http-nio-7200-exec-1] Servlet.service() for servlet [dispatcherServlet] in context with path [] threw exception
org.springframework.security.web.firewall.RequestRejectedException: The request was rejected because the URL contained a potentially malicious String ";"
We understand this is part of StrictHttpFirewall, and aren't looking to use .setAllowSemicolon(true);.
But we are confused why this is happening, because we aren't, as far as we know, appending anything to our URL. It's an error that also seems to pop up randomly, and then disappear randomly. It also isn't universal- a user will start having the problem while others are OK, and then they will be fine. It typically happens with mobile data networks and not wifi.
Suggestions?? Other things we should look at? Don't suppose it could somehow be certificate related? We have set the timeouts fairly liberally because our app is used on networks that are often slow, but if this particular error doesn't happen, upload is usually successful. We are using retrofit 2.4 and okhttp3 3.10.
Thanks
It appears that the problem is resolved by simply updating Retrofit to 2.9.0 and OkHttp3 to 4.9.1.

How to get intermediate response from Retrofit API call?

I using Retrofit to making API call. All API call is working fine except one where its returning huge response around 15k records.
Issue is when made call progress bar is being shown infinitely until I get response. And as response too huge getting OOM exception.
As an solution I found that need to use #Streaming annotation. I used that but didn't get intermediate callback. I want API should return chunk of response one by one.
Please help me.
public static ServiceInterface getServiceAPIClient() {
HttpLoggingInterceptor interceptor = new HttpLoggingInterceptor();
interceptor.setLevel(HttpLoggingInterceptor.Level.BODY);
Gson gson = new GsonBuilder()
.setLenient()
.create();
OkHttpClient client = new OkHttpClient.Builder()
.addInterceptor(
new Interceptor() {
#Override
public Response intercept(Interceptor.Chain chain) throws IOException {
Request request = chain.request();
Request.Builder builder = request.newBuilder();
builder = request.newBuilder();
if (!TextUtils.isEmpty(PrefsHelper.getAccessTokenEdrm())) {
builder.addHeader(AUTHORIZATION, PrefsHelper.getAccessTokenEdrm());
}
builder.addHeader(API_VERSION, "1.0")
.addHeader("Accept", "application/json");
request = builder.build();
return chain.proceed(request);
}
}).connectTimeout(5, TimeUnit.MINUTES) .readTimeout(5, TimeUnit.MINUTES).addInterceptor(interceptor)
.build();
Retrofit retrofit = new Retrofit.Builder()
.baseUrl(API_BASE_URL)
.addCallAdapterFactory(RxJavaCallAdapterFactory.create())
.addConverterFactory(GsonConverterFactory.create(gson))
.client(client)
.build();
return retrofit.create(ServiceInterface.class);
}
API Method
#POST(EdrmConstants.SEARCH_DOCUMENTS)
#Streaming
Observable<ResponseBody> searchDocuments(#Body DocumentRequest documentRequest);
15k records is too match.
Retrofit needs time to make http request and makes serialization to your ResponseBody.class
I sure serialization takes main time.
I guess most right solution is to edit request on server side to split data on pages with 200-500 records.

java.net cannot resolve Domain name

So, I was doing my project on localhost and everything ran smooth. I was able to make request call to the server and getting the response from my android. No problem. Then I got a ec2 from AWS and did't map to any domain name. SO, When I use curl or postman to make the request it works totally fine. Everything works great. But the same URL I try to make a request from my Retrofit it says
HTTP FAILED: java.net.UnknownHostException: Unable to resolve host "ec2-11-130-81-251.ap-south-1.compute.amazonaws.comapi": No address associated with hostname
I also made sure that I have given internet permission in my phone and everything. So, is it a must to have a domain name when making network request from an android phone? is there any way to bypass such things?
here is my retrofit singleton class if it finds useful. Thanks
public class ApiClient {
// public static final String BASE_LOCAL_URL = "http://192.168.1.4:3000/";
// public static final String BASE_LOCAL_EMULATOR_URL = "http://10.0.2.2:3000/";
public static final String SERVIER_LOGIN="ec2-11-130-81-251.ap-south-1.compute.amazonaws.comapi";
public static final String BASE_URL = SERVIER_LOGIN;
private static Retrofit retrofit = null;
private ApiClient() {
if (retrofit != null) {
throw new RuntimeException("Use getInstance() method to get the single instance of this class.");
}
}
public static synchronized Retrofit getClient(Context context) {
if (retrofit == null) {
HttpLoggingInterceptor interceptor = new HttpLoggingInterceptor();
interceptor.level(HttpLoggingInterceptor.Level.BODY);
AuthInterceptor authInterceptor = new AuthInterceptor(context);
OkHttpClient client = new OkHttpClient.Builder().
addInterceptor(authInterceptor).
readTimeout(60, TimeUnit.SECONDS)
.writeTimeout(60, TimeUnit.SECONDS)
.connectTimeout(60, TimeUnit.SECONDS)
.addInterceptor(interceptor)
.build();
retrofit = new Retrofit.Builder()
.baseUrl(BASE_URL)
.addConverterFactory(GsonConverterFactory.create(getGson()))
.client(client)
.build();
return retrofit;
}
return retrofit;
}
private static Gson getGson() {
return new GsonBuilder().setDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSS").create();
}
}
Resolved by adding a slash in your base URL. That is if your base URL is www.some_base_url.com it should be www.some_base_url.com/ when initiating a retrofit instance. So. Make sure you're adding a slash in the end if it gives that error.

okHttp requests + retrofit share session

I have an old app which uses raw okhttp calls and utilizes sessions.
OkHttp is setup by this code:
OkHttpClient okHttpClient = null;
try {
OkHttpClient.Builder builder = SupportRequests.getUnsafeOkHttpClient();
builder.readTimeout(5000, TimeUnit.MILLISECONDS);
builder.connectTimeout(10000, TimeUnit.MILLISECONDS);
CookieManager cookieManager = new CookieManager();
cookieManager.setCookiePolicy(CookiePolicy.ACCEPT_ALL);
builder.cookieJar(new JavaNetCookieJar(cookieManager));
okHttpClient = builder.build();
SupportRequests.setOkHttpClient(okHttpClient);
} catch (NoSuchAlgorithmException | KeyManagementException e) {
FirebaseCrash.report(e);
}
I want to switch to using retrofit. The app is quite big and contains several dozens requests. Making it in a one-time switch is not possible.
I tried to start switching and encountered a problem.
Retrofit and okHtpp raw calls do not share PHP session.
I create retrofit with following code:
Retrofit retrofit = new Retrofit.Builder()
.baseUrl(Preferences.getInstance().server)
.addConverterFactory(GsonConverterFactory.create(SupportGson.get()))
.addCallAdapterFactory(RxJava2CallAdapterFactory.create())
.client(okHttpClient)
.build();
retrofitProvider.setRetrofit(retrofit);
But sessions are different for retrofit calls and raw okhttp calls.
Any idea how to make them share session?

Add Response call back when using retrofit with rxjava

I am using retrofit with Rxjava to get response from API as you can see the method i am using i can't see what's coming in the response and offcourse i don't need to becuase i am providing GsonConverter to retrofit but for some debugging reason i need to see the response that coming from API. How can i do this, what code i need to add.
public interface ProductApiService
{
String END_POINT = "http://beta.site.com/index.php/restmob/";
#GET(Url.URL_PRODUCT_API)
Observable<Product> getProducts(#Query("some_id") String cid);
class Creator
{
public static ProductApiService getProductAPIService() {
Gson gson = new GsonBuilder()
.setDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSS'Z'")
.create();
Retrofit retrofit = new Retrofit.Builder()
.baseUrl(ProductApiService.END_POINT)
.addConverterFactory(GsonConverterFactory.create(gson))
.addCallAdapterFactory(RxJavaCallAdapterFactory.create())
.build();
return retrofit.create(ProductApiService.class);
}
}
}
You can only do this as of Retrofit 2: Change the return type to include Response:
#GET(Url.URL_PRODUCT_API)
Observable<Response<Product>> getProducts(/* ...etc... */);
You can also use Observable<Result<Product>> if you want to see all possible errors in onNext (including IOException, which normally uses onError).
Daniel Lew's approach is quick and contains the least amount of boiler plate code. However, this may force you to refactor your networking logic. Since you mention needing this for debugging purposes, perhaps using a configured OkHttpClient with Interceptors is a less intrusive strategy.
OkHttpClient httpClient = new OkHttpClient.Builder()
.addInterceptor(new Interceptor() {
#Override
public Response intercept(Chain chain) throws IOException {
Request req = chain.request();
Response resp = chain.proceed(req);
// ... do something with response
return resp;
}
})
.build();
Retrofit retrofit = new Retrofit.Builder()
.client(httpClient)
.baseUrl(ProductApiService.END_POINT)
.addConverterFactory(GsonConverterFactory.create(gson))
.addCallAdapterFactory(RxJavaCallAdapterFactory.create())
.build();

Categories

Resources