Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

OkHttp Interceptor using OkHttpClient without dependency cycle

I'm using Retrofit and Dagger 2. I have implemented an OkHttp Interceptor for adding the oauth token. In the case there is no oauth token or the timestamp is invalid, I request a new one (via the Retrofit service) before the actual request is carried out.

This creates a dependency cycle where the Retrofit service requires the Interceptor but the Interceptor also requires the Retrofit service (for retrieving the oauth token).

Example for the Interceptor (for simplification it always requests the token via restService#refreshAccessToken):

@Override
public Response intercept(Chain chain) throws IOException {
    Request originalRequest = chain.request();
    Request.Builder requestBuilder = originalRequest.newBuilder();

    String authHeader = "Bearer " + restService.refreshAccessToken();
    requestBuilder.addHeader("Authorization", authHeader);
    return chain.proceed(requestBuilder.build());
}
like image 297
Maradox Avatar asked Nov 10 '15 14:11

Maradox


2 Answers

Your Interceptor will need to inject itself in the Interceptor#intercept() method. This way your Retrofit service (including the OkHttpClient with the added interceptor) dependency can be satisfied without a dependency cycle.

My NetworkComponent (which provides my Retrofit service) lives in my Application class. Here is an example of injecting it using an Application Context.

public class AuthInterceptor implements Interceptor {

    @Inject RestService restService;

    public AuthInterceptor(Context context) {
        mContext = context;
    }

    @Override
    public Response intercept(Chain chain) throws IOException {
        // Inject Retrofit service
        ((MyApplication) context.getApplicationContext())
                .getNetworkComponent().inject(this);

        Request originalRequest = chain.request();
        Request.Builder requestBuilder = originalRequest.newBuilder();

        String authHeader = "Bearer " + restService.refreshAccessToken();
        requestBuilder.addHeader("Authorization", authHeader);
        return chain.proceed(requestBuilder.build());
    }
}

You could also just set a local variable without injecting the entire class, possibly getting better performance.

RestService restService = InjectHelper.getNetworkComponent(mContext).restService();
like image 165
Austyn Mahoney Avatar answered Sep 23 '22 10:09

Austyn Mahoney


I personally prefer Lazy injection, which is a bit cleaner

public class AuthInterceptor implements Interceptor {

    Lazy<RestService> restService;

    @Inject
    public AuthInterceptor(Lazy<RestService> restService) {
        this.restService = restService;
    }

    @Override
    public Response intercept(Chain chain) throws IOException {
        Request originalRequest = chain.request();
        Request.Builder requestBuilder = originalRequest.newBuilder();

        String authHeader = "Bearer " + restService.get().refreshAccessToken();
        requestBuilder.addHeader("Authorization", authHeader);
        return chain.proceed(requestBuilder.build());
    }
}
like image 20
Szymon Klimaszewski Avatar answered Sep 21 '22 10:09

Szymon Klimaszewski