Receiving incomplete response from retrofit - android

I am using retrofit version 2.6.1 for making http requests over the network. The JSON I am expecting is 42466 characters long. However, I am receiving only 4073 characters and API are working fine on web browser and postman.
So I added custom okhttp client and increase timeout but it does not help me.
private var okHttpClient: OkHttpClient = OkHttpClient().newBuilder()
.connectTimeout(10, TimeUnit.SECONDS)
.readTimeout(10, TimeUnit.SECONDS)
.writeTimeout(10, TimeUnit.SECONDS)
.build()
Then I tried adding a logging interceptor and I found that okhttp is receiving the response what I wanted in interceptor logs but in chunks.
private val httpInterceptor: HttpLoggingInterceptor = HttpLoggingInterceptor().setLevel(HttpLoggingInterceptor.Level.BODY)
private var okHttpClient: OkHttpClient = OkHttpClient().newBuilder()
.connectTimeout(10, TimeUnit.SECONDS)
.readTimeout(10, TimeUnit.SECONDS)
.writeTimeout(10, TimeUnit.SECONDS)
.addInterceptor(httpInterceptor)
.build()
At last, I assigned http client and interceptor to retrofit builder and this is how it's look
private val centralRetrofit = Retrofit.Builder().baseUrl("https://www.********.com/")
.addConverterFactory(ScalarsConverterFactory.create())
.client(okHttpClient)
.build()
.create(MusicAccess::class.java)
So, I think using post request will help me rather than get and tried taking all the response in string format to check the reponse
#Headers("Content-Type: text/html; charset=UTF-8")
#POST("**********")
fun getMusic(): Call<String>
But it also did not get to conclusion after that I thought http response will have size limit and used reader to access the json from url by following way.
val client = OkHttpClient()
val request = Request.Builder().url("********")
.addHeader("Content-Type", "application/json")
.build()
val response = client.newCall(request).execute()
val input = response.body()?.byteStream()
val reader = BufferedReader(InputStreamReader(input))
But as this https://stackoverflow.com/a/26023885/7639056 answer state that we cannot configure the OkHttpClient to read more than 2048 bytes from the buffer
So is there any way I can get all the data at once?

Eventually I figured out that why this is happening.
There were no problem with the HTTP request. But only a part of it was being printed in logcat due to it's limited buffer size.
There is a size limit of 1024 bytes for binary logs. The size limit for non-binary logs are as shown below.
#define LOGGER_ENTRY_MAX_LEN (4*1024)
#define LOGGER_ENTRY_MAX_PAYLOAD (LOGGER_ENTRY_MAX_LEN - sizeof(struct logger_entry))
To set the limit to a larger number, Preferences/Settings -> Editor -> General -> Console, check the box next to Override console cycle buffer size, and enter the number.
I am feeling so embarrassed, but thank you for your support.

Related

what is retrofit interceptor , can we have multiple?

I can not understand the retrofit interceptors ,
private val OkHttpClient by lazy {
okhttp3.OkHttpClient.Builder()
.addInterceptor {
onOnIntercept(it)
}
.addInterceptor(LoggingInterceptor())
.addInterceptor(getInterceptor404())
.callTimeout(10, TimeUnit.MILLISECONDS)
// .addInterceptor(TimeoutInterceptor())
.build()
}
and what do these lines do, and If I have, multiple does the speed down?
val response: Response = chain.proceed(chain.request())
return chain.proceed(chain.request())
In Android sometimes you need to add a couple of parameters, like headers, to make a successful request, this is normal behavior from all the Android Apps when you are using Retrofit, you can do it in multiple ways
For example, you can add parameters directly to your request interface using the annotation Headers and putting a plain String, like this:
#Headers("Content-Type:application/json; charset=UTF-8")
#GET("yourwebsite/{someParam}/login")
fun logout(#Path("someParam") someParam: String?): Observable<LoginResponseViewModel>
Another solution is to send the Headers as a parameter to your interface function, using an annotation Header and sending a parameter, this gives you the possibility to have a custom parameter that you can manage from every request:
#Headers("Content-Type:application/json; charset=UTF-8")
#GET("yourwebsite/{someParam}/login")
fun logout(#Header(UUID.randomUUID().toString()) authToken: String?, #Path("someParam") someParam: String?): Observable<LoginResponseViewModel>**
Interceptor
A couple of people using Dagger probably will go for an Interceptor, you can have two types of interceptor:
The first one is using an interceptor directly in your Singleton, this will not give you versatility, but it will solve your problem faster, in this example, you can go for the chain object, get the request of the Retrofit call, get a new Builder and then add the Headers.
#Provides
#Singleton
fun getUnsafeOkHttpClient(): OkHttpClient {
val interceptor = HttpLoggingInterceptor()
interceptor.level = HttpLoggingInterceptor.Level.BODY
val builder = OkHttpClient.Builder()
builder.addInterceptor(interceptor)
.connectTimeout(30, TimeUnit.SECONDS)
.readTimeout(30, TimeUnit.SECONDS)
.followRedirects(true)
.followSslRedirects(true)
.addInterceptor { chain ->
val newRequest = chain.request().newBuilder()
.addHeader("Authorization", UUID.randomUUID().toString())
.build()
chain.proceed(newRequest)
}
}
Yes, you can use multiple interceptors. When you do a request calling interface method using retrofit, your request go to the interceptor and then continue. In the interceptor you can rewrite or retry request. For example, you could add the access token in all request and refresh the token if is necessary, add the headers, another bodies, etc. When you received a response from api, the interceptor intercept the response too. But please, read the documentation to understand how it works. Have a nice coding!

How to handle retrofit socket timeout for uploading files in android kotlin?

var client = OkHttpClient()
val builder = OkHttpClient.Builder()
val gson = GsonBuilder()
.setLenient()
.create()
builder.addInterceptor(AddCookiesInterceptor(mcontext))
builder.addInterceptor(ReceivedCookiesInterceptor(mcontext))
builder.callTimeout(100,TimeUnit.SECONDS)
client = builder.build()
retrofit = Retrofit.Builder()
.baseUrl(BASE_URL)
.client(client)
.addConverterFactory(GsonConverterFactory.create())
.addCallAdapterFactory(RxJava2CallAdapterFactory.create())
// .addConverterFactory(ScalarsConverterFactory.create())
.build()
This is my retrofit client service builder. For normal api functions with json response callback it works fine. Is there any modification required for an large file upload?
With current scenario, uploading 20 mb data, takes more time in slow network connection which returns a socket timeout exception.
Uploading as multipart body
var fileBody : ProgressRequestBody? = null
fileBody = ProgressRequestBody(file,"*/*",this#CaseFileUploadFragment)
var fileToUpload: MultipartBody.Part =
MultipartBody.Part.createFormData("image",file.name, fileBody)
var filename : RequestBody =
RequestBody.create(MediaType.parse("text/plain"),file.getName())
and following is the function used
#Multipart
#POST("{urlMpString}")
fun uploadFile(
#Path ("urlMpString") urlEndString : String, #Part file: MultipartBody.Part, #Part("file") requestBody: RequestBody,
#Part("apiInfo") `object1`: JsonObject, #Part("parameters") `object2`: JsonObject
): Call<JsonObject>
Everything works fine for small data files.
Any suggestions and help will be appreciated.
Thanks
Try below code for okHttpClient
OkHttpClient okHttpClient = new OkHttpClient.Builder()
.connectTimeout(5, TimeUnit.MINUTES)
.readTimeout(30, TimeUnit.SECONDS)
.writeTimeout(15, TimeUnit.SECONDS)
.build();

Use cached response only. Without network call

There is an API that has calls limit, in this case I want to cache response and don't run network response if cache is still valid.
First of all I have cache interceptor
fun provideCacheInterceptor(): Interceptor = Interceptor { chain ->
val response = chain.proceed(chain.request())
val cacheControl = CacheControl.Builder()
.maxAge(6, TimeUnit.HOURS)
.maxStale(6, TimeUnit.HOURS)
.onlyIfCached()
.build()
response.newBuilder()
.header("CacheControl", cacheControl.toString())
.build()
}
I attach cache and interceptor to the client
client = OkHttpClient().newBuilder()
.cache(cache)
.addInterceptor(loggingInterceptor)
.addInterceptor(cacheInterceptor)
As I result when I'm trying to check if the reponse from cache and/or from network
Log.e("!##", "cached: ${it.raw().cacheResponse()?.toString()}")
Log.e("!##", "network: ${it.raw().networkResponse()?.toString()}")
I get
cached: Response{protocol=http/1.1, code=200, message=, url=https://API}
network: Response{protocol=h2, code=200, message=, url=https://API}
Is there any way not to call the network endpoint if cache is still valid?
Problem in my implementation that I proceed the original request and apply header to response (my bad), need to modify initial request and proceed modified request then.
Also seems like Cache-Control header typo.
The proper cache interceptor looks like
fun provideCacheInterceptor(networkManager: NetworkManager): Interceptor = Interceptor { chain ->
val request = chain.request()
val cacheControl = CacheControl.Builder()
.maxAge(6, TimeUnit.HOURS)
.maxStale(6, TimeUnit.HOURS)
.build()
chain.proceed(request.newBuilder()
.header("Cache-Control", cacheControl.toString())
.build())
}

Retrofit socket timeout exception while uploading larger files using multipart

I am facing the issue of socket timeout exception while using retrofit 2.0.2 library and okhttp 2.3.0. I am trying to upload the image file which is between 500kb to 1.5mb it is uploading successfully.but when i tried to upload video file which is greater than 5mb i am getting this exception.
I used httpclient for connection settings as below.
public static OkHttpClient okHttpClient = new OkHttpClient.Builder()
.connectTimeout(0, TimeUnit.SECONDS)
.writeTimeout(0, TimeUnit.SECONDS)
.readTimeout(0, TimeUnit.SECONDS)
.build();
please suggest me to upload larger files without this issue.Thanks in advance
you can provide the time in seconds as follows
public class ApiClient {
public static final String BASE_URL = "your_url";
public static Retrofit retrofit = null;
public static Retrofit getApiClient() {
if (retrofit == null) {
OkHttpClient okHttpClient = new OkHttpClient().newBuilder()
.connectTimeout(60, TimeUnit.SECONDS)
.readTimeout(60, TimeUnit.SECONDS)
.writeTimeout(60, TimeUnit.SECONDS)
.build();
return new Retrofit.Builder()
.baseUrl(BASE_URL)
.client(okHttpClient)
.addConverterFactory(GsonConverterFactory.create())
.build();
}
return retrofit;
}
}
i've given 60 seconds
There can be two issues with this type of error.
Check Read & Write Timeout
val client = OkHttpClient.Builder()
.connectTimeout(30, TimeUnit.SECONDS)
.writeTimeout(30, TimeUnit.SECONDS)
.readTimeout(30, TimeUnit.SECONDS)
.build()
Check the Mime type you are sending. The backend developer may have filtered the accepted Mime type at their end. instead of MediaType.parse("multipart/form-data"). write the valid mime type for files like image/jpg or video/mp4
MediaType.parse("image/png")

HttpLoggingInterceptor not logging with retrofit 2

Im trying to log all the requests (with a network interceptor) using refrofit2, kotlin and logging-interceptor:
retrofit: "2.0.2"
okhttp3 : "3.2.0"
com.squareup.okhttp3:logging-interceptor 3.2.0
like:
val interceptor = HttpLoggingInterceptor()
interceptor.setLevel(HttpLoggingInterceptor.Level.BODY);
val okHttpClient = OkHttpClient.Builder()
.addNetworkInterceptor(interceptor) // same for .addInterceptor(...)
.connectTimeout(30, TimeUnit.SECONDS) //Backend is really slow
.writeTimeout(30, TimeUnit.SECONDS)
.readTimeout(30, TimeUnit.SECONDS)
.build()
sRestAdapter = Retrofit.Builder()
.client(okHttpClient)
.baseUrl(if (host.endsWith("/")) host else "$host/")
.addConverterFactory(GsonConverterFactory.create(gson()))
.addCallAdapterFactory(RxJavaCallAdapterFactory.create())
.build()
It just print:
D/OkHttp: --> GET url...
D/OkHttp: --> END GET
What is happening?
--------------- EDIT --------
Errors doing requests on Main Thread are not showing by the logger, so be careful.
private val interceptor = run {
val httpLoggingInterceptor = HttpLoggingInterceptor()
httpLoggingInterceptor.apply {
httpLoggingInterceptor.level = HttpLoggingInterceptor.Level.BODY
}
}
private val okHttpClient = OkHttpClient.Builder()
.addNetworkInterceptor(interceptor) // same for .addInterceptor(...)
.connectTimeout(30, TimeUnit.SECONDS) //Backend is really slow
.writeTimeout(30, TimeUnit.SECONDS)
.readTimeout(30, TimeUnit.SECONDS)
.build()
Instead of
val okHttpClient = OkHttpClient.Builder()
.addNetworkInterceptor(interceptor)
...
you should have something like:
val okHttpClient = OkHttpClient.Builder()
.addInterceptor(interceptor)
...
as the addNetworkInterceptor() plays with interceptors that observe a single network request and response, while addInterceptor() adds interceptor that observes the full span of each call: from the connection is established (if any) until after the response source is selected (either the origin server, cache, or both).
EDIT
Errors doing requests on Main Thread are not showing by the logger, so be careful
Doing networking on main thread is not an "ordinary" error. It will result in your app being killed by the system with NetworkOnMainThreadException and this will happen before interceptor would be given any chance to run.

Categories

Resources