Dagger 2: Unable to inject singleton in other scope - android

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.

Related

Dagger 2 How to create a Module for Base Activity Components and a separate Module for all MVP components

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?

Dagger2 and Android

I am trying to implement Dagger Dependency Injection into my app but I am having a hard time understanding how it works, especially coming from Spring where DI was much easier and much more declarative.
What I want to do is have a bunch of inject-ready objects that can be used throughout my app, that is the SharedPreferences, the Network objects (OkHttp, Retrofit, Picasso...), and EventBus and a SchedulerProvider object for RxJava.
This sample seems to offer everything I need but I am having trouble grasping some concepts.
In this other sample referenced in the previous page they create a GithubService that uses the Retrofit object provided in the NetModule. For that they create a GithubComponent like this:
#UserScope
#Component(dependencies = NetComponent.class, modules = GitHubModule.class)
public interface GitHubComponent {
void inject(MainActivity activity);
}
They are using a UserScope annotation that defines its own scope. Since #Singleton cannot be used, does this mean that the object will not be a Singleton? How do scopes really affect the DI? It seems they are only declaring a named-scope with no more effect, but I'm not sure.
Also, my app is built using Activities with Fragments. Do I have to create a Component for every Fragment in my app? i.e. I need to use my REST api services all throughout the app, do I have to declare a Component for every screen using them? This raises the amount of boilerplate code required and thus sounds not very clean.
Components ought to be the "big DI provider" that provides everything for a specific scope.
For example, you could have a SingletonComponent with #Singleton scope that has every single module added to it that has at least one #Singleton scoped provider method.
#Singleton
#Component(modules={NetworkingModule.class, DatabaseModule.class, MapperModule.class, UtilsModule.class})
public interface SingletonComponent {
// provision methods
OkHttpClient okHttpClient();
RealmHolder realmHolder();
// etc.
}
You can have the provision methods defined per module.
public interface DatabaseComponent {
RealmHolder realmHolder();
}
public interface NetworkingComponent{
OkHttpClient okHttpClient();
}
In which case you'd have
#Singleton
#Component(modules={NetworkingModule.class, DatabaseModule.class, MapperModule.class, UtilsModule.class})
public interface SingletonComponent
extends NetworkingComponent, DatabaseComponent, MapperComponent, UtilsComponent {
// provision methods inherited
}
In a Module, you can specify a factory method ("provider method") that specifies how to create a particular type of dependency.
For example,
#Module
public class NetworkingModule {
#Provides
#Singleton
OkHttpClient okHttpClient() {
return new OkHttpClient.Builder()./*...*/.build();
}
#Provides
#Singleton
Retrofit retrofit(OkHttpClient okHttpClient) {
// ...
}
}
You can imagine the #Singleton scope as the big DI container that Spring would have given you.
You can also provide instances of the class using #Inject annotated constructor. This can receive any class from the component that is able to instantiate it from provider methods within that scoped component's modules (and unscoped dependencies, of course).
#Singleton
public class MyMapper {
#Inject
public MyMapper(RealmHolder realmHolder, OkHttpClient okHttpClient) { // totally random constructor for demo
}
}
or
#Singleton
public class MyMapper {
#Inject
RealmHolder realmHolder;
#Inject
OkHttpClient okHttpClient;
#Inject
public MyMapper() {
}
}
Then this will be available in the component, you can even make provision method for it to make it inheritable in component dependencies:
#Singleton
#Component(modules={...})
public interface SingletonComponent {
MyMapper myMapper();
}
With Dagger2, you can also additionally create "subscoped components", that inherit all dependencies provided from a component of a given scope.
For example, you can inherit all #Singleton scoped components, but you can still have new scoped dependencies per that new scope, such as #ActivityScope.
#Scope
#Retention(RetentionPolicy.RUNTIME)
public #interface ActivityScope {
}
Then, you can create subscoped components using either subcomponents, or component dependencies.
Subcomponent:
.
#ActivityScope
#Subcomponent(modules={MainActivityModule.class})
public interface MainActivityComponent {
MainPresenter mainPresenter();
}
Then this can be created in its parent scoped component:
#Singleton
#Component(modules={...})
public interface SingletonComponent {
MainActivityComponent mainActivityComponent(MainActivityModule module);
}
Then you can use the singleton component to instantiate this:
SingletonComponent singletonComponent = DaggerSingletonComponent.create();
MainActivityComponent mainActivityComponent = singletonComponent.mainActivityComponent(new MainActivityModule(mainActivityHolder));
Component dependency:
.
#ActivityScope
#Component(dependencies={SingletonComponent.class}, modules={MainActivityModule.class})
public interface MainActivityComponent extends SingletonComponent {
MainPresenter mainPresenter();
}
For this to work, you must specify provision methods in the superscoped component.
Then you can instantiate this like so:
SingletonComponent singletonComponent = DaggerSingletonComponent.create();
MainActivityComponent mainActivityComponent = DaggerMainActivityComponent.builder()
.singletonComponent(singletonComponent)
.mainActivityModule(new MainActivityModule(mainActivityHolder))
.build();
In Dagger2, you can therefore obtain dependencies either via:
#Inject annotated constructor parameters
#Inject annotated fields on classes with #Inject annotated constructor
from #Component provision methods
via manual field injection method defined in component (for classes you can't create using #Inject annotated constructor)
Manual field injection can happen for classes like MainActivity, that you yourself don't create.
Manual field injection injects only the specific class that you are injecting. Base-classes don't get automatically injected, they need to call .inject(this) on component.
It works like this:
#ActivityScope
#Subcomponent(modules={MainActivityModule.class})
public interface MainActivityComponent {
void inject(MainActivity mainActivity);
}
Then you can do:
public class MainActivity extends AppCompatActivity {
#Override
public void onCreate(Bundle bundle) {
super.onCreate(bundle);
MainActivityComponent mainActivityComponent = DaggerMainActivityComponent.builder()
.singletonComponent(getSingletonComponent())
.mainActivityModule(new MainActivityModule(this))
.build(); // ensure activity `holder` instead, and retain component in retained fragment or `non-configuration instance`
mainActivityComponent.inject(this);
}
}
1)
Since #Singleton cannot be used, does this mean that the object will
not be a Singleton?
GitHubComponent component has #UserScope scope, it should be #Singleton if you want to declare Singletons in this scope.
2)
How do scopes really affect the DI? It seems they are only declaring a
named-scope with no more effect, but I'm not sure.
From javax docs
A scope annotation applies to a class containing an injectable
constructor and governs how the injector reuses instances of the type.
By default, if no scope annotation is present, the injector creates an
instance (by injecting the type's constructor), uses the instance for
one injection, and then forgets it. If a scope annotation is present,
the injector may retain the instance for possible reuse in a later
injection.
3)
Do I have to create a Component for every Fragment in my app?
You can create it once, instantiate and store in your Application object and then query it everytime you need to inject something if your fragment or activity.
Check this example

How inject the Activity to object that is being injected to the Activity?

I have this module:
#Module
public class UserProfileModule {
#Provides
#Singleton
UserProfileController providesUserProfileController() {
return new UserProfileController();
}
}
and this component:
#Component(modules = {UserProfileModule.class})
#Singleton
public interface AppComponent {
void inject(UserProfileActivity activity);
}
So far, in My UserProfileActivity I can #Injectan UserProfileController. But now, I need to inject the UserProfileActivity to the controller. I mean, inject each other.
I could do it by calling a UserProfileController setter in UserProfileActivity: setActivity(this);, but it would be nice if can be automatic.
How can achieve that?
Thanks.
For starters: add it to the constructor. Then declare that dependency.
#Provides
#Singleton
UserProfileController providesUserProfileController(UserProfileActivity activity) {
return new UserProfileController(activity);
}
After doing so dagger will complain about not being able to provide UserProfileActivity unless you already do so. If you don't, add another module, or just provide the dependency from that same module. The actual implementation follows, first we need to fix your code.
#Singleton is a dependency on top of the hierarchy. You can't—or at least should not—have an activity dependency for a #Singleton annotated object, since this will probably cause bad smells and/or memory leaks. Introduce a custom scope #PerActivity to use for dependencies within your activities lifespan.
#Scope
#Retention(RUNTIME)
public #interface PerActivity {}
This will allow for correct scoping of the object. Please also refer to some tutorials about dagger, since this is a really important issue and covering everything in a single answer would be too much. e.g. Tasting dagger 2 on android
The following uses the latter approach of the aforementioned 2 options by expanding your module:
#Module
public class UserProfileModule {
private final UserProfileActivity mActivity;
public UserProfileModule(UserProfileActivity activity) {
mActivity = activity;
}
#Provides
#PerActivity
UserProfileActivity provideActivity() {
return mActivity;
}
#Provides // as before
#PerActivity
UserProfileController providesUserProfileController(UserProfileActivity activity) {
return new UserProfileController(activity);
}
}
If you now use your component Builder you can create a new instance of your module with the activity as an argument. The dependency will then correctly be supplied.

Dagger 2 error: dependency "cannot be provided without an #Inject constructor" while it actually annotated with #Inject

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

Dagger 2 - modules from different components

I am not quite sure how to solve this with dagger 2.
Lets assume we have ApplicationModule that provides us ApplicationContext
then we have ApplicationComponent that uses just this one module.
Then on top of it we have ActivityModule and ActivityComponent that has dependency on ApplicationComponent.
ActivityComponent is build just like
ApplicationComponent component = ((MyApplication) getApplication()).getComponent();
mComponent = Dagger_ActivityComponent.builder()
.applicationComponent(component)
.activityModule(new ActivityModule(this))
.build();
And then I inject my activity:
mComponent.inject(this);
Now I am able to use everything that is declared inside my ActivityModule, however it is not possible for me to access ApplicationModule.
So the question is how could that be achieved? So that when I build component that depends on another component I can still access module from the first one?
EDIT
I think I have found solutions, after rewatching Devoxx talk by Jake again, I have had to miss that out, whatever I want to use from another components module I have to provide in that component, for example I want to use Context from ApplicationModule then inside ApplicationComponent I have to state Context provideContext(); and it is going to be available. Pretty cool :)
You have already answered your question, but the answer is to specify the provision methods in your "superscoped" component (ApplicationComponent).
For example,
#Module
public class ApplicationModule {
#Provides
#Singleton
public Something something() {
return new Something.Builder().configure().build();
// if Something can be made with constructor,
// use #Singleton on the class and #Inject on the constructor
// and then the module is not needed
}
}
#Singleton
#Component(modules={ApplicationModule.class})
public interface ApplicationComponent {
Something something(); //PROVISION METHOD. YOU NEED THIS.
}
#Scope
#Retention(RetentionPolicy.RUNTIME)
public #interface ActivityScope {
}
#ActivityScope
public class OtherThing {
private final Something something;
#Inject
public OtherThing(Something something) {
this.something = something;
}
}
#Component(dependencies = {ApplicationComponent.class})
#ActivityScope
public interface ActivityComponent extends ApplicationComponent { //inherit provision methods
OtherThing otherThing();
}

Categories

Resources