Retrofit 2 : End of input at line 1 column 1 path $ - android

Have made a thorough searching to this particular problem but while implementing answers under each question I encountered, am still getting the same output:
End of input at line 1 column 1 path $
I perfomed my Request on PostMan and I got expected output:
Here is the Screenshot of the Postman Request
Interfaces
#POST(Constant.API_REQUEST)
Observable<ServerResponse> postToWinnersList(#Body ServerRequest serverRequest);
ApiClient
public class ApiClient {
public static Retrofit retrofit;
private static OkHttpClient.Builder okHttpClientBuilder;
private static HttpLoggingInterceptor loggingInterceptor;
public static Retrofit getApiClient(){
if (retrofit == null){
// create instance of Httpclient
okHttpClientBuilder = new OkHttpClient.Builder();
loggingInterceptor = new HttpLoggingInterceptor();
loggingInterceptor.setLevel(HttpLoggingInterceptor.Level.BODY);
if(BuildConfig.DEBUG){
okHttpClientBuilder.addInterceptor(loggingInterceptor);
}
// instance of retrofit
retrofit = new Retrofit.Builder().baseUrl(Constant.BASE_URL).
addCallAdapterFactory(RxJava2CallAdapterFactory.create()).
addConverterFactory(GsonConverterFactory.create())
.client(okHttpClientBuilder.build())
.build();
}
return retrofit;
}
}
Retrofit/RxJava Request Code:
Observable<ServerResponse> response = apiInterface.postToWinnersList(serverRequest);
response.observeOn(AndroidSchedulers.mainThread())
.subscribeOn(Schedulers.io())
.subscribeWith(new DisposableObserver<ServerResponse>() {
#Override
public void onNext(ServerResponse serverResponse) {
AVLoadingIndicatorView1.setVisibility(View.GONE);
txtSubmitWinner.setVisibility(View.VISIBLE);
}
#Override
public void onError(Throwable e) {
AVLoadingIndicatorView1.setVisibility(View.GONE);
txtSubmitWinner.setVisibility(View.VISIBLE);
Toast.makeText(getApplicationContext(), e.getMessage(), Toast.LENGTH_LONG).show();
}
#Override
public void onComplete() {
showShortMsg(getString(R.string.submit_success));
}
});
Kindly help, thanks in Advance.

You can get this error when you are expecting an object in the response, but the API doesn't return anything other than result codes (200,...). Options:
- Check that the API really returns a ServerResponse.
- If you don't really need it to return anything, use Observable<Response<Void>> instead of Observable<ServerResponse>

You only get that error when something is wrong with your json response, Check again to make sure both the error response and correct response are well formatted.
Enter wrong credentials using postman and see what the output looks like.

Not sure but I think in Constant.API_REQUEST you are not appending "/".Let me know if it is right.
ex.#POST("index.php") // wrong
#POST("/index.php") //correct way

This error happens when an answer is void.
To correct this error make sure that in the return of the request there will be void.
Eg.
Interface whit kotlin
#POST("endPoint/")
fun relatarProblema(#Body serverRequest: ServerRequest ): Call<Void>
Don't forget to override the return type in the api call.
example whit Kotlin
call.enqueue (object: Callback <Void> {
override fun onResponse(call: Call<Void>, response: Response<Void>) {
}
override fun onFailure(call: Call<Void>, t: Throwable) {
}
})

When I used Coroutines what helped was to basically not return anything from the method:
#GET
#Headers("X-Requested-With:XMLHttpRequest")
suspend fun methodCall(
#Url url: String
)

I was getting the same exception yesterday and figured out that I was using a LogOutResponse data class as the expected response for the API, but then I got to know that the API doesn't return any JSON response corresponding to that data class, in fact, it didn't return anything in the body and only returned result code (200,300,400, etc). So I changed my implementation from:
#POST("logout")
suspend fun logout(): LogOutResponse
to
#POST("logout")
suspend fun logout(): Response<Unit>
Earlier retrofit tried to parse a JSON response at line 1 when there was no JSON there, that's why it threw that exception.
After the changes, the code worked fine as Response is a default retrofit class and Unit type inside it is of void type meaning we don't expect anything in return (no JSON body in response).

Related

Read plain text response from server using Retrofit

I'm working on an application that uses Retrofit for network operations. As it stands, everything works well with GsonConverterFactory handling serialization. Here is how I setup Retrofit
Retrofit.Builder()
.baseUrl("<base url>")
.client(client)
.addConverterFactory(GsonConverterFactory.create(gson))
.build()
Now I need to connect to a legacy service which returns content in text/plain; charset=utf-8 format. Here is the Retrofit interface
#GET("https://<domain>/<endpoint>?Type=Query")
suspend fun callStatus(#Query("userId") id: Int): Response<String>
This will return status of a call for a valid user. For instance, if the user is valid and there is a status, it returns "Active" as plain text. If there is no valid user, it returns an error code of #1005
I could add custom converter factory like this (found on the web)
final class StringConverterFactory implements Converter.Factory {
private StringConverterFactory() {}
public static StringConverterFactory create() {
return new StringConverterFactory();
}
#Override
public Converter<String> get(Type type) {
Class<?> cls = (Class<?>) type;
if (String.class.isAssignableFrom(cls)) {
return new StringConverter();
}
return null;
}
private static class StringConverter implements Converter<String> {
private static final MediaType PLAIN_TEXT = MediaType.parse("text/plain; charset=UTF-8");
#Override
public String fromBody(ResponseBody body) throws IOException {
return new String(body.bytes());
}
#Override
public RequestBody toBody(String value) {
return RequestBody.create(PLAIN_TEXT, convertToBytes(value));
}
private static byte[] convertToBytes(String string) {
try {
return string.getBytes("UTF-8");
} catch (UnsupportedEncodingException e) {
throw new RuntimeException(e);
}
}
}
}
But I didn't see it make any difference. Also, it could well disguise JSON as normal text and break all existing service. Is there a better way to handle this scenario? I thought of having separate retrofit instance for plain text, bit dirty though. Do you have any other suggestions/solutions?
Edited
Response header contains the content type as
Content-Type: text/plain; charset=utf-8
Actual response for valid user
Active
Actual response for invalid user
#1005
Update
The order in which you register the converter factories matters. ScalarsConverterFactory must come first.
it should be possible by adding ScalarsConverterFactory when building the Retrofit object.
This can be done alongside with other json converters, e.g.
Retrofit.Builder()
.baseUrl("<base url>")
.client(client)
.addConverterFactory(ScalarsConverterFactory.create())
.addConverterFactory(GsonConverterFactory.create(gson))
.build()
After that, you should be able to receive plaintext responses.
You probably need to add this to your dependencies as well:
implementation 'com.squareup.retrofit2:converter-scalars:2.9.0'
The following is the way that how I get response as plain text (using Java not Kotlin).
Step One
in your gradle (Module);
implementation 'com.squareup.retrofit2:converter-scalars:2.9.0'
Step Two
Create an interface
public interface MyInterface {
#GET("something.php")
Call<String> getData(#Query("id") String id,
#Query("name") String name);
}
Step Three
Retrofit retrofit = new Retrofit.Builder()
.baseUrl("https://example.com")
.addConverterFactory(ScalarsConverterFactory.create())
.build();
MyInterface myInterface = retrofit.create(MyInterface.class);
Call<String> call = myInterface.getData("id","myname");
call.enqueue(new Callback<String>() {
#Override
public void onResponse(Call<String> call, Response<String> response) {
String plain_text_response = response.body();
}
#Override
public void onFailure(Call<String> call, Throwable t) {
}
});
You don't need to use a your custom implementation of Converter.Factory you could just use
// your coroutine context
val response = callStatus(userId)
if(response.isSuccessful){
val plainTextContent = response.body()
// handle plainText
} else {
//TODO: Handle error
}
//...
Two things to check first that function should not be suspended & your response should be in the Callback
No need to add extra implementation of scalars.
#GET
fun getJson(
#Url baseUrl: String = slab_pro
): Call<DataClass>

Retrofit : java.lang.IllegalStateException: closed

I am using two kind of interceptor, one is HttpLoggingInterceptor and another one is my custom AuthorizationInterceptor
I am using below updated retrofit version library,
def retrofit_version = "2.7.2"
implementation "com.squareup.retrofit2:retrofit:$retrofit_version"
implementation "com.squareup.retrofit2:converter-gson:$retrofit_version"
implementation 'com.squareup.okhttp3:logging-interceptor:4.4.0'
implementation 'com.squareup.okhttp3:okhttp:4.4.0'
below is code
private fun makeOkHttpClient(): OkHttpClient {
val logger = HttpLoggingInterceptor().setLevel(HttpLoggingInterceptor.Level.BODY)
return OkHttpClient.Builder()
.addInterceptor(AuthorizationInterceptor(context)) <---- To put Authorization Barrier
.addInterceptor(logger) <---- To log Http request and response
.followRedirects(false)
.connectTimeout(50, TimeUnit.SECONDS)
.readTimeout(50, TimeUnit.SECONDS)
.writeTimeout(50, TimeUnit.SECONDS)
.build()
}
When I try to execute below code, in file named SynchronizationManager.kt, it gives me an error.
var rulesResourcesServices = RetrofitInstance(context).buildService(RulesResourcesServices::class.java)
val response = rulesResourcesServices.getConfigFile(file).execute() <---In this line I am getting an exception... (which is at SynchronizationManager.kt:185)
My RulesResourcesServices class is here
After debug I found that when below function called, at that time I am getting an exception
#GET("users/me/configfile")
fun getConfigFile(#Query("type") type: String): Call<ResponseBody>
I am getting following error
java.lang.IllegalStateException: closed
at okio.RealBufferedSource.read(RealBufferedSource.kt:184)
at okio.ForwardingSource.read(ForwardingSource.kt:29)
at retrofit2.OkHttpCall$ExceptionCatchingResponseBody$1.read(OkHttpCall.java:288)
at okio.RealBufferedSource.readAll(RealBufferedSource.kt:293)
at retrofit2.Utils.buffer(Utils.java:316)<------- ANDROID IS HIGH-LIGHTING
at retrofit2.BuiltInConverters$BufferingResponseBodyConverter.convert(BuiltInConverters.java:103)
at retrofit2.BuiltInConverters$BufferingResponseBodyConverter.convert(BuiltInConverters.java:96)
at retrofit2.OkHttpCall.parseResponse(OkHttpCall.java:225)
at retrofit2.OkHttpCall.execute(OkHttpCall.java:188)
at retrofit2.DefaultCallAdapterFactory$ExecutorCallbackCall.execute(DefaultCallAdapterFactory.java:97)
at android.onetap.SynchronizationManager.downloadFile(SynchronizationManager.kt:185)
at android.base.repository.LoginRepository.downloadConfigFilesAndLocalLogin(LoginRepository.kt:349)
at android.base.repository.LoginRepository.access$downloadConfigFilesAndLocalLogin(LoginRepository.kt:48)
at android.base.repository.LoginRepository$loginTask$2.onSRPLoginComplete(LoginRepository.kt:210)
at android.base.repository.LoginRepository$performSyncLogin$srpLogin$1$1.onSRPLogin(LoginRepository.kt:478)
at android.srp.SRPManager$SRPLoginOperation$execute$1.invokeSuspend(SRPManager.kt:323)
at kotlin.coroutines.jvm.internal.BaseContinuationImpl.resumeWith(ContinuationImpl.kt:33)
at kotlinx.coroutines.DispatchedTask.run(DispatchedTask.kt:56)
at kotlinx.coroutines.scheduling.CoroutineScheduler.runSafely(CoroutineScheduler.kt:561)
at kotlinx.coroutines.scheduling.CoroutineScheduler$Worker.executeTask(CoroutineScheduler.kt:727)
at kotlinx.coroutines.scheduling.CoroutineScheduler$Worker.runWorker(CoroutineScheduler.kt:667)
at kotlinx.coroutines.scheduling.CoroutineScheduler$Worker.run(CoroutineScheduler.kt:655)
Below is screenshot, in that you can see that, I am getting output of file but don't know why it is throwing an exception.
checked Retrofit's Utils class
https://github.com/square/retrofit/blob/master/retrofit/src/main/java/retrofit2/Utils.java
static ResponseBody buffer(final ResponseBody body) throws IOException {
Buffer buffer = new Buffer();
body.source().readAll(buffer); <-This line throws an error.
return ResponseBody.create(body.contentType(), body.contentLength(), buffer);
}
Update
Same thing is working fine with enqueue method.
response.enqueue(object : Callback<ResponseBody?> {
override fun onResponse(call: Call<ResponseBody?>, response: retrofit2.Response<ResponseBody?>) {
}
})
I have post same issue with Retrofit team, lets see.
https://github.com/square/retrofit/issues/3336
Thanks to JakeWharton (https://github.com/square/retrofit/issues/3336), I can be able to get solution.
Actually in my custom interceptor I was reading response by following code
Response.body().string()
I was doing because above code was helping me to find out that if there is any error than what kind of error it is....
if it is AUTH_ERROR, I have to generate new token and append it to request header.
According to retrofit document, if we call any of below method then response will be closed, which means it's not available to consume by the normal Retrofit internals.
Response.close()
Response.body().close()
Response.body().source().close()
Response.body().charStream().close()
Response.body().byteStream().close()
Response.body().bytes()
Response.body().string()
So to read data, I will use
response.peekBody(2048).string()
instead of
response.body().string(),
so it will not close response.
below is the final code
val response = chain.proceed(request)
val body = response.peekBody(Long.MAX_VALUE).string()//<---- Change
try {
if (response.isSuccessful) {
if (body.contains("status")) {
val jsonObject = JSONObject(body)
val status = jsonObject.optInt("status")
Timber.d("Status = $status")
if (status != null && status == 0) {
val errorCode = jsonObject.getJSONObject("data").optString("error_code")
if (errorCode != null) {
addRefreshTokenToRequest(request)
return chain.proceed(request)
}
}
} else {
Timber.d("Body is not containing status, might be not valid GSON")
}
}
Timber.d("End")
} catch (e: Exception) {
e.printStackTrace()
Timber.d("Error")
}
return response
Extending #Siddhpura Amit's answer:
If you don't know the bytes to pass into peak method then you can still use all of the methods, but will just have to create new Response object.
Inside interceptor:
okhttp3.Response response = chain.proceed(request);
String responseBodyString = response.body().string();
//Do whatever you want with the above string
ResponseBody body = ResponseBody.create(response.body().contentType(), responseBodyString);
return response.newBuilder().body(body).build();
maybe you closed your response in your AuthorizationInterceptor like this
override fun intercept(chain: Interceptor.Chain): Response {
...
val response = chain.proceed(builder.build())
response.close()
...
}

Unable to successfully send a POST request using Retrofit 2.6.1 - Problems with JSON coverter

I am using the new Retrofit2 with suspending coroutines, and with GET requests everything works fine.
But I now have to implement a POST request, and just can't get it to work
I have a CURL example that looks like this:
curl -X POST -H "Content-Type: application/json;charsets: utf-8" -d '{"tx_guapptokenlist_tokenitem":{"tokenchar":"my-token-string","platform":"android"}}' https://www.example-url.com/tokens?type=56427890283537921
This works fine, and returns this response: {"errors":false,"success":true}%
So here's what my request looks like in my Api class right now:
#Headers( "Content-Type: application/json" )
#POST("/tokens?type=56427890283537921")
suspend fun sendFirebaseToken(#Body tokenRequest: RequestBody) : Call<TokenResponse>
This is my TokenResponse class:
#JsonClass(generateAdapter = true)
data class TokenResponse(
#Json(name="errors")
val errors: Boolean,
#Json(name="success")
val success: Boolean)
and the ApiClient class I'm using:
object ApiClient {
private const val BASE_URL = "https://myExampleUrl.com"
private var retrofit: Retrofit? = null
var moshi = Moshi.Builder().add(KotlinJsonAdapterFactory()).build()
val client: Retrofit?
get() {
if (retrofit == null) {
retrofit = Retrofit.Builder().baseUrl(
BASE_URL
).client(getOkHttpClient())
.addConverterFactory(MoshiConverterFactory.create())
.build()
}
return retrofit
}
fun getOkHttpClient(): OkHttpClient {
return OkHttpClient.Builder().addInterceptor(getLoggingInterceptor())
.connectTimeout(120, TimeUnit.SECONDS)
.readTimeout(120, TimeUnit.SECONDS).writeTimeout(90, TimeUnit.SECONDS).build()
}
private fun getLoggingInterceptor(): HttpLoggingInterceptor {
return HttpLoggingInterceptor().setLevel(
if (BuildConfig.DEBUG) HttpLoggingInterceptor.Level.HEADERS
else HttpLoggingInterceptor.Level.NONE
)
}
}
The first odd thing I noticed: Even with the #POST annotation, if my suspend fun has no return type, I get no error, but okhttp will always send a GET request (at least the endpoint always receives a GET). Not sure if that is supposed to be like that?
Anyway: I need the return values, so I'm returning Call<TokenResponse>.
This leads me to my main problem, that I can't solve: If now I execute my code, it crashes with this log:
java.lang.IllegalArgumentException: Unable to create converter for retrofit2.Call<myapp.communication.TokenResponse>
for method TokenApi.sendToken
at retrofit2.Utils.methodError(Utils.java:52)
To try and deal with this I have used moshi-kotlin-codegen to generate the proper adapter (hence the annotations in the data class), but to no avail. The class is generated, but not used. I have tried to pass a Moshi with JsonAdapterFactory like this var moshi = Moshi.Builder().add(KotlinJsonAdapterFactory()).build()to my ConverterFactory but that doesn't work either.
Tried to add the generated adapter maually to moshi but that also did not work.
I've also tried returning different types in my request. The Retrofit docs state that without a converter one could only return a ResponseBody, but same result: Retrofit complains it has no converter. The same for returning Call<Void>
I feel like I'm missing something here? Who can help? Happy to provide more details, please request what's needed.
Your request function should look like this.
#Headers( "Content-Type: application/json" )
#POST("/tokens?type=56427890283537921")
suspend fun sendFirebaseToken(#Body tokenRequest: RequestBody): TokenResponse
You don't use Call<...> since you have marked it as suspend.
Think the annotation should be:
#JsonClass(generateAdapter = true)
data class TokenResponse(
#field:Json(name = "errors") val errors: Integer,
#field:Json(name = "success") val success: Boolean
)
And try to remove the suspend keyword once, which might clash with generateAdapter = true.
I've got it working now, this is what I learned:
First of all: #Dominic Fischer here is right, Call is wrong, and with everything set up correctly, there is no need to wrap the result object at all (I noticed by the way the #Headers annotation looks to be not necessary, Retrofit seems to just take care of it).
The second and biggest problem is that the client object in my ApiClient class was not used correctly. See the new version:
fun getRetrofitService(): ApiService {
return Retrofit.Builder()
.baseUrl(BASE_URL)
.client(getOkHttpClient())
.addConverterFactory(MoshiConverterFactory.create())
.build().create(ApiService::class.java)
}
See that now the 'create()' step is added, which before I handled outside of this class. There I used my Retrofit object to create the service just like here, but I accidentally passed ApiClient::class.java. Interestingly that compiles and runs just fine, but of course this must mess up somewhere - it's unable to properly build the JSON adapters.
As a result I pulled this step into my ApiClientin order to prevent such accidents in the future.
If anybody has suggestions as to meking this question + answer more useful for future readers, please let me know!

Error in posting data to Rest API server with auth Token in android

I am trying to post data to REST API server with retrofit + RxJava . When I am trying to send data to server , it said " HTTP 500 Internal Server Error Occurred". But when the data is send with POSTMAN, it succeeded.
This is the function for sending data in Model.
// Encountering with 500 server error
fun postSchedule(data : ScheduleResponse , errorLD: MutableLiveData<String>){
Log.d("POST DATA", "${data.title} ${data.remindMeAt}" )
userClient.postScheduleItem(data)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.unsubscribeOn(Schedulers.io())
.subscribe(object : io.reactivex.Observer<ServerResponse>{
override fun onComplete() {
}
override fun onSubscribe(d: Disposable) {
}
override fun onNext(t: ServerResponse) {
errorLD.value = t.status
}
override fun onError(e: Throwable) {
errorLD.value = e.message
}
})
}
This is my API interface
#Headers("Accept: application/json")
#POST("schedules")
fun postScheduleItem(#Body data: ScheduleResponse): Observable<ServerResponse>
This is the retrofit client.
val httpLoggingInterceptor = HttpLoggingInterceptor().setLevel(HttpLoggingInterceptor.Level.BODY)
val httpClient = OkHttpClient.Builder()
var dbInstance: TodoDB = TodoDB.getInstance(context)
var rxJavaAdapter = RxJava2CallAdapterFactory.createWithScheduler(Schedulers.io())
val retrofitBuilder =
Retrofit.Builder()
.baseUrl(AppConstants.BASE_URL)
.addConverterFactory(GsonConverterFactory.create())
.addCallAdapterFactory(rxJavaAdapter)
fun <T> createService(serviceClass: Class<T>, authToken: String?): T {
if (!TextUtils.isEmpty(authToken)) {
val interceptor = AuthenticationInterceptor(authToken!!)
if (!httpClient.interceptors().contains(interceptor)) {
httpClient.addInterceptor(interceptor)
retrofitBuilder.client(httpClient.build())
}
}
return retrofitBuilder.build().create(serviceClass)
}
Please help me with this.Thank you.
Client side code is not enough to determine what causes the server to respond with 500. The best you can do is start debugging the issue.
There are several directions you can go:
If you have access to the server or know someone who does, you could debug the server and determine what causes the Internal server error. Maybe the server logs can help as well and you don't have to actually step through the server code.
If you don't have access to the server, you could look at the body of the server response. Maybe there's a detailed error description there in html, json or some other format that will help you find out the root cause.
If the above steps don't help then it's very useful that you know the request works with POSTMAN. You can compare the exact POSTMAN request with the exact Retrofit request, header by header,
line by line. To do that, you should first add your httpLoggingInterceptor to your okhttp client builder with
val httpClient = OkHttpClient.Builder().addNetworkInterceptor(httpLoggingInterceptor)
and look for the request log in logcat.
If you spot the differences between the working and the not working requests, then you should work your way through all the differences, and adjust the retrofit request by adding or modifying headers using okhttp interceptors so that, at the end, the retrofit request looks exactly the same as the POSTMAN request. I suggest you remove the AuthenticationInterceptor at first and simulate it "manually" with a custom interceptor and a hard coded auth token.
Retry the request every time you eliminate a difference to isolate the cause of the internal server error.
Hope this helps!

Retrofit Adding tag to the original request object

I'm trying to solve a problem where I'll be making a couple of asynchronous calls and based on the original request, I'm performing a task. To solve this issue, I'm trying to add a TAG to each request and then on successful response, I can get the tag and take action based on the tag. Here, I'm using TAG only to identify the original request.
Problem
Before calling the enqueue method, I'm setting the tag to the original request. But when I get the response in the successful callback, I'm getting different tag that I didn't set. Somehow the request object itself is coming as the tag object there. I'm not sure, how???
Please check the code below-
GitHubService gitHubService = GitHubService.retrofit.create(GitHubService.class);
final Call<List<Contributor>> call = gitHubService.repoContributors("square", "retrofit");
// Set the string tag to the original request object.
call.request().newBuilder().tag("hello").build();
call.enqueue(new Callback<List<Contributor>>() {
#Override
public void onResponse(Call<List<Contributor>> call, Response<List<Contributor>> response) {
Log.d("tag", response.raw().request().tag().toString());
// I'm getting Request{method=GET, url=https://api.github.com/repos/square/retrofit/contributors, tag=null} as the value of the tag. WHY????
final TextView textView = (TextView) findViewById(R.id.textView);
textView.setText(response.body().toString());
}
#Override
public void onFailure(Call<List<Contributor>> call, Throwable t) {
final TextView textView = (TextView) findViewById(R.id.textView);
textView.setText("Something went wrong: " + t.getMessage());
}
});
Can somebody point out that what exactly I'm doing wrong here. Any help would be appreciated.
For me this code is working
val CLIENT: OkHttpClient = OkHttpClient.Builder().apply {
addInterceptor(TagInterceptor())
}.build()
val SERVER_API: ServerApi = Retrofit.Builder()
.client(CLIENT)
.baseUrl(BASE_URL)
.build()
.create(ServerApi::class.java)
interface ServerApi {
#GET("api/notifications")
#Tag("notifications")
suspend fun getNotifications(): ResponseBody
}
#Target(AnnotationTarget.FUNCTION, AnnotationTarget.PROPERTY_GETTER, AnnotationTarget.PROPERTY_SETTER)
#Retention(AnnotationRetention.RUNTIME)
annotation class Tag(val value: String)
internal class TagInterceptor : Interceptor {
override fun intercept(chain: Interceptor.Chain): Response {
val request = chain.request()
val builder = request.newBuilder()
request.tag(Invocation::class.java)?.let {
it.method().getAnnotation(Tag::class.java)?.let { tag ->
builder.tag(tag.value)
}
}
return chain.proceed(builder.build())
}
}
Then cancel by tag
fun OkHttpClient.cancelAll(tag: String) {
for (call in dispatcher().queuedCalls()) {
if (tag == call.request().tag()) {
call.cancel()
}
}
for (call in dispatcher().runningCalls()) {
if (tag == call.request().tag()) {
call.cancel()
}
}
}
CLIENT.cancelAll("notifications")
This solution is clearly a hack, but it works.
Let's say you create your Retrofit service like this :
public <S> S createService(Class<S> serviceClass) {
// Could be a simple "new"
Retrofit.Builder retrofitBuilder = getRetrofitBuilder(baseUrl);
// Could be a simple "new"
OkHttpClient.Builder httpClientBuilder = getOkHttpClientBuilder();
// Build your OkHttp client
OkHttpClient httpClient = httpClientBuilder.build();
Retrofit retrofit = retrofitBuilder.client(httpClient).build();
return retrofit.create(serviceClass);
}
You will need to add a new CallFactory to your Retrofit instance, so it adds a tag every-time. Since the tag will be read-only, we will use an array of Object containing only one element, which you will be able to change later on.
Retrofit retrofit = retrofitBuilder.client(httpClient).callFactory(new Call.Factory() {
#Override
public Call newCall(Request request) {
request = request.newBuilder().tag(new Object[]{null}).build();
Call call = httpClient.newCall(request);
// We set the element to the call, to (at least) keep some consistency
// If you want to only have Strings, create a String array and put the default value to null;
((Object[])request.tag())[0] = call;
return call;
}
}).build();
Now, after creating your call, you will be able to change the contents of your tag:
((Object[])call.request().tag())[0] = "hello";
The request already have tag on it . You can get it form this codeļ¼š
val invocation: Invocation? = call.request().tag(Invocation::class.java)
if (invocation != null) {
Timber.d("tag--${invocation.method().name}}-------${invocation.arguments()}")
}

Categories

Resources