Java – How can I avoid waiting on the main thread when adding headers to requests using transformation?

How can I avoid waiting on the main thread when adding headers to requests using transformation?… here is a solution to the problem.

How can I avoid waiting on the main thread when adding headers to requests using transformation?

I use it to configure my makeover :

 RestAdapter restAdapter = new RestAdapter.Builder()

add headers to requests
            .setRequestInterceptor(getAuthenticatedRequestInterceptor())
            .setEndpoint(BASE_URL)
            .setConverter(new GsonConverter(getGson()))
            .build();

And the getAuthenticatedRequestInterceptor() method adds header to the request:

public AccountRequestInterceptor getAuthenticatedRequestInterceptor() {
    AccountRequestInterceptor interceptor = new AccountRequestInterceptor();
    Map<String, String> headers = new HashMap<>();
     String accessToken = null;
    try {
        accessToken = TokenProvider.getInstance(mContext).getToken();
    } catch (InterruptedException e) {

}
    headers.put(HeadersContract.HEADER_AUTHONRIZATION, O_AUTH_AUTHENTICATION + accessToken);
    interceptor.setHeader(headers);
    return interceptor;
}

getToken() is:

private synchronized string getToken() throws InterruptedException {
    if (!isRefreshing()) {
        This is very important to call notify() on the same object that we call wait();
        final TokenProvider myInstance = this;
        setRefreshing(true);

MyApplication.getRestClient().getAccountService().getRefreshedToken(mLoginData.getRefreshToken())
                .subscribe(new Observer<LoginResponse>() {
                    @Override
                    public void onCompleted() {
                        synchronized (myInstance) {
                            setRefreshing(false);
                            myInstance.notifyAll();
                        }
                    }

@Override
                    public void onError(Throwable e) {
                        synchronized (myInstance) {
                            setRefreshing(false);
                            myInstance.notifyAll();
                        }
                    }

@Override
                    public void onNext(LoginResponse loginResponse) {
                        synchronized (myInstance) {
                            mLoginData = loginResponse;
                            mAccountProvider.saveLoginData(loginResponse);
                            myInstance.notifyAll();
                        }
                    }
                });
    }
    this.wait();
    return mLoginData.getToken();
}

TokenProvider.getInstance(mContext).getToken() has a wait() on the main thread to get the response to the async method, I know this is a bad thing, but I need to wait here for the response to get the token from it and then return the token. How do I do this in a separate thread to avoid waiting on the main thread?

Note:

1 – Call it before any request with a makeover.

2 – I read this I know I can refresh tokens after a failed request, but for business reasons I want to avoid invalid tokens.

3 – I call MyApplication.getRestClient().getAccountService().login(loginRequest,callback...) in my activity and call everything before adding the token takes place in a background thread. So I want to use my token without blocking the main thread.

Update: I added the following interceptor to my new OkHttp:

public class RequestTokenInterceptor implements Interceptor {
    @Override
    public Response intercept(Interceptor.Chain chain) throws IOException {
        Request request = chain.request();
        Request newRequest;
        try {
            Log.d("addHeader", "Before");
            String token = TokenProvider.getInstance(mContext).getToken();
            if (token != null) {
                newRequest = request.newBuilder()
                        .addHeader("Bearer", token)
                        .build();
            } else {
                 I want to cancel the request or raise an exception to catch it in onError method
                 of retrofit callback.
            }
        } catch (InterruptedException e) {
            Log.d("addHeader", "Error");
            e.printStackTrace();
            return chain.proceed(request);
        }
        Log.d("addHeader", "after");
        return chain.proceed(newRequest);
    }
}

Now, if the token is empty, how do I cancel the request or throw an exception to catch it in the onError method of the improved callback?

Solution

It’s a bit of a weird question, but let me try to help you. 🙂

As you know, you can use a response interceptor to remodel the token after a failed request.

Let’s try to use an interceptor before the request.

public class RequestTokenInterceptor implements Interceptor {
   @Override
   public Response intercept(Chain chain) throws IOException {
      Request request = chain.request();
       Here where we'll try to refresh token.
       with an retrofit call
       After we succeed we'll proceed our request
      Response response = chain.proceed(request);
      return response;
   }
}

When you create your API, create a new HttpClient:

OkHttpClient client = new OkHttpClient();
client.interceptors().add(new RequestTokenInterceptor());

Then add your HTTP client to your adapter as follows:

.setClient(new OkClient(client))

If this works, you will first try to refresh the token before each request and then continue with your API request. So there is no difference in the UI with your normal API calls.

Edit:

I’m also editing my answers. If you want to return an error in other cases, you can create a custom response if token is empty:

private Response(Builder builder) {
    this.request = builder.request;
    this.protocol = builder.protocol;
    this.code = builder.code;
    this.message = builder.message;
    this.handshake = builder.handshake;
    this.headers = builder.headers.build();
    this.body = builder.body;
    this.networkResponse = builder.networkResponse;
    this.cacheResponse = builder.cacheResponse;
    this.priorResponse = builder.priorResponse;
  }

Or you can simply return an empty response. If you build a custom response and set the code to something other than 200 (for example, 401 or 400+), you will receive that response in Retrofit’s callback failure method. Than you can do whatever you want.

If you return null, I think you’ll get a RuntimeException, and you can still catch the response in the callback’s failed method.

After you create your own response in else, you can create a custom callback and capture your empty response and transform your custom error the way you want, as follows:

public abstract class DefaultRequestCallback<T> implements Callback<T> {

public abstract void failure(YourCustomException ex);

public abstract void success(T responseBean);

@Override
    public void success(T baseResponseBean, Response response) {
        if (response == null) {
             Here we catch null response and transform it to our custom     Exception
            failure(new YourCustomException());
        }
        } else {
            success(baseResponseBean);
        }
    }

@Override
    public void failure(RetrofitError error) {
         Here's your failure method.
         Also you can transform default retrofit errors to your customerrors
        YourCustomException ex = new YourCustomException();
        failure(ex);
    }
}

I think this helps you.

Edit 2:

You can build a new response like this. There is a builder pattern in Retrofit’s Response class. You can check it out from there.

Response response = new Response.Builder().setCode(401).setMessage("Error Message").build();

Related Problems and Solutions