How to add dynamic base url in retrofit module with dagger2 - java

I am using dagger2 for my application. I have one module which provides some dependencies like Retrofit, Gson etc.
NetModule.java
#Module
public class NetModule {
private String mBaseUrl;
public NetModule(String baseUrl) {
this.mBaseUrl = baseUrl;
}
#Provides
#Singleton
SharedPreferences providesSharedPreferences(Application application) {
return PreferenceManager.getDefaultSharedPreferences(application);
}
#Provides
#Singleton
Cache provideOkHttpCache(Application application) {
int cacheSize = 10 * 1024 * 1024; // 10 MiB
Cache cache = new Cache(application.getCacheDir(), cacheSize);
return cache;
}
#Provides
#Singleton
Gson provideGson() {
GsonBuilder gsonBuilder = new GsonBuilder();
gsonBuilder.setFieldNamingPolicy(FieldNamingPolicy.LOWER_CASE_WITH_UNDERSCORES);
return gsonBuilder.create();
}
#Provides
#Singleton
OkHttpClient provideOkHttpClient(Cache cache) {
OkHttpClient okHttpClient = new OkHttpClient();
okHttpClient.newBuilder()
//.addNetworkInterceptor(REWRITE_CACHE_CONTROL_INTERCEPTOR)
.cache(cache)
.build();
return okHttpClient;
}
#Provides
#Singleton
Retrofit provideRetrofit(Gson gson, OkHttpClient okHttpClient) {
Retrofit retrofit = new Retrofit.Builder()
.addConverterFactory(GsonConverterFactory.create(gson))
.baseUrl(mBaseUrl)
.client(okHttpClient)
.build();
return retrofit;
}
}
NetComponent.java
#Singleton
#Component(modules = {AppModule.class, NetModule.class, Validator.class})
public interface NetComponent {
void inject(AuthenticationActivity authenticationActivity);
void inject(PaymentActivity paymentActivity);
}
AppApplication.java
#Override
public void onCreate() {
super.onCreate();
mNetComponent = DaggerNetComponent.builder()
.appModule(new AppModule(this))
.netModule(new NetModule("https://corporateapiprojectwar.mybluemix.net/corporate_banking/mybank/"))
.build();
}
This approach was working until I had only one base url for my complete application. Now I have different base Url for AuthenticationActivity and PaymentActivity so I can not send Url in constructor of NetModule in onCreate of Application
Can anyone help me how to add dynamic base Url of retrofit using dagger2.

You can use #Named annotation Dagger2 user guide (see 'Qualifiers' section'):
In your NetModule.java:
#Provides
#Singleton
#Named("authRetrofit")
public Retrofit provideAuthRetrofit() {
// setup retrofit for authentication
return retrofit;
}
#Provides
#Singleton
#Named("paymentRetrofit")
public Retrofit providePaymentRetrofit() {
// setup retrofit for payments
return retrofit;
}
In your AuthenticationActivity:
#Inject
#Named("authRetrofit")
Retrofit retrofit;
And finally in your PaymentActivity.java:
#Inject
#Named("paymentRetrofit")
Retrofit retrofit;
Then dagger shall automatically inject Retrofit configured for payments into PaymentActivity and Retrofit configured for authentication into AuthenticationActivity

Related

NPE while injecting Retrofit with Dagger

I'm using Dagger for DI and Retrofit for network.
When i trying to call retrofits api method in my presenter, i catchs NPE.
Here is Network module:
#Module
public class NetworkModule {
String baseUrl;
public NetworkModule(String baseUrl) {
this.baseUrl = baseUrl;
}
#Provides
#Singleton
Gson provideGson() {
GsonBuilder gsonBuilder = new GsonBuilder();
return gsonBuilder.create();
}
#Provides
#Singleton
Retrofit provideRetrofit(Gson gson, OkHttpClient client) {
return new Retrofit.Builder()
.addConverterFactory(GsonConverterFactory.create(gson))
.addCallAdapterFactory(RxJavaCallAdapterFactory.create())
.baseUrl(baseUrl)
.build();
}
#Provides
#Singleton
UsersApi provideUsersApi(Retrofit retrofit){
return retrofit.create(UsersApi.class);
}
}
And app component:
#Singleton
#Component(dependencies = {}, modules = {AppModule.class, DatabaseModule.class, NetworkModule.class})
public interface AppComponent {
void inject(UsersFragmentPresenter usersFragmentPresenter);
UserDao userDao();
Retrofit retrofit();
UsersApi usersApi();
AppDatabase appDatabase();
Application application();
}
And when i inject api and call api method here, the member isn't inizialized
#InjectViewState
public class UsersFragmentPresenter extends MvpPresenter<BaseFragmentView> {
#Inject
UsersApi usersApi; // is null
public UsersFragmentPresenter() {
App.getAppComponent().inject(this);
}
public void loadData(){
usersApi.getUser()
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(observer);
}

How do I inject dependencies into an Activity that require an argument from a previous Activity?

I have a LoginActivity where the user logs in via Auth0 and returns an auth token. This token is passed to MainActivity:
Intent intent = new Intent(LoginActivity.this, MainActivity.class);
intent.putExtra(KEY_ACCESS_TOKEN, credentials.getAccessToken());
intent.putExtra(KEY_ID_TOKEN, credentials.getIdToken());
startActivity(intent);
I was able to get dependency injection working with LoginActivity fine by following this guide.
Now I'm trying to inject dependencies into MainActivity. My MainActivity has a MainActivityViewModel to handle all the interactions between the UI and the data layer. I'd like to inject my API into my ViewModel:
PetshackApi apiService;
#Inject
public PetMapViewModel(PetshackApi apiService) {
this.apiService = apiService;
}
I have ViewModelModule, ViewModelKey, and MainActivityViewModelFactory (renamed from GithubViewModelFactory) defined. I injected the viewModelFactory at the top of my MainActivity:
#Inject
ViewModelProvider.Factory viewModelFactory;
And then use the factory to get my viewModel:
viewModel = ViewModelProviders.of(this, viewModelFactory).get(MainActivityViewModel.class);
I set this up using this answer.
The problem is that my Retrofit/PetshackApi dependency will require the accessToken from the LoginActivity. So I defined another method in my MainActivity to allow retrieving it:
public String getAccessToken() {
return getIntent().getStringExtra(LoginActivity.KEY_ACCESS_TOKEN);
}
I'm having trouble setting up my modules/components/???. I think I need to inject MainActivity somehow into my modules so I tried following Injecting Activity objects.
MainActivityComponent.java
#Component(modules={AndroidSupportInjectionModule.class, AppModule.class, MainActivityModule.class, ViewModelModule.class})
public interface MainActivityComponent extends AndroidInjector<PetApplication> {
#Component.Builder
abstract class Builder extends AndroidInjector.Builder<PetApplication>{
#BindsInstance
abstract Builder application(Application application);
}
void inject(MainActivity mainActivity);
}
MainActivityModule.java
#Module(subcomponents = MainActivitySubcomponent.class)
abstract class MainActivityModule {
#Binds
#IntoMap
#ActivityKey(MainActivity.class)
abstract AndroidInjector.Factory<? extends Activity>
bindMainActivityInjectorFactory(MainActivitySubcomponent.Builder builder);
}
MainActivitySubcomponent.java
#Subcomponent(modules = MainActivityChildModule.class)
public interface MainActivitySubcomponent extends AndroidInjector<MainActivity> {
#Subcomponent.Builder
public abstract class Builder extends AndroidInjector.Builder<MainActivity> {}
}
MainActivityChildModule.java
#Module
abstract class MainActivityChildModule {
#Provides
#Singleton
Retrofit providesRetrofit(Application application, MainActivity mainActivity) {
final String accessToken = mainActivity.getAccessToken();
Interceptor interceptor = new Interceptor() {
#Override
public okhttp3.Response intercept(Chain chain) throws IOException {
Request newRequest = chain.request().newBuilder()
.addHeader("authorization", "Bearer " + accessToken).build();
return chain.proceed(newRequest);
}
};
// Add the interceptor to OkHttpClient
OkHttpClient.Builder builder = new OkHttpClient.Builder();
builder.interceptors().add(interceptor);
OkHttpClient client = builder.build();
Retrofit retrofit = new Retrofit.Builder()
.baseUrl(application.getString(R.string.endpoint_url))
.addConverterFactory(GsonConverterFactory.create())
.client(client)
.build();
return retrofit;
}
#Provides
#Singleton // needs to be consistent with the component scope
PetshackApi providesPetshackApiInterface(Retrofit retrofit) {
return retrofit.create(PetshackApi.class);
}
}
Am I on the right track? Any hints or examples on how to do this?
I'd recommend moving your networking code outside of your Activity module and creating an Application module that could be shared across your application.
The important thing is, if you have a TokenStore that provides your token for each request you'd need to update the value as requests are sent.
#Module
abstract class NetworkModule {
#Provides
#Singleton
static TokenStore provideTokenStore(TokenStoreImpl tokenStore) {
return tokenStore;
}
#Provides
#Singleton
static OkHttpClient provideOkHttpClient(AuthInterceptor authInterceptor) {
// Add the interceptor to OkHttpClient
OkHttpClient.Builder builder = new OkHttpClient.Builder();
builder.interceptors().add(authInterceptor);
return builder.build();
}
#Provides
#Singleton
static Retrofit providesRetrofit(Application application, OkHttpClient okHttpClient) {
Retrofit retrofit = new Retrofit.Builder()
.baseUrl(application.getString(R.string.endpoint_url))
.addConverterFactory(GsonConverterFactory.create())
.client(okHttpClient)
.build();
return retrofit;
}
#Provides
#Singleton // needs to be consistent with the component scope
static PetshackApi providesPetshackApiInterface(Retrofit retrofit) {
return retrofit.create(PetshackApi.class);
}
}
interface TokenStore {
String getToken();
void setToken(String token);
}
#Singleton
class TokenStoreImpl implements TokenStore {
String token;
#Inject
public TokenStoreImpl() { }
#Override
public String getToken() {
return token;
}
#Override
public void setToken(String token) {
this.token = token;
}
}
#Singleton
class AuthInterceptor implements Interceptor {
private final TokenStore tokenStore;
#Inject
public AuthInterceptor(TokenStore tokenStore) {
this.tokenStore = tokenStore;
}
#Override
public Response intercept(Chain chain) throws IOException {
Request newRequest = chain.request()
.newBuilder().addHeader("authorization", "Bearer " + tokenStore.getToken()).build();
return chain.proceed(newRequest);
}
}

Provide two different instances of the same type

I have a Dagger module with two #Provides methods that construct different Retrofit instances. I also have two methods that each need to consume one of the Retrofit instances.
How do I tell Dagger which Retrofit I want to use in each of the consuming functions?
My code :
#Provides
#Singleton
public OkHttpClient provideOkHttpClient(){
final OkHttpClient.Builder builder = new OkHttpClient.Builder();
if (BuildConfig.DEBUG) {
HttpLoggingInterceptor logging = new HttpLoggingInterceptor();
logging.setLevel(HttpLoggingInterceptor.Level.BODY);
builder.addInterceptor(logging);
}
builder.connectTimeout(60 * 1000, TimeUnit.MILLISECONDS)
.readTimeout(60 * 1000, TimeUnit.MILLISECONDS);
return builder.build();
}
#Provides
#Singleton
public Retrofit provideRestAdapter1(Application application, OkHttpClient okHttpClient) {
Retrofit.Builder builder = new Retrofit.Builder();
builder.client(okHttpClient)
.baseUrl(application.getString(R.string.Endpoint1))
.addCallAdapterFactory(RxJava2CallAdapterFactory.create())
.addConverterFactory(GsonConverterFactory.create());
return builder.build();
}
#Provides
#Singleton
public Retrofit provideRestAdapter2(Application application, OkHttpClient okHttpClient) {
Retrofit.Builder builder = new Retrofit.Builder();
builder.client(okHttpClient)
.baseUrl(application.getString(R.string.Endpoint2))
.addCallAdapterFactory(RxJava2CallAdapterFactory.create())
.addConverterFactory(GsonConverterFactory.create());
return builder.build();
}
#Provides
#Singleton
public GithubApiService provideGithubApiService(Retrofit restAdapter) {
return restAdapter.create(GithubApiService.class);
}
#Provides
#Singleton
public GithubApiService2 provideGithubApiService(Retrofit restAdapter) {
return restAdapter.create(GithubApiService2.class);
}
}
You can use #Qualifier annotations to distinguish between the two.
First create a new annotation type (in its own java file of course):
#Qualifier
#Retention(RetentionPolicy.RUNTIME)
public #interface EndPoint1 {
}
Then annotate the relevant #Provides method:
#Provides
#Singleton
#EndPoint1
public Retrofit provideRestAdapter1(Application application, OkHttpClient okHttpClient) {
...
}
And then tell Retrofit to use this one in the other #Provides:
#Provides
#Singleton
public GithubApiService provideGithubApiService(#EndPoint1 Retrofit restAdapter) {
return restAdapter.create(GithubApiService.class);
}
You can also use #Named if you don't want to create you own annotations. See the documentation here.
You can also use name parameter
use this code
#Provides
#Singleton
#Named("Google")
Retrofit providePlaceApiClient(OkHttpClient client, Gson gson) {
return new Retrofit.Builder()
.baseUrl(BaseApiConfig.getPlaceApiUrl())
.client(client)
.addConverterFactory(GsonConverterFactory.create(gson))
.addCallAdapterFactory(RxJavaCallAdapterFactory.create())
.build();
}
#Provides
#Singleton
Retrofit provideRetrofit(OkHttpClient client, Gson gson) {
return new Retrofit.Builder()
.baseUrl(BaseApiConfig.getBaseUrl())
.client(client)
.addConverterFactory(GsonConverterFactory.create(gson))
.addCallAdapterFactory(RxJavaCallAdapterFactory.create())
.build();
}
You can get this using named annotation with inject annotation.
#Inject
#Named("Google")
Retrofit retrofit
Also, you can add on your component for child referance
#Named("Google")
Retrofit providePlaceApiClient();

Dagger2 where inject #Named #Provides in dependent module?

I use dagger2 demo by https://guides.codepath.com/android/Dependency-Injection-with-Dagger-2.
I want to use cached and non_cached retrofit call. I create in NetModule.java
#Provides #Named("cached")
#Singleton
OkHttpClient provideOkHttpClient(Cache cache) {
OkHttpClient okHttpClient = new OkHttpClient.Builder()
.cache(cache)
.build();
return okHttpClient;
}
#Provides #Named("non_cached")
#Singleton
OkHttpClient provideOkHttpClientNonCached() {
OkHttpClient okHttpClient = new OkHttpClient.Builder()
.build();
return okHttpClient;
}
GitHubModule.java is dependent on NetModule.java.
my GitHubComponent.java
#UserScope
#Component(dependencies = NetComponent.class, modules = GitHubModule.class)
public interface GitHubComponent {
void inject(DemoDaggerActivity activity);
}
my NetComponent.java
#Singleton
#Component(modules={ApplicationModule.class, NetModule.class})
public interface NetComponent {
// downstream components need these exposed
Retrofit retrofit();
OkHttpClient okHttpClient();
SharedPreferences sharedPreferences();
}
In my DemoDaggerActivity.java I inject retrofit:
#Inject #Named("cached")
OkHttpClient mOkHttpClient;
#Inject
Retrofit mRetrofit;
After rebuild project I get error:
Where can I tell to dagger, that i want to use cached or non_cached retrofit?
Your Retrofit provider should use #Named annotation for OkHttpClient, for example:
#Provides
#Singleton
public Retrofit provideRetrofit(#Named("cached") OkHttpClient okHttpClient)
{
return new Retrofit.Builder()
.baseUrl("...")
.addConverterFactory(GsonConverterFactory.create())
.client(okHttpClient)
.build();
}
You have two methods with same name: provideOkHttpClient(). Rename one of them, make them distinct.
If you are using kotlin, the correct way to inject named is next:
#field:[Inject Named("api1")].
Source: https://medium.com/#WindRider/correct-usage-of-dagger-2-named-annotation-in-kotlin-8ab17ced6928

How to send activity instance to module in a constructor in dagger2

I am using dagger2 in my application. I have created module, component which is being in my entire application so I am initializing it in the application class.
Below is my module, component of dagger2 which are helping for resolving dependencies.
NetComponent.java
#Singleton
#Component(modules = {AppModule.class, NetModule.class})
public interface NetComponent {
void inject(AuthenticationActivity authenticationActivity);
void inject(PaymentActivity paymentActivity);
}
AppModule.java
#Module
public class AppModule {
private Application application;
public AppModule(Application application) {
this.application = application;
}
#Provides
#Singleton
Application providesApplication() {
return application;
}
}
NetModule.java
#Module
public class NetModule {
#Provides
#Singleton
SharedPreferences providesSharedPreferences(Application application) {
return PreferenceManager.getDefaultSharedPreferences(application);
}
#Provides
#Singleton
Cache provideOkHttpCache(Application application) {
int cacheSize = 10 * 1024 * 1024; // 10 MiB
Cache cache = new Cache(application.getCacheDir(), cacheSize);
return cache;
}
#Provides
#Singleton
Gson provideGson() {
GsonBuilder gsonBuilder = new GsonBuilder();
gsonBuilder.setFieldNamingPolicy(FieldNamingPolicy.LOWER_CASE_WITH_UNDERSCORES);
return gsonBuilder.create();
}
#Provides
#Singleton
OkHttpClient provideOkHttpClient(Cache cache) {
OkHttpClient okHttpClient = new OkHttpClient();
okHttpClient.newBuilder()
//.addNetworkInterceptor(REWRITE_CACHE_CONTROL_INTERCEPTOR)
.cache(cache)
.build();
return okHttpClient;
}
#Provides
#Singleton
#Named("authRetrofit")
Retrofit provideAuthRetrofit(Gson gson, OkHttpClient okHttpClient) {
Retrofit retrofit = new Retrofit.Builder()
.addConverterFactory(GsonConverterFactory.create(gson))
.baseUrl(PAYMENT_SERVICE)
.client(okHttpClient)
.build();
return retrofit;
}
#Provides
#Singleton
#Named("paymentRetrofit")
Retrofit providePaymentRetrofit(Gson gson, OkHttpClient okHttpClient) {
Retrofit retrofit = new Retrofit.Builder()
.addConverterFactory(GsonConverterFactory.create(gson))
.baseUrl(LOGIN_SERVICE)
.client(okHttpClient)
.build();
return retrofit;
}
}
AppApplication.java
public class AppApplication extends Application {
private NetComponent mNetComponent;
#Override
public void onCreate() {
super.onCreate();
mNetComponent = DaggerNetComponent.builder()
.appModule(new AppModule(this))
.build();
}
public NetComponent getmNetComponent() {
return mNetComponent;
}
}
Validator.java
#Module
public class Validator {
#Provides
com.mobsandgeeks.saripaar.Validator providesValidator(Application application) {
return new com.mobsandgeeks.saripaar.Validator(application);
}
}
I want to pass activity instance to the constructor of Validator in which I am using it.
Suppose I want to inject Validator in MainActivity.java then constructor should have MainActivity instance.
What approach should I take for this ? Should I initialize the dagger dependency in an activity for this and Do I need to create a new component for this ?
You can simply create constructor for your ValidatorModule:
#Module
public class Validator {
private final Activity activity;
public Validator(Activity activity) {
this.activity = activity;
}
#Provides
com.mobsandgeeks.saripaar.Validator providesValidator() {
return new com.mobsandgeeks.saripaar.Validator(activity);
}
}
Let me know if it is what you are looking for

Categories

Resources