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.
Related
How does an Android application in one gradle module include dagger #Modules defined in an other Android Library (.aar)?
I'm trying to have a generic library that provides a set of functionality defined thru dagger #Module's able to have have them injected into Applications that depend on that library
#Singleton
#Component(modules = {
AndroidSupportInjectionModule.class,
LibraryAppModule.class
})
public interface LibraryComponent extends AndroidInjector<DaggerApplication> {
#Component.Builder
interface Builder {
#BindsInstance
Builder application(Application application);
LibraryComponent build();
}
void inject(LibraryApplication app);
#Override
void inject(DaggerApplication instance);
}
#Module
public class LibraryAppModule {
#Provides
#Singleton
Context provideContext(Application application) {
return application;
}
#Singleton
#Provides
ServiceFoo provideServiceFoo(Context context) {
return new ServiceFoo(context);
}
#Singleton
#Provides
ServiceBar provideServiceBar(Context context, ServiceFoo serviceFoo) {
return new ServiceBar(context, serviceFoo);
}
}
Now in the Application that has a dependency for this library (build.gradle) I've tried the following:
implementation project(":Library")
Application using the library
#Singleton
#Component(modules = {
AndroidSupportInjectionModule.class,
LibraryAppModule.class
})
public interface ExampleApplicationComponent extends AndroidInjector<DaggerApplication> {
#Component.Builder
interface Builder {
#BindsInstance
Builder application(Application application);
ExampleApplicationComponent build();
}
void inject(ExampleApplication app);
#Override
void inject(DaggerApplication instance);
}
This is an example of the place where I'm getting the issue (a null object). This is in a separate module that depends on the library.
public class MainActivity extends AppCompatActivity {
#Inject
ServiceFoo serviceFoo;
#Inject
ServiceBar serviceBar;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
serviceFoo.downloadImages();
serviceBar.uploadImages();
}
}
I expect to see the LibraryAppModule's services to be available in my Activities using #Inject. What am I doing wrong?
In order to resolve the issue 2 things needed to be done.
The MainActivity had to extend DaggerAppCompatActivity (this ensures during onCreate() for our Activity that inject() is called, but for this to work there needs to be an AndroidInjector for the Activity.
This is accomplished by using #ContributesAndroidInjector within a module that I defined and installed into the ExampleApplicationComponent
#Module
public abstract class ExampleApplicationActivityBuilder {
#ContributesAndroidInjector
abstract MainActivity bindMainActivity();
}
Now within the MainActivity, the Services I want injected from the library module are non-null.
Here's is a git commit that shows the difference implementing this fix.
https://github.com/PeregrineFalcon/DaggerExampleLibrary/commit/1ebfdecfa3684c7ac124b9f6a4cecd23712a74fd
Hope this helps someone else who trying to do a multi-module Dagger Android project with #Modules provided through the library.
A class that wants a property injected into it should call inject()!
Since you want serviceFoo injected in MainActivity, you should call inject() from onCreate(). However, where is inject() defined for you to just call it? Hence, you additionally declare inject() in ExampleApplicationComponent.
All that said, it looks like your understanding of dagger is incomplete. Recommend this blog post.
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 used dagger2 in my project, but the injection field is always null. Here is the code.
sorry, my english is poor.
Thanks in advance.
Module
#Module
public class RetrofitModule {
#Provides
#Singleton
Retrofit provideRetrofit() {
return new Retrofit.Builder().build();
}
}
Component
#Component(modules = RetrofitModule.class)
public interface RetrofitComponent {
void inject(Activity activity);
}
And in MainActivity, I write this
DaggerRetrofitComponent.builder().build().inject(this);
But the Retrofit is always null. How can I solve it?
You can not inject this way to your Activity class!
change your component like this and specify the exact name of your Activity:
#Component(modules = RetrofitModule.class)
public interface RetrofitComponent {
void inject(MainActivity activity);
}
and then perhaps also you have to change your module like this or anything else that fit your need:
#Module
public class RetrofitModule {
#Provides
Retrofit provideRetrofit() {
return new Retrofit.Builder().baseUrl("http://google.com").build();
}
}
By the way, make sure you have written #Inject before Retrofit declaration in your activity:
#Inject
Retrofit retrofit;
note that: if you want to have singleton provide in your module, the
whole component cannot remain unstopped and it must be annotated
#Singleton.
I hope it helps :)
I have Singleton scoped module that provides some standard singletons: Application, DB services, etc.
But for Activity I have separate module that should create Presenter for he Activity and I need to pass Application context to it. However I get following error when trying to compile the project:
Error:(13, 1) error: xxx.SplashComponent scoped with #xxx.ViewScope may not reference bindings with different scopes:
#Provides #Singleton xxx.ApplicationModule.provideAppContext()
Here is snippet of my Application module:
#Singleton
#Module
public class ApplicationModule {
private Application app;
public ApplicationModule(Application app) {
this.app = app;
}
#Provides
#Singleton
#Named("ui")
Scheduler provideUIScheduler() {
return AndroidSchedulers.mainThread();
}
#Provides
#Singleton
#Named("io")
Scheduler provideIOScheduler() {
return Schedulers.io();
}
#Provides
#Singleton
Application provideApplication() {
return app;
}
#Provides
#Singleton
Context provideAppContext() {
return app;
}
}
And here is Activity module and Component:
#Module
public class SplashModule {
private final FragmentManager fragmentManager;
public SplashModule(FragmentManager fragmentManager) {
this.fragmentManager = fragmentManager;
}
#Provides
#ViewScope
Presenter getPresenter(Context context) {
return new SplashPresenter(context, fragmentManager);
}
}
Component:
#ViewScope
#Component(modules = {SplashModule.class, ApplicationModule.class})
public interface SplashComponent {
void inject(SplashActivity activity);
}
What am I doing wrong?
What am I doing wrong?
This:
#ViewScope
#Component(modules = {SplashModule.class /*View scoped*/,
ApplicationModule.class/*Singleton scoped*/})
You can only include unscoped or modules scoped with the same scope in your components. You will need to use more than one component.
To include the dependencies from your application, you need to have them in a different component, e.g. ApplicationComponent. If yo do this, you have 2 options: either declare SplashComponent as a SubComponent of ApplicationComponent or add ApplicationComponent as a dependency to your component. If you add it as a dependency, be sure to also provide methods in your ApplicationComponent, so that it can access the dependencies.
e.g. if you were to use component dependencies:
#Component(modules = {ApplicationModule.class})
public interface ApplicationComponent {
void inject(MyApplication app);
// todo: also add getters for your other dependencies you need further down the graph
Application getApplication();
}
#Component(modules = {SplashModule.class}, dependencies={ApplicationComponent.class})
public interface SplashComponent {
// as before
}
I want to explain some key points of Dagger 2 from my understanding.
Main actors:
"Component" is the bridge between modules and places where injection happens.
"Module" is the place where we declare our objects which will be injected.
"Scope" is like the life-time of related injection story.
How does it work?
Declare component with a scope ("Singleton").
Define modules that can inject the required objects in the component's modules list.
void inject(BaseFragment baseFragment);
*******Expose the provided objects in the component's module to sub components****
DbHelper dbHelper();
Declare module and provides objects to be injected via component.
Ex:
#Singleton
#Component(modules = { ApplicationModule.class, NetworkModule.class })
public interface ApplicationComponent {
void inject(BaseActivity baseActivity);
DbHelper dbHelper();
}
#PerService #Component(dependencies = ApplicationComponent.class, modules = ServiceModule.class)
public interface ServiceComponent {
void inject(SyncService service);
}
// SyncService.java
#Inject DbHelper dbHelper; (even Singleton scoped)
private void setupInjector() {
ServiceComponent mServiceComponent = DaggerServiceComponent.builder()
.applicationComponent(getApplicationComponent())
.serviceModule(new ServiceModule(this))
.build();
mServiceComponent.inject(this);
}
ok then...
You can inject both unscoped and (Singleton and PerService) scoped objects to your SyncService.class
Scoping rules:
When a type is marked with a scope annotation, it can only be used by Components that are annotated with the same scope.
When a Component is marked with a scope annotation, it can only provide types with that annotation or types that have no annotation.
A subcomponent cannot use a scope annotation used by one of its parent Components.
Components also involve subcomponents in this context.
Caution:- Modules that use a scope annotation can only be used in components that are annotated with the same scope. Check Here.
For more information on Dagger in Android go through this training and practise it here.
I've started using Dagger 2 and faced strange issue that looks like a bug to me.
I have 3 modules, that are composed into one subcomponent, which in turn extends/pluses higher level component.
Subcomponent is pretty simple: just combination of modules and a single injection point:
#Singleton
#Subcomponent(
modules = {
NavigationDrawerModule.class,
NavigationListModule.class,
SwitcherModule.class
}
)
public interface NavigationDrawerComponent {
NavigationDrawerFragment inject(NavigationDrawerFragment object);
}
First modules looks like this - it provides general fragment-level dependencies:
#Module
public class NavigationDrawerModule {
private final Activity activity;
private final View rootView;
private final LoaderManager loaderManager;
public NavigationDrawerModule(Activity activity, View rootView, LoaderManager loaderManager) {
this.activity = activity;
this.rootView = rootView;
this.loaderManager = loaderManager;
}
#Provides #Singleton EventBus provideLocalBus() {
return EventBus.builder().build();
}
#Provides #Singleton View provideViewRoot() {
return rootView;
}
#Provides #Singleton LoaderManager provideLoaderManager() {
return loaderManager;
}
#Provides #Singleton Context provideContext() {
return activity;
}
}
Second module looks like this - it provides presenter/controller and their dependencies for a subset of UI on screen:
#Module
public class SwitcherModule {
#Provides SwitchController provideSwitcherController(SwitchControllerImpl impl) {
return impl;
}
#Provides SwitcherView provideSwitcherView(SwitcherViewImpl impl) {
return impl;
}
}
Third module - another presenter/controller for a subset of UI:
#Module
public class NavigationListModule {
#Provides #Singleton NavigationListController provideNavigationListController(NavigationListControllerImpl impl) {
return impl;
}
#Provides #Singleton NavigationListView provideNavigationListView(NavigationListViewImpl impl) {
return impl;
}
}
Relevant part of the fragment that is being injected:
#Inject SwitchController identitySwitchController;
#Inject SwitcherView identitySwitcherView;
#Inject NavigationListController navigationListController;
#Inject NavigationListView navigationListView;
NavigationListControllerImpl implements the following constructor:
#Inject
public NavigationListControllerImpl(Context ctx, EventBus bus) {
this.ctx = ctx;
this.bus = bus;
}
Error I'm getting from the Dagger 2 compiler is the following:
error: ...sidenavigation.navigationlist.NavigationListControllerImpl cannot be provided without an #Inject constructor or from an #Provides-annotated method.
...sidenavigation.NavigationDrawerFragment.navigationListController
[injected field of type: ...sidenavigation.navigationlist.NavigationListController navigationListController]
...sidenavigation.navigationlist.NavigationListModule.provideNavigationListController(...sidenavigation.navigationlist.NavigationListControllerImpl impl)
[parameter: ...sidenavigation.navigationlist.NavigationListControllerImpl impl]
Error complains about missing #Inject-annotated constructor, but it exists! If I replace implicit NavigationListControllerImpl instance creation (passing via #Provides-method parameter) with explicit (with new), dagger starts complaining about the same error but now for the presenter object which is the second entry in the same module, and so on.
All this situation looks very strange, and I'd like to hear some input from more experienced Dagger 2 users (and developers?).
Thank you in advance!
I got this same error because I forgot to expose the objects provided by the modules in the parent component to the other components that are depend on it.
Parent component example:
#Singleton
#Component(modules = {AppModule.class})
public interface AppComponent {
AppPref exposeAppPref(); /* my issue was caused by forgot this line,
the method name doesn't matter, what matters is the object type AppPref provided in the AppModule
that you want it to be available in the component that declares this component as one of its dependencies*/
}
Sample component that makes the above component as a dependency
#UserScope
#Component (dependencies = {AppComponent.class})
public interface ActivityComponent {
void inject(MainActivity activity);
}
Update:
AppModule:
...
#Provides
#Singleton
AppPref provideAppPref() {
return appPref;
}
...
The GlobalComponent and the subcomponent NavigationDrawerComponent must have different scopes. Use #Singleton for your GlobalComponent and some another scope for the subcomponent.
Otherwise, if you apply the same scope to the GlobalComponent and to the subcomponent, you must declare the modules of your subcomponent in your global component as well:
#Component(
// modules from subcomponent must be declared here also
modules = {NavigationListModule.class,
SwitcherModule.class,
NavigationDrawerModule.class,
...}
)
#Singleton
public interface GlobalComponent {
NavigationDrawerComponent plus(NavigationDrawerModule module);
}
For your use case, you can also use component dependencies. For instance:
#Component(
dependencies = GlobalComponent.class,
modules = {NavigationListModule.class,
SwitcherModule.class,
NavigationDrawerModule.class}
)
#YourOtherDaggerScope // #Singleton scope won't work here, it must be a different scope
public interface NavigationDrawerComponent extends GlobalComponent { // extend the parent component if you wish to get access to parent dependencies
NavigationDrawerFragment inject(NavigationDrawerFragment object);
}
Seems like I've figured out what was wrong with my Dagger 2 setup. It's not possible to use the same scope in both component and subcomponents. It's required to define a new scope for subcomponent. In my case I've ended up creating #Screen scope for me subcomponent.
I'd say that this is a small but very annoying defect in Dagger 2. Apparently dagger-compiler reports nice and understandable error about the same scopes in a parent component and child component if child component is extended with a parent component as dependency. But completely misleading error is reported by the compiler if parent component and child subcomponent share the same scope.
Thank you, #lukas, for giving me a hint here https://stackoverflow.com/a/30383088/808313 that led to a problem resolution.
Came accross this issue today too. For me there was a problem with the Annotation processing (on Android Studio 2.2 with gradle 2.x).
Instead of ~~apt~~ I used annotationProcessor
I used
annotationProcessor 'com.google.dagger:dagger-compiler:2.6'
and now It's working.
Came across the same issue while trying to create Subcomponents, but it seems to be fixed in Dagger 2.0.1.
Seems it is the same kinda error dagger reports for many mistakes. In my case, my target injection was expecting concrete class (Presenter) where as the module that provides presenter was returning only the interface (DemoContract.Presenter)
So changed from
#Inject
public Presenter mDemoPresenter;
to
#Inject
public DemoContract.Presenter mDemoPresenter;
and module that provides presenter looks like this:
#Module
public class DiDemoPresenterModule {
private final DemoContract.View mView;
DiDemoPresenterModule(MainActivity mView) {
this.mView = mView;
}
#Provides
public DemoContract.Presenter providesDemoPresenter(Repository repository) {
return new DemoPresenter(repository, mView);
}
}