Dagger2 where inject #Named #Provides in dependent module? - java

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

Related

Inject presenter into activity via Dagger

I want to know how to inject Presenter in activity using code Following are details
Following is error message:
Error:(12, 46) error: cannot find symbol class DaggerCategoryPresenterComponent
Error:(9, 46) error: cannot find symbol class DaggerNetComponent
Error:(18, 10) error: [Dagger/MissingBinding] com.headytest.android.category_listing.CategoryContract.CategoryPresenter cannot be provided without an #Provides-annotated method.
com.headytest.android.category_listing.CategoryContract.CategoryPresenter is injected at
com.headytest.android.MainActivity.categoryPresenter
com.headytest.android.MainActivity is injected at
com.headytest.android.dagger_component.NetComponent.inject(com.headytest.android.MainActivity)
Following are modules
#Module
public class NetworkModule {
String baseURL;
public NetworkModule(String baseURL) {
this.baseURL = baseURL;
}
#Provides
#Singleton
Cache provideHttpCache(Application application) {
int cacheSize = 10 * 1024 * 1024;
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) {
HttpLoggingInterceptor interceptor = new HttpLoggingInterceptor();
interceptor.setLevel(HttpLoggingInterceptor.Level.BODY);
OkHttpClient.Builder client = new OkHttpClient.Builder();
client.cache(cache);
client.addInterceptor(interceptor);
return client.build();
}
#Provides
#Singleton
Retrofit provideRetrofit(Gson gson, OkHttpClient okHttpClient) {
try {
return new Retrofit.Builder()
.addConverterFactory(GsonConverterFactory.create(gson))
.addCallAdapterFactory(RxJava2CallAdapterFactory.create())
.baseUrl(baseURL)
.client(okHttpClient)
.build();
} catch (Exception e) {
e.printStackTrace();
return new Retrofit.Builder()
.addConverterFactory(GsonConverterFactory.create(gson))
.addCallAdapterFactory(RxJava2CallAdapterFactory.create())
.baseUrl(Constants.BASE_URL)
.client(okHttpClient)
.build();
}
}
}
ApplicationModule
#Module
public class ApplicationModule {
Application application;
public ApplicationModule(Application application) {
this.application = application;
}
#Provides
#Singleton
Application providesApplication() {
return application;
}
}
CategoryContractModule
#Module
public class CategoryContractModule {
public CategoryContractModule() {
}
#Provides
#AScope
CategoryContract.CategoryPresenter providesCategoryPresenter(CategoryPresenterImpl categoryPresenter) {
return (CategoryContract.CategoryPresenter)categoryPresenter;
}
}
Following are components:
#Singleton
#Component(modules = {ApplicationModule.class, NetworkModule.class})
public interface NetComponent {
void inject(MainActivity activity);
}
CategoryPresenterComponent:
#AScope
#Component(dependencies = NetComponent.class, modules =
{CategoryContractModule.class})
public interface CategoryPresenterComponent {
void inject(MainActivity activity);
}
AScope
#Scope
#Retention(RetentionPolicy.RUNTIME)
public #interface AScope {
}
Following is the code in MainActivity:
#Inject
CategoryPresenter categoryPresenter;
#Inject
Retrofit retrofit;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
DaggerCategoryPresenterComponent.builder()
.categoryContractModule(new CategoryContractModule())
.netComponent(((App) getApplicationContext()).getNetComponent())
.build()
.inject(this);
}
CategoryPresenterImpl
public class CategoryPresenterImpl implements CategoryContract.CategoryPresenter {
#Inject
public CategoryPresenterImpl() {
}
#Override
public void onStart() {
}
#Override
public void onStop() {
}
#Override
public void getCategoryLiast() {
}
}
I assume current error will be easy to fix, because it very much looks like this issue.
You've instructed Dagger how to provide #AScope CategoryContract.CategoryPresenter but in activity you request Dagger to inject CategoryContract.CategoryPresenter. Dagger is confused at this point, because those two things do not match.
What you have to do, is simply add #AScope to categoryPresenter:
#Inject
#AScope
CategoryPresenter categoryPresenter;
I've checked out your project. The problem was in NetComponent.java:
#Singleton
#Component(modules = {ApplicationModule.class, NetworkModule.class})
public interface NetComponent {
void inject(MainActivity activity);
}
The issue is in line void inject(MainActivity activity). Why is this an issue? Because by the time you construct NetComponent in App, dagger analyzes MainActivity for #Inject annotated field and sees CategoryPresenter. This means, that at this point Dagger should find appropriate provider/binder method in order to be able to inject MainActivity as declared inside this interface.
But it cannot find such a provider/binder method, because it is declared in an another component - CategoryPresenterComponent and this component (NetComponent) is not connected with CategoryPresenterComponent anyhow (subcomponents or component dependencies), thus bindings are not exposed.
Simply removing that line will make your build successful:
#Singleton
#Component(modules = {ApplicationModule.class, NetworkModule.class})
public interface NetComponent {
}
For resolving "Error:cannot access Nullable" refer to this thread, which suggest applying compile 'com.google.code.findbugs:jsr305:3.0.2' to your gradle file.
After doing this you'll overcome that error message and will stumble on retrofit2.Retrofit cannot be provided without an #Inject constructor or an #Provides-annotated method, which has the same symptoms as CategoryPresenter had.
To overcome this issue you have to add a Retrofit provider method inside NetComponent (or to remove #Inject Retrofit retrofit from activity):
#Singleton
#Component(modules = {ApplicationModule.class, NetworkModule.class})
public interface NetComponent {
Retrofit providesRetrofit();
}
After doing this you'll be able to run the app, but you'll end up in a NPE in MainActivity, because of this line:
.categoryContractModule(new CategoryContractModule(retrofit.create(HeadyAPI.class)))
You are referring to retrofit object, which is not yet initialized.
Long story short, your initial question has mutated a couple of times and in fact you had a few problems in your code. Still you have a problem there, because you are trying to use retrofit in order to construct a component, that was supposed to inject the retrofit for you.

kotlin dagger retrofit field injection

When trying to inject field variables using dagger I'm getting null. Here are the files. Some are in Java and some in Kotlin
App.java
public class App extends DaggerApplication{
#Override
protected AndroidInjector<? extends DaggerApplication> applicationInjector() {
return DaggerAppComponent.builder().application(this).build();
}
}
AppComponent.kt
#Singleton
#Component(modules = arrayOf(
NetworkModule::class,
ApplicationModule::class,
AndroidSupportInjectionModule::class
))
interface AppComponent : AndroidInjector<TBApplication> {
#Component.Builder
interface Builder {
#BindsInstance
fun application(application: Application): AppComponent.Builder
fun build(): AppComponent
}
}
NetworkModule.kt
#Module
class NetworkModule {
#Provides
#Singleton
fun provideOkHttpClient(): OkHttpClient {
val builder = OkHttpClient.Builder();
if (BuildConfig.DEBUG) {
val interceptor = HttpLoggingInterceptor()
interceptor.setLevel(HttpLoggingInterceptor.Level.BODY)
builder.addInterceptor(interceptor).build()
}
return builder.build()
}
#Singleton
#Provides
fun provideRetrofit(client: OkHttpClient): Retrofit {
val retrofit = Retrofit.Builder()
.baseUrl(BaseApi.SITE_ENDPOINT)
.addConverterFactory(GsonConverterFactory.create())
.addCallAdapterFactory(RxJava2CallAdapterFactory.create())
.client(client)
.build();
return retrofit
}
}
// Repository where injection should be done
class Repository {
private var examsService: BlogExamsService
#Inject
var retrofit: Retrofit? = null
init {
// retrofit is null here
examsService = retrofit?.create(BlogExamsService::class.java)!!
}
}
Field injection won't work as you do not run inject() method.
To make it work with your approach you should call in you Repository class:
App.self.getComponent().inject(this)
Where:
self is static instance of your application
getComponent() public getter for ApplicationComponent
Though I would not recommend it in your case, it is a misuse of DI framework.
You should create RepositoryModule and #Provide instance of Repository the same as you have done with NetworkModule.
Change your Repository to:
class Repository {
private var examsService: BlogExamsService
#Inject
constructor(retrofit: Retrofit) {
examsService = retrofit.create(BlogExamsService::class.java)!!
}
}

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();

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

How to add dynamic base url in retrofit module with dagger2

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

Categories

Resources