TheAnimatrix TheAnimatrix - 2 months ago 20
Android Question

Retrofit calls are adding up

I have a button in my application for logging out the user's session , when it is clicked retrofit sends a basic POST request to the localhost and technically it is logged out , but if i click the logout button again the following happens:

1st time : one request goes through
2nd time click logout : 2 requests are made for logout at same time
3rd time click logout : 3 requests are made for logout

and so on ..

Honestly , this situation is very puzzling to me and yes i use retrofit with okhttp interceptors and some token management .

Note : - (Tinydb is a simple shared preference manager)

Code posted below:

ApiInterfacer.java - This is my api interface

@FormUrlEncoded
@POST("/api/token/logout")
Call<BasicResponse> LOG_OUT(@Field("guid") String guid);


ApiService.java - My Api service creator

public class ApiService {

public static final String baseurl = StaticVars.LOCALHOST;
public static OkHttpClient.Builder httpClientBuilder = new OkHttpClient.Builder();
private static Retrofit.Builder retrofitbuilder = new Retrofit.Builder().baseUrl(baseurl).addConverterFactory(GsonConverterFactory.create());

public static <S>S createService(Class<S> serviceClass , Context context) // service class is the REST class , ex : GithubClient
{

Retrofit retrofit = retrofitbuilder.build();
httpClientBuilder.interceptors().add(new ApiTokenInterceptor(context));
OkHttpClient client = httpClientBuilder.build();
retrofitbuilder.client(client);
return retrofit.create(serviceClass);

}

}


ApiTokenInterceptor.java

public class ApiTokenInterceptor implements Interceptor {

private final Context context;
public ApiTokenInterceptor(Context context){

this.context = context;
}

@Override
public Response intercept(Chain chain) throws IOException {

Log.i("INTERCEPT" , "FIRST " + chain.request().url());
TinyDB db = new TinyDB(context);
Request request = chain.request();
Request modifiedReq = request;
if(!db.getString("token").equals(""))
{
modifiedReq = request.newBuilder().addHeader("token" ,db.getString("token"))
.build();
}

Response response = chain.proceed(modifiedReq);
boolean unauthorized = response.code() == 401;
Log.i("INTERCEPT" , "SECOND - LOGOUT - PROCEED " + modifiedReq.url() + " " + unauthorized);
if(unauthorized){
response.body().close();
db.remove("token");
String refreshToken = db.getString("refken");

Request request1 = new Request.Builder()
.url(StaticVars.LOCALHOST + "/api/token")
.addHeader("refken" , refreshToken)
.get()
.build();
OkHttpClient client = new OkHttpClient();
Response response1 = client.newCall(request1).execute();
String response_string = response1.body().string();
Log.d("INTERCEPT" , "RESPONSE TOKEN STRING " + response_string);
try{
JSONObject object = new JSONObject(response_string);
String token = object.getString("token");
//set token to db
Log.d("TOKENB" , " "+token);
db.putString("token" , token);
}catch (JSONException e){
e.printStackTrace();
Log.d("INVALID RESPONSE" , " "+response_string);
db.putString("token" , "placeholder");
Log.i("INTERCEPT" , "AFINAL - LOGOUT - PROCEED " + modifiedReq.url());
return response;
}

modifiedReq = request.newBuilder().addHeader("token" , db.getString("token"))
.build();
Log.i("INTERCEPT" , "BFINAL - LOGOUT - PROCEED " + modifiedReq.url());
return chain.proceed(modifiedReq);
}

return response;

}


}


(Usage).java - usage takes place in onclicklistener of a button

retro.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(final View v) {


//call retrofit :)
final ProgressDialog pd = new ProgressDialog(v.getContext());
pd.setMessage("Loading...");
pd.setCancelable(false);
pd.setIndeterminate(true);
pd.show();
ApiInterfacer client = ApiService.createService(ApiInterfacer.class, v.getContext());
Call<BasicResponse> call = client.LOG_OUT(db.getString("guid"));
call.enqueue(new Callback<BasicResponse>() {
@Override
public void onResponse(Call<BasicResponse> call, Response<BasicResponse> response) {
Log.d("Response : string" , "" + response.message());
switch(response.code())
{
case 401:
Log.d("LOGOUT" , "Unauthorized : "+401);
break;
case 400:
Log.d("LOGOUT" , "Malformed Body : " + 400);
break;
case 404:
Log.d("LOGOUT" , "URL Not Found :" + 404);
break;
case 200:
Log.d("Message" ,""+ response.body().getMessage()+" "+response.body().getCode());
if(response.body().getCode().equals("LOGGEDOUT"))
{
db.remove("token");
db.remove("refken");
startActivity(new Intent(getActivity(), LoginActivity.class));
getActivity().finish();
}
break;
default:
BasicResponse resp = response.body();
if(resp==null)
{
Log.d("Error" , "Response is null : " + response.code());
}else {
Log.d("Code", "" + resp.getCode());
Log.d("Source", "" + resp.getSource());
Log.d("Message", "" + resp.getMessage());
Log.d("Error", "" + resp.getError());
}
}

if(!call.isCanceled()) {
Log.i("INTERCEPT" , "FORCE CANCEL");
call.cancel();
}
pd.cancel();
}

@Override
public void onFailure(Call<BasicResponse> call, Throwable t) {
t.printStackTrace();
pd.cancel();
//page divert intent
Toast.makeText(v.getContext() , "Could not connect to server" , Toast.LENGTH_SHORT).show();
Log.d("FAIL" , ""+t.getMessage());

if(!call.isCanceled()) {
Log.i("INTERCEPT" , "FORCE CANCEL");
call.cancel();
}
}
});

}
});


LOG : -

1st click : ( 0 * FIRST - Run at starting of interceptor ? interceptor not running ?? )
09-13 00:40:04.657 29600-29600/some.example.okhttp I/INTERCEPT: FORCE CANCEL

2nd click : ( 1 * FIRST - Run at starting of interceptor )
09-13 00:41:45.156 29600-31375/some.example.okhttp I/INTERCEPT: FIRST http://192.168.2.2/api/token/logout
09-13 00:41:45.166 29600-31375/some.example.okhttp I/INTERCEPT: SECOND - LOGOUT - PROCEED http://192.168.2.2/api/token/logout false
09-13 00:41:45.217 29600-29600/some.example.okhttp I/INTERCEPT: FORCE CANCEL

3rd click: ( 3 * FIRST )
09-13 00:41:54.364 29600-31375/some.example.okhttp I/INTERCEPT: FIRST http://192.168.2.2/api/token/logout
09-13 00:41:54.364 29600-31375/some.example.okhttp I/INTERCEPT: FIRST http://192.168.2.2/api/token/logout
09-13 00:41:54.370 29600-31375/some.example.okhttp I/INTERCEPT: SECOND - LOGOUT - PROCEED http://192.168.2.2/api/token/logout true
09-13 00:41:54.382 29600-31375/some.example.okhttp I/INTERCEPT: BFINAL - LOGOUT - PROCEED http://192.168.2.2/api/token/logout
09-13 00:41:54.431 29600-31375/some.example.okhttp I/INTERCEPT: SECOND - LOGOUT - PROCEED http://192.168.2.2/api/token/logout true
09-13 00:41:54.454 29600-31375/some.example.okhttp I/INTERCEPT: BFINAL - LOGOUT - PROCEED http://192.168.2.2/api/token/logout
09-13 00:41:54.454 29600-31375/some.example.okhttp I/INTERCEPT: FIRST http://192.168.2.2/api/token/logout
09-13 00:41:54.466 29600-31375/some.example.okhttp I/INTERCEPT: SECOND - LOGOUT - PROCEED http://192.168.2.2/api/token/logout true
09-13 00:41:54.503 29600-31375/some.example.okhttp I/INTERCEPT: BFINAL - LOGOUT - PROCEED http://192.168.2.2/api/token/logout
09-13 00:41:54.695 29600-29600/some.example.okhttp I/INTERCEPT: FORCE CANCEL

4th click: ( 7 * FIRST )
09-13 00:42:57.726 29600-32408/some.example.okhttp I/INTERCEPT: FIRST http://192.168.2.2/api/token/logout
09-13 00:42:57.726 29600-32408/some.example.okhttp I/INTERCEPT: FIRST http://192.168.2.2/api/token/logout
09-13 00:42:57.726 29600-32408/some.example.okhttp I/INTERCEPT: FIRST http://192.168.2.2/api/token/logout
09-13 00:42:57.731 29600-32408/some.example.okhttp I/INTERCEPT: SECOND - LOGOUT - PROCEED http://192.168.2.2/api/token/logout true
09-13 00:42:57.740 29600-32408/some.example.okhttp I/INTERCEPT: BFINAL - LOGOUT - PROCEED http://192.168.2.2/api/token/logout
09-13 00:42:57.779 29600-32408/some.example.okhttp I/INTERCEPT: SECOND - LOGOUT - PROCEED http://192.168.2.2/api/token/logout true
09-13 00:42:57.819 29600-32408/some.example.okhttp I/INTERCEPT: BFINAL - LOGOUT - PROCEED http://192.168.2.2/api/token/logout
09-13 00:42:57.819 29600-32408/some.example.okhttp I/INTERCEPT: FIRST http://192.168.2.2/api/token/logout
09-13 00:42:57.831 29600-32408/some.example.okhttp I/INTERCEPT: SECOND - LOGOUT - PROCEED http://192.168.2.2/api/token/logout true
09-13 00:42:57.865 29600-32408/some.example.okhttp I/INTERCEPT: BFINAL - LOGOUT - PROCEED http://192.168.2.2/api/token/logout
09-13 00:42:57.892 29600-32408/some.example.okhttp I/INTERCEPT: SECOND - LOGOUT - PROCEED http://192.168.2.2/api/token/logout true
09-13 00:42:57.942 29600-32408/some.example.okhttp I/INTERCEPT: BFINAL - LOGOUT - PROCEED http://192.168.2.2/api/token/logout
09-13 00:42:57.942 29600-32408/some.example.okhttp I/INTERCEPT: FIRST http://192.168.2.2/api/token/logout
09-13 00:42:57.942 29600-32408/some.example.okhttp I/INTERCEPT: FIRST http://192.168.2.2/api/token/logout
09-13 00:42:57.968 29600-32408/some.example.okhttp I/INTERCEPT: SECOND - LOGOUT - PROCEED http://192.168.2.2/api/token/logout true
09-13 00:42:58.006 29600-32408/some.example.okhttp I/INTERCEPT: BFINAL - LOGOUT - PROCEED http://192.168.2.2/api/token/logout
09-13 00:42:58.029 29600-32408/some.example.okhttp I/INTERCEPT: SECOND - LOGOUT - PROCEED http://192.168.2.2/api/token/logout true
09-13 00:42:58.053 29600-32408/some.example.okhttp I/INTERCEPT: BFINAL - LOGOUT - PROCEED http://192.168.2.2/api/token/logout
09-13 00:42:58.053 29600-32408/some.example.okhttp I/INTERCEPT: FIRST http://192.168.2.2/api/token/logout
09-13 00:42:58.086 29600-32408/some.example.okhttp I/INTERCEPT: SECOND - LOGOUT - PROCEED http://192.168.2.2/api/token/logout true
09-13 00:42:58.152 29600-32408/some.example.okhttp I/INTERCEPT: BFINAL - LOGOUT - PROCEED http://192.168.2.2/api/token/logout
09-13 00:42:58.245 29600-29600/some.example.okhttp I/INTERCEPT: FORCE CANCEL

Answer

Each time you create the Retrofit service, instanciate your Service again which might cause the issue.

try making a factory that return singleton of the service instead:

   private static AmazonServices amazonServices = null;
    private static PurpleServices purpleServices = null;

    static OkHttpClient.Builder httpClient = getUnsafeOkHttpClient();

    private static Retrofit.Builder builder = new Retrofit.Builder();

    public static <S> S createService(Class<S> serviceClass) {

        S mAPI = null;
        if (serviceClass.getSimpleName().equals("PurpleService")) {
            if (purpleServices == null) {
                purpleServices = (PurpleServices) createPurpleAPI(serviceClass);
            }
            mAPI = (S) purpleServices;
        } else if (serviceClass.getSimpleName().equals("AmazonServices")) {
            if (amazonServices == null) {
                amazonServices = (AmazonServices) createAmazonAPI(serviceClass);
            }
            mAPI = (S) amazonServices;
        }
        return mAPI;
    }

and you call it like this:

private AmazonServices amazonServices = RetrofitCreator.createService(AmazonServices.class);
Comments