Dagger 2 doesn't provide my class as singleton, why? - android

I have a class:
public class MyFactory {
public MyFactory() {
}
}
My Dagger module:
#Module
public class MyModule {
#Provides
#Singleton
public MyFactory provideMyFactory() {
return new MyFactory();
}
}
My component:
#Singleton
#MyScope
#Component(modules = MyModule.class)
public interface MyComponent {
MyFactory getMyFactory();
}
My Scope:
#Scope
#Retention(RetentionPolicy.CLASS)
public #interface MyScope {
}
I expect MyFactory instance being provided by Dagger2 is a singleton however, it is not. I tried to call following code in two places of my project:
MyFactory factory = DaggerMyComponent.builder()
.build()
.getMyFactory();
Logcat.d("tag", "factory = " + factory);
My log shows me :
factory = :com.my.app.MyFactory#eaa9330
factory = :com.my.app.MyFactory#8bd552e
As you can see, they are two instances. So, My questions are:
Question 1. In above case, why Dagger doesn't provide MyFactory as a singleton??
Question 2. With my above code, does everytime calling DaggerMyComponent.builder().build(); create a new instance of MyComponent?
Question 3. Dagger also allow developer's class instance to be injectable by place a #Inject annotation in constructor without bothering having the module:
public class MyFactory {
#Inject
public MyFactory() {
}
}
developer can use it just by #Inject MyFactory factory;
In this case, how to make MyFactory singleton with Dagger 2?

Scopes are per component. If you create multiple components then you create multiple objects. #Singleton is no exception.
// same component
DaggerMyComponent firstComponent = DaggerMyComponent.builder().build();
firstComponent.getMyFactory() == firstComponent.getMyFactory(); // true
// second component
DaggerMyComponent secondComponent = DaggerMyComponent.builder().build();
firstComponent.getMyFactory() == secondComponent.getMyFactory(); // false
You will have to keep the component instances and reuse them, or you will always create new references. E.g. A common way is to place the #Singleton AppComponent within your Application and grab it from there when needed.
In this case, how to make MyFactory singleton with Dagger 2?
With constructor injection you just add the scope to the top of the class.
#Singleton // place scope _on_ the class
public class MyFactory {
#Inject
public MyFactory() {
}
}

Related

Dagger 2 how to access same component everywhere

How do I create a singleton object that I can access anywhere from any place in my code?
Objects(1) from which I want to inject my signleton component can't be created withing dagger.
Those objects don't have common dependencies which I can access (like getApplication()
Quick to an example:
InjectedClass:
public class InjectedClass {
public InjectedClass() {
System.out.println("injected class created");
}
}
HolderClassA:
public class HolderClassA { // this is one of object I marked with (1)
#Inject InjectedClass b;
public HolderClassA() {
Injector build = DaggerInjector.builder().build();
build.inject(this);
System.out.println(b);
}
}
HolderClassB:
public class HolderClassB { // this is another object I marked with (1)
#Inject InjectedClass b;
public HolderClassB() {
Injector build = DaggerInjector.builder().build();
build.inject(this);
System.out.println(b);
}
}
Provider:
#Module
public class Provider {
#Provides
#Singleton
InjectedClass provideInjectedClass() {
return new InjectedClass();
}
}
Injector:
#Component(modules = {Provider.class})
#Singleton
public interface Injector {
void inject(HolderClassA a);
void inject(HolderClassB a);
}
Somewhere in code:
new HolderClassA();
Somewhere else in code that is NO WAY related to previous code, nor has the same parent or can access same objects (like in dagger guide with getApplication()):
new HolderClassB();
Actual result: two instances of InjectedClass are crated
Expected result: a single instance of InjectedClass is created.
The issue is DaggerInjector.builder().build(); creates different scopes which don't know about each other. For example, I can solve this issue by creating static variable DaggerInjector.builder().build() and call inject from it. But thus it wouldn't be dependency injection anymore, but rather not trivial singleton pattern.
From what I understand from comments, you want separate components for your holder classes, that would inject same instance? Pardon me if I am wrong.
You can try this.
#Component(modules = arrayOf(AppModule::class))
#Singleton
interface AppComponent {
//sub components
fun plusHolderClass1(holderModule: HolderModule1):HolderComponent1
fun plusHolderClass2(holderModule: HolderModule2):HolderComponent2
//provision methods
fun getInjectedClass():InjectedClass
}
This is your application component, or the top level component, that you initialise in your application, of course using your Module class that will provide the Injected class as a singleton.
#Subcomponent(modules = arrayOf(HolderModule1::class))
#ActivityScope
interface HolderComponent1{
fun inject(holder:Holder1)
}
And a similar one for Holder2 class. You can define your local scope dependencies in the modules.
But of course even in this case you have to store the instance of appComponent in Application class.
While injecting
appComponent.plusHolderComponent1(HolderModule1()).inject(yourObject)
The InjectedClass object will be injected to yourObject by fetching it from provision methods

dagger2 constructor injection how to provide dependency without module

I've read that constructor injections don't require a module.
So I have this questions.
If I have this constructor injection:
private Model realmModel;
#Inject
public MainActivityPresenter(Model realmModel) {
this.realmModel = realmModel;
}
and this component:
#Singleton
#Component(modules = {AppModule.class})
public interface AppComponent {
Model realmModel();
void inject(MainActivity activity);
}
if in my MainActivity I do it:
((MyApp)getApplication()).createAppComponent().inject(this);
how could I pass the realmModel parameter to the presenter constructor injection?
EDIT: this is the model:
Presenter presenter;
#Inject
public RealmModel(Presenter presenter) {
this.presenter = presenter;
}
Thanks
Three ways to solve this problem
1) Give a module which does the provide of the Relam Model
#Provides
#Singleton
public Model providesRealmModel() {
return new Model();
}
2) Make your RelamModel class also constructor injected
class Model {
#Inject
public Model() {}
}
The Trick in construction injection is all its dependencies should also be constuctor injeceted then it would work fine.
(From experience your model would need application context. look at the 3 option for ways to implement external Dependencies
3) Provide Model as external dependency.
#Module
public class ModelModule {
private Model relamModel;
public ModelModule(Model relamModel) {
this.relamModel = relamModel
}
}
#Component(module={ModelModule.class})
public interface ApplicationComponent {
}
Take a look at the series of videos from twisted eqautions these were my first video tutorial on dagger2. I found it really helpful. Hope it helps you too
https://www.youtube.com/watch?v=Qwk7ESmaCq0
You have two choices:
use a module with a provide method
annotate the constructor of Model with #Inject, doing that when you pass realmModel in the presenter contructor, the model
constructor will be called.
I prefer to use modules, but that's just my opinion.

Dagger - Getting Same Instance On Different Component

I'm having a similar problem like the one in this question.
While the accepted answer does help, but I'm missing final piece to solve the problem.
I have 2 android library modules: common and exp which depends on common.
Everything under common:
#Module
public class CommonModule {
#Singleton
#Provides
public Repository providesRepository() {
return new Repository();
}
}
#Singleton
#Component(modules={CommonModule.class})
public interface CommonComponent {
void inject(CommonClass commonClass);
/**
CommonClass needs instance of Repository
**/
}
public class CommonDIHolder {
public static CommonComponent sComponent;
public static void init() {
sComponent = DaggerCommonComponent.builder().build();
}
}
Everything under exp:
#Module(includes={CommonModule.class})
public class ExpModule {
#Singleton
#Provides
public ExpResource provideExpResource() {
return new ExpResource();
}
}
#Singleton
#Component(modules={ExpModule.class}, dependencies={CommonComponent.class})
public interface ExpComponent {
void inject(ExpClass expClass);
/**
ExpClass needs instance of Repository and ExpResource
**/
}
public class ExpDIHolder {
public static ExpComponent sComponent;
public static void init() {
sComponent = DaggerExpComponent.builder()
.commonComponent(CommonDIHolder.sComponent)
.build();
}
}
I need both CommonClass and ExpClass receive the same instance of Repository.
The problem with this approach is that #Singleton can't depends on #Singleton. So I have to change the scope of ExpComponent into self-defined scope called #ExpScope. Then I changed the provideExpResource into #ExpScope as well.
Then I encountered an error saying that ExpComponent may not reference bindings with different scopes. It refers to the provideRepository which has different scope (#Singleton) on it. If I changed the scope into ExpScope then the CommonComponent will have different scope with provideRepository.
If I changed all #Singleton into #ExpScope then I receive this error message: depends on scoped components in a non-hierarchical scope ordering
What should I do? Or I'm doing the wrong approach here?
Use one and only one #Singleton scoped component
You should have one and only one #Singleton scoped component like this:
#Singleton
#Component(modules={CommonModule.class, ExpModule.class})
public interface CommonComponent {
}
Only specify Activities, Fragments, and Services as explicit injection targets for Components
In an Android app, you should only list Activities, Fragments and Services as injection sites. You should configure Dagger 2 to inject the rest of your dependencies without having to resort to calling component.inject(this) inside them.
For example, if your CommonClass looks like this:
public class CommonClass {
#Inject Repository repository;
public class CommonClass() {
CommonComponentHolder.get().inject(this);
}
}
Refactor it like this:
public class CommonClass {
private final Repository repository;
#Inject
public class CommonClass(Repository repository) {
this.repository = repository;
}
}
Now when you have an Activity or Fragment that needs CommonClass and you are injecting with CommonComponent or one of its sub-components or dependent components, they can obtain instances of CommonClass wired with the correct dependencies:
public class MyActivity extends AppCompatActivity {
#Inject CommonClass commonClass;
public void onCreate(Bundle savedInstanceState) {
CommonComponentHolder.getComponent().inject(this);
}
}
Use subcomponents or dependent components to specify the injection targets
Now you have a #Singleton scoped component, you'll probably want to create a component for a narrower scope for your Activity or Fragment. You'll have to connect it to your CommonComponent, so use dependent components or subcomponents (subcomponents are preferred as of Dagger 2.10). Since you say you have already tried defining a #ExpScope, I think the missing piece is to make subcomponent or dependent component with the #ExpScope that injects your Activity or Fragment.
Something like the following for the top-level singleton component:
#Singleton
#Component(modules={CommonModule.class, ExpModule.class})
public interface CommonComponent {
ExpComponent.Builder exComponent();
}
And then for the subcomponent:
#ExpScope
#Subcomponent(modules = {NarrowerScopedModule.class})
public interface ExpComponent {
#Subcomponent.Builder
public interface Builder {
Builder narrowerScopedModule(NarrowerScopedModule narrowerScopedModule);
ExpComponent build();
}
}
There are good working examples of Android projects in the Google Android Architecture Blueprints Github repo

Dagger 2: Unable to inject singleton in other scope

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.

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

Categories

Resources