Search code examples
androiddagger-2

Dagger2 - Best practice for passing an Object to a Module?


I'm trying to create a RetrofitModule. I have an interceptor that adds dynamic headers using a AuthHeader object. I'm unsure how I can pass this object to the module class.

@Module
public class RetrofitModule {

    private static final String TAG = "RetrofitModule";


    //Do I create a constructor here for the RetrofitModule that accepts an AuthHeader object?
    
    @Provides
    @Singleton
    RequestInterceptor providesRequestInterceptor(@NonNull AuthHeader authHeader) {
        return new RequestInterceptor(authHeader);
    }

    @Provides
    @Singleton
    OkHttpClient.Builder provideOkHttp() {
        return new OkHttpClient.Builder();
    }

    @Provides
    @Singleton
    Retrofit provideRetrofit(OkHttpClient.Builder okHttpClient, RequestInterceptor requestInterceptor) {
        okHttpClient.addInterceptor(requestInterceptor);

        HttpLoggingInterceptor logging = new HttpLoggingInterceptor().setLevel(
                HttpLoggingInterceptor.Level.BASIC
        );
        okHttpClient.addInterceptor(logging);

        return new Retrofit.Builder()
                .addConverterFactory(GsonConverterFactory.create())
                .baseUrl(Constants.ENDPOINT)
                .client(okHttpClient.build())
                .build();
    }

    @Provides
    @Singleton
    LoginService provideLoginServer(Retrofit retrofit) {
        return retrofit.create(LoginService.class);
    }

}

This is the AuthHeader object I want to pass to the module so that I can add dynamic headers to the network call:

public class AuthHeader {
    private String idToken;
    private String idClient;
    private String idEmail;

    public AuthHeader(String idToken, String idClient, String idEmail) {
        this.idToken = idToken;
        this.idClient = idClient;
        this.idEmail = idEmail;
    }

    public String getIdToken() {
        return idToken;
    }

    public String getIdClient() {
        return idClient;
    }

    public String getIdEmail() {
        return idEmail;
    }
}

Solution

  • based on your comment that you want to read the token from SharedPreferences, I provided a complete sample:

    In addition to your code I've created AuthInfoProvider that can read and provide authentication info. It's used instead of passing an AuthHeader object.

    class AuthInfoProvider {
        SharedPreferences preferences;
    
        public AuthInfoProvider(Context context) {
            preferences = context.getSharedPreferences("MyPreferences", Context.MODE_PRIVATE);
        }
    
        public String getToken() {
            return preferences.getString("MyToken", "");
        }
    }
    

    pass the AuthInfoProvider to interceptor:

    It retrieves the token for each API call.

    class RequestInterceptor implements Interceptor {
        private final AuthInfoProvider provider;
    
        public RequestInterceptor(AuthInfoProvider provider) {
            this.provider = provider;
        }
    
        @Override
        public Response intercept(Chain chain) throws IOException {
               Request request = chain.request().newBuilder()
                    .addHeader("Authentication", provider.getToken())
                    .build();
               return chain.proceed(request);
        }
    }
    

    Now we should pass it to our module

    @Module
    public class RetrofitModule {
    
        private static final String TAG = "RetrofitModule";
    
        @Provides
        @Singleton
        public static AuthInfoProvider providesAuthInfoProvider(Context context) {
            return new AuthInfoProvider(context);
        }
    
        @Provides
        @Singleton
        public static RequestInterceptor providesRequestInterceptor(@NonNull AuthInfoProvider authInfoProvider) {
            return new RequestInterceptor(authInfoProvider);
        }
    
        // other your methods
        // just make them static
    
    }
    

    and the component will be like:

    @Singleton
    @Component(modules = RetrofitModule.class)
    interface NetworkComponent {
    
        @Component.Builder
        interface Builder {
            Builder bindContext(@BindsInstance Context context);
    
            NetworkComponent build();
        }
    
        void inject(SampleFragment fragment);
    }
    

    and pass context to component and build it like this:

    DaggerNetworkComponent.builder()
            .bindContext(getContext())
            .build()
            .inject(this);