I am writing an app where I am using Dagger2 for dependency Injection. I am new to dagger. I am getting confused on how it works and I am not able to figure it out how to use this library. I have tried writing below modules and components with the help of examples in blogs. I have mentioned the purpose of that module. Please correct me if I am wrong.
App Module and Component : It should generate a singleton instance across the application and I want to use this instance to access some android resources
#Module
public class AppModule {
private BaseApplication baseApplication;
public AppModule(BaseApplication baseApplication){
this.baseApplication = baseApplication;
}
#Provides
#Singleton
BaseApplication providesApplication(){
return baseApplication;
}
}
#Singleton
#Component(modules = {AppModule.class})
public interface AppComponent {
BaseApplication getBaseApplication();
}
Instantiation of AppComponent
public class BaseApplication extends Application {
private AppComponent appComponent;
#Override
public void onCreate() {
super.onCreate();
appComponent = DaggerAppComponent.builder().appModule(new
AppModule(this)).build();
}
public AppComponent getAppComponent(){
return appComponent;
}
}
The above code works fine. I am getting problems in the below code
Location Module and Component : Is it Ok if I don't have a constructor
for the below module?
#Module
public class LocationModule {
#IntDef(
{LocationRequest.PRIORITY_BALANCED_POWER_ACCURACY,
LocationRequest.PRIORITY_HIGH_ACCURACY,
LocationRequest.PRIORITY_LOW_POWER,
LocationRequest.PRIORITY_NO_POWER})
#Retention(RetentionPolicy.SOURCE)
public #interface LocationPriority {}
#Provides
#Singleton
#Named("default")
LocationRequest providesLocationRequest(){
return new LocationRequest()
.setPriority(LocationRequest.PRIORITY_HIGH_ACCURACY)
.setInterval(Constants.LOCATION_UPDATE_INTERVAL_MS)
.setFastestInterval(Constants.LOCATION_UPDATE_FASTEST_INTERVAL_MS)
.setSmallestDisplacement(Constants.LOCATION_UPDATE_DISPLACEMENT_METERS);
}
#Provides
#Singleton
#Named("custom")
LocationRequest providesCustomLocationRequest(int interval, int
fastestInterval, #LocationPriority int priority, float updateDisplacement) {
return new LocationRequest()
.setPriority(priority)
.setInterval(interval)
.setFastestInterval(fastestInterval)
.setSmallestDisplacement(updateDisplacement);
}
#Provides
#Singleton
LocationSettingsRequest providesLocationSettingsRequest(LocationRequest
locationRequest){
return new LocationSettingsRequest.Builder()
.addLocationRequest(locationRequest)
.build();
}
#Provides
#Singleton
FusedLocationProviderClient providesFusedLocationClient(BaseApplication
baseApplication){
return LocationServices.getFusedLocationProviderClient(baseApplication);
}
#Provides
#Singleton
SettingsClient providesSettingsClient(BaseApplication baseApplication){
return LocationServices.getSettingsClient(baseApplication);
}
}
#Singleton
#Component(modules = {LocationModule.class, AppModule.class})
public interface LocationComponent {
**Do we Really need a method here?**
void inject(GetLocationUseCase getLocationUseCase);
}
I am getting the following error
error: cannot access Nullable class file for
javax.annotation.Nullable not found
after using #Inject in below class. If I remove #Inject the error goes away.
public final class GetLocationUseCase implements
UseCaseContract.BusinessLogic {
UseCaseContract.Mediator mediator;
#Inject
FusedLocationProviderClient fusedLocationProviderClientl;
#Override
public void onInitialize() {
BaseApplication application = mediator.requestUserContext();
DaggerLocationComponent.builder().appModule(new
AppModule(application)).locationModule(new
LocationModule()).build().inject(this);
}
Why am I getting that error? Any help would be appreciated. Thanks in advance.
You need to add missing modules in your Component ..
For example, you will get this error if you remove NetModule from below
#Component(modules = {AppModule.class, NetModule.class, MainModule.class})
public interface AppComponent {
...
In case someone like me out there. I forgot to add #Provides annotation to the Dagger Module.
In my case I had a class with #Inject annotated constructor which extended another 3rd party class which had #Nullable annotation on one of the method parameters. Once I removed this extension everything started to work normally.
After wasting about 1 day on this issue, I found out that I need to temporarily add findbugs dependency like this:
implementation 'com.google.code.findbugs:jsr305:3.0.2'
Then dagger will print the actual error.
Related
I'm using Dagger 2 for DI in my MVP application. The application has different services which are responsible for making requests like getting product data or doing an order.
Most of the time each screen has his own #Component interface and #Module class and in these modules the corresponding presenters are provided.
In some cases a service is only used by one presenter. In this case I just #Provide it in that module. But I have a lot of services which are used multiple times in different presenters. At the moment I have put them all in the ApplicationModule, expose them in the ApplicationComponent and add a dependency to ApplicationComponent in the 'screen components' if their presenters need one of the services. Now the problem is, that almost every component is depending on ApplicationComponent, which I think is wrong.
How can I handle this the best way? Should I create a module for each service and add it to the components that need it? I also want to keep scoping in mind and think the services should be added to the #Singleton scope.
ApplicationComponent
#Singleton
#Component(modules = {ApplicationModule.class, ContextModule.class}
public interface ApplicationComponent {
void inject(MyApplication application);
Context getContext();
ProductDataService getProductDataService();
AnalyticsService getAnalyticsService();
CartService getCartService();
...
}
ApplicationModule
#Module
public class ApplicationModule {
#Provides
static ProductDataService provideProductDataService(ProductDataServiceImpl productDataService) {
return productDataService;
}
#Provides
static AnalyticsService provideAnalyticsService(AnalyticsServiceImpl analyticsService) {
return analyticsService;
}
#Provides
static CartService provideCartService(CartServiceImpl cartService) {
return cartService;
}
...
}
ProductDataServiceImpl
#Singleton
public class ProductDataServiceImpl implements ProductDataService {
private WebService webService;
#Inject
public ProductDataServiceImpl(WebService webService) {
this.webService = webService;
}
...
}
DependentComponent
#Component(modules = DependentModule.class, dependencies = ApplicationComponent.class)
public interface DependentComponent {
void inject(MyFragment fragment)
}
DependentModule
#Module
public class DependentModule {
private MyView myView;
public DependentModule(MyView myView) {
this.myView = myView;
}
#Provides
public MyPresenter provideMyPresenter(ProductDataService productDataService, AnalyticsService analyticsService) {
return new MyPresenterImpl(myView, productDataService, analyticsService)
}
}
Using Dagger 2.11, the following code returns "Error:[dagger.android.AndroidInjector.inject(T)] Found a dependency cycle:". This is happening because the provideApp method has the argument "App app". If I remove it and do a dirty hack that provides the application instance directly, the code compiles and works.
From examples I've seen before, it used to be common practice to keep an instance of the application in the module and using that for other providers, but since this module class is now abstract, this doesn't work as the #Provide methods need to be static.
How can I solve this?
#Module(includes = AndroidInjectionModule.class)
public abstract class AppModule {
#Provides
#Singleton
static App provideApp(App app) {
return app;
}
#Provides
static Authenticator provideAuthenticator(App app) {
return new AuthenticatorImpl(app);
}
}
EDIT:
What I need to achieve is basically this behaviour:
#Module(includes = AndroidInjectionModule.class)
public class AppModule {
private App application;
AppModule(App app) {
application = app;
}
#Provides
Authenticator provideAuthenticator() {
return new AuthenticatorImpl(application);
}
}
However, this doesn't work since the AppModule is now an abstract class and the app wouldn't compile if I use #Provides Authenticator provideAuthenticator() without the static keyword. If I use the static keyword, then I can't access the non-static application field.
Your code:
#Provides
#Singleton
static App provideApp(App app) {
return app;
}
means "provide App using App as a dependency". In other words, you want to provide App but you need App first. Hence your cycle.
The best solution to this problem is to follow the Rule of Demeter and not to provide the Application singleton at all.
Why does your AuthenticatorImpl need to know the concrete type of your Application subclass? I bet you can refactor that class so that Application is no longer a direct dependency for it - perhaps you merely need a Context. If you merely need singletons at app-scope, you can simply make a module with #Singleton #Provides methods and install it in your application component.
If, after all this, you find you must #Provide your subclass you can create a new module that doesn't include any others:
#Module //don't include other modules here
class AppModule {
private final App app;
public AppModule(App app) {
this.app = app;
}
#Provides
#Singleton
App app() {
return app;
}
}
And install it in your app-level component:
//inside your App class
DaggerAppComponent.builder()
.appModule(new AppModule(this))
.build()
.inject(this);
You would not need the AppModule if you implemented the following in your Application:
public class App extends DaggerApplication {
private AndroidInjector<App> appInjector;
#dagger.Component(modules = {
AndroidSupportInjectionModule.class,
AppModule.class
})
public interface Component extends AndroidInjector<App> {
#dagger.Component.Builder
abstract class Builder extends AndroidInjector.Builder<App> {}
}
#Override
protected AndroidInjector<? extends DaggerApplication> applicationInjector() {
return DaggerApp_Component
.builder()
.username("Username from application")
.create(this);
}
}
Now App will be a dependency on the global scope and can be used in every module linked to the #Component.
I've started to setup Dagger 2 and faced a strange issue that looks like a bug to me.
I have 1 main component and 1 subcomponent which I 'plus' in parent component.
I use:
compile "com.google.dagger:dagger:2.4"
apt "com.google.dagger:dagger-compiler:2.4"
annotationProcessor "com.google.dagger:dagger-compiler:2.4"
Application component is prety easy. It just 'plus' the subcomponent:
#Singleton #Component(modules = { AristocratsAppModule.class })
public interface AristocratsAppComponent {
StreamComponent plus(StreamModule module);
}
Application module also quite basic. It provides application level dependancies:
#Module public class AristocratsAppModule {
private static AristocratsApp app;
public AristocratsAppModule(AristocratsApp app) {
AristocratsAppModule.app = app;
}
#Provides #Singleton AristocratsApp providesApplication() {
return app;
}
#Provides #Singleton GsonXml providesGsonXml() {
return ...
}
#Provides #Singleton #Named("aristocrats") Retrofit providesAristocratsRetrofit() {
return ...
}
#Provides #Singleton #Named("last_fm") Retrofit providesLastFmRetrofit() {
return ...
}
}
Subcomponent has two StreamServices (Retrofit services) and simple inject:
#PerController #Subcomponent(modules = { StreamModule.class }) public interface StreamComponent {
void inject(StreamsController streamsController);
AristocratsStreamService providesAristocratsStreamService();
LastFmStreamService providesLastFmStreamService();
}
Module of subcomponent provides presenter injection and two different injections of Retrofit services:
#Module public class StreamModule {
private StreamsController mView;
public StreamModule(StreamsController view) {
mView = view;
}
#Provides #PerController StreamMvp.Presenter providesPresenter(StreamCase streamCase,
StreamModelMapper streamMapper) {
return new StreamPresenter(mView, streamCase, streamMapper);
}
#Provides #PerController AristocratsStreamService providesAristocratsStreamService(
#Named("aristocrats") Retrofit retrofit) {
return retrofit.create(AristocratsStreamService.class);
}
#Provides #PerController LastFmStreamService providesLastFmStreamService(
#Named("last_fm") Retrofit retrofit) {
return retrofit.create(LastFmStreamService.class);
}
}
Application injection that I call in onCreate() of my app class:
mAppComponent = DaggerAristocratsAppComponent.builder()
.aristocratsAppModule(new AristocratsAppModule(this))
.build();
View injection that I call in my controller:
getAppComponent().plus(new StreamModule(this)).inject(this);
Exception that I'm getting during build:
Error:(13, 8) error: [com.qsoft.streams.presentation.di.StreamComponent.inject(com.qsoft.streams.presentation.StreamsController)] com.qsoft.streams.data.network.AristocratsStreamService cannot be provided without an #Provides-annotated method.
com.qsoft.streams.data.network.AristocratsStreamService is injected at
com.qsoft.streams.data.network.StreamApi.<init>(…, streamServiceAristocrats, …)
com.qsoft.streams.data.network.StreamApi is injected at
com.qsoft.streams.data.repository.StreamRepository.<init>(streamApi, …)
com.qsoft.streams.data.repository.StreamRepository is injected at
com.qsoft.streams.domain.interactors.StreamCase.<init>(streamRepository)
com.qsoft.streams.domain.interactors.StreamCase is injected at
com.qsoft.streams.presentation.di.StreamModule.providesPresenter(streamCase, …)
com.qsoft.streams.presentation.StreamMvp.Presenter is injected at
com.qsoft.streams.presentation.StreamsController.mPresenter
com.qsoft.streams.presentation.StreamsController is injected at
com.qsoft.streams.presentation.di.StreamComponent.inject(streamsController)
A binding with matching key exists in component: com.qsoft.streams.presentation.di.StreamComponent
Error complaints about missing #Provides-annotated method but they are inside my child module. Seems that Dagger2 just does not see them. I was thinking that the problem could be in the missing declaration of the AristocratsStreamService in subcomponent, so I even added it there but nothing changed.
This situation looks very strange and I would like to hear some inputs from more experienced Dagger2 developers.
Thank you in advance!
I have noticed that you did not set subcomponents part in AristrocratsModule.
This:
#Module public class AristocratsAppModule {
into this:
#Module(subcomponents = {StreamComponent.class})
public class AristocratsAppModule {
Try to make the following changes. In your StreamComponent create a Builder.
#Subcomponent.Builder
interface Builder {
Builder streamModule(StreamModule module);
StreamComponent build();
}
Eventually you will be injecting everything via StreamComponent, and it will inject everything in AristocratsAppComponent as well.
public StreamComponent getStreamComponent(Controller controller) {
return getAppComponent().streamBuilder().streamModule(new StreamModule(controller)).build();
}
Plus thing can be removed after this.
Hello I am new to Dagger2.
Goal. Take my Networking DI and MVP DI. MVP as in the presenter for an an activity that extends base activity. I want to combine all this into one super module and place this into my base activity. Over time add more presenters.
I do not want 30+ inject statements in my baseActivity.
I am following this example but it is too simple compared to what I am trying to do.
I think the issue is with injecting the API at the base activity. For some reason Dagger is looking for Api in my MVP class.. So that would be a dependency graph issue?
Having spent more time on this.. The issue stems from Mvp's interface of baseActivity or any sub activity that extends baseActivity. That means when it goes to inject, it sees the #inject Api call, and cannot find it. It will work if I add Api to this module, but thats upside down of what I want. I want Component / Module for Application level items. I then want a component / module that has all my different MVP component in one module.. It's like Dagger starts looking for dependencies in the leaf of a tree and getting upset when it doesn't see whats in the root. I need it to go the other way. Be satisfied that I injected the dependency in the Root activity.
Base Activity...
#inject
public ApiClient mClient;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
mManager = new SharedPreferencesManager(this);
DaggerInjector.get().inject(this);
}
DaggerInjector
public class DaggerInjector {
private static AppComponent appComponent = DaggerAppComponent.builder().appModule(new AppModule()).build();
public static AppComponent get() {
return appComponent;
}
}
#Component(modules = {AppModule.class, ApiModule.class, MvpModule.class})
#Singleton
public interface AppComponent {
void inject(BaseActivity activity);
}
Api
#Singleton
#Component(modules = {ApiModule.class})
public interface ApiComponent {
void inject( BaseActivity activity);
}
#Module
public class ApiModule {
#Provides
#Singleton
public ApiClient getApiClient(){
return new ApiClient();
}
}
Mvp
#Singleton
#Component(modules = {MvpModule.class})
public interface MvpComponent {
void inject(BaseActivity activity);
}
#Module
public class MvpModule {
#Provides
#Singleton
public MvpPresenter getMvpPresenter(){ return new MvpPresenter();}
}
Error:(16, 10) error: ApiClient cannot be provided without an #Inject constructor or from an #Provides- or #Produces-annotated method. This type supports members injection but cannot be implicitly provided.
ApiClient is injected at
...BaseActivity.ApiClient
...BaseActivity is injected at
MvpComponent.inject(activity)
I found out my problem. I needed to use a subcomponent.
#Singleton
#Subcomponent(modules = {MvpModule.class})
public interface MvpComponent {
void inject(BaseActivity activity);
}
#Module
public class MvpModule {
#Provides
#Singleton
public MvpPresenter getMvpPresenter(){ return new MvpPresenter();}
}
see
Dagger- Should we create each component and module for each Activity/ Fragment
Dagger2 activity scope, how many modules/components do i need?
I have the following simple module:
#Module
public class ApplicationModule {
private CustomApplication customApplication;
public ApplicationModule(CustomApplication customApplication) {
this.customApplication = customApplication;
}
#Provides #Singleton CustomApplication provideCustomApplication() {
return this.customApplication;
}
#Provides #Singleton #ForApplication Context provideApplicationContext() {
return this.customApplication;
}
}
And the respective simple component:
#Singleton
#Component(
modules = ApplicationModule.class
)
public interface ApplicationComponent {
CustomApplication getCustomApplication();
Context getApplicationContext();
}
And I'm creating the component here:
public class CustomApplication extends Application {
...
private ApplicationComponent component;
#Override
protected void attachBaseContext(Context base) {
super.attachBaseContext(base);
MultiDex.install(this);
}
#Override
public void onCreate() {
super.onCreate();
component = DaggerApplicationComponent.builder()
.applicationModule(new ApplicationModule(this))
.build();
It throws this error at compile time: Error:(22, 13) error: android.content.Context cannot be provided without an #Provides-annotated method, but as you can see it is annotated with #Provides.
It's really strange because the problem goes away when I take the qualifier annotation off.
Just in case, this is my #ForApplication qualifier:
#Qualifier #Retention(RUNTIME)
public #interface ForApplication {
}
This is practically a textbook Dagger2 example. What am I doing wrong?
After quite a while of trial and error I've seem to found the cause, it's the ambiguity of Context because #ForApplication is missing in some places where Context is needed.
Also it may be my frail understanding of Dagger2 at the moment, but this boilerplate is quite prone to developer errors.
Anyhow... for anyone that finds the problem you just have to add the qualifier annotations in every place that dependency is used:
#Singleton
#Component(
modules = ApplicationModule.class
)
public interface ApplicationComponent {
CustomApplication getCustomApplication();
#ForApplication Context getApplicationContext();
}