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
Related
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() {
}
}
According to tutorials like this one: in Dagger2 we are able to provide local singleton objects using a custom scope.
I already have a global appComponent and my goal is to create a activity scope that allows a subcomponent to have local singleton providers.
My issue is when i create a custom scope and inject my activity twice, i see that the object being provided is not the same one, even though i tagged it with the custom scope. for the appComponent which uses the #Singleton annotation it works fine though.
Lets take a look at how i did this:
Here is the custom activity scope:
#Scope
#Retention(RetentionPolicy.RUNTIME)
public #interface ActivityScope {
}
And here is how i define the subcomponent:
#ActivityScope
#Subcomponent(modules = {PresenterModule.class, UseCaseModule.class})
public interface ActivitySubComponent {
void inject(LoginActivity target);
void inject(LoginPresenter target);
void inject(UCGetFireBaseAccount target);
}
Notice here that i've tagged this subcomponent with #ActivityScope annotation that i custom made above.
Now lets see the appComponent which is the parent component and global:
#Singleton
#Component(modules = {AppModule.class, NetworkModule.class, RepositoryModule.class})
public interface AppComponent {
void inject(myappCloudRepo target);
void inject (FireBaseCloudRepo target);
ActivitySubComponent plus(PresenterModule presenterModule, UseCaseModule useCaseModule);
}
Notice ActivitySubComponent is defined here as a subcomponent.
Finally lets look at the presenterModule class to see what i wanted to be provided as a singleton:
#Module
public class PresenterModule {
#Provides
#ActivityScope
LoginPresenter provideLoginPresenter(Context context) {
return new LoginPresenter(context);
}
}
Now when i actually use this i set it up in my application class like this:
public class MyApplication extends Application {
private AppComponent appComponent;
#Override
public void onCreate() {
super.onCreate();
appComponent = initDagger(this);
}
public AppComponent getAppComponent() {
return appComponent;
}
protected AppComponent initDagger(MyApplication application) {
return DaggerAppComponent.builder()
.appModule(new AppModule(application))
.build();
}
public ActivitySubComponent getActivityComponent() {
return appComponent.plus(new PresenterModule(),new UseCaseModule());
}
}
Then lastly in any activity i do this:
public class LoginActivity extends AppCompatActivity{
ActivitySubComponent activityComponent;
//this loginpresenter is from the activitysubcomponent
#Inject
LoginPresenter loginPresenter;
//this retrofit object is from the appcomponent
#Inject
Retrofit retrofit;
#Override
protected void onCreate(#Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
activityComponent= ((MyApplication)getApplication()).getActivityComponent();
activityComponent.inject(this);
Log.d("j2emanue",loginPresenter.toString());
Log.d("j2emanue",retrofit.toString());
activityComponent= ((MyApplication)getApplication()).getActivityComponent();
activityComponent.inject(this);
Log.d("j2emanue2",loginPresenter.toString());
Log.d("j2emanue2",retrofit.toString());
}
}
When i take a look at the logs after injecting TWICE i am expecting all objects to be the same but instead the loginPresenter is another instance but the retrofit is the same so that works.
Here is the log:
05-23 23:33:35.617 31298-31298/com.myapp.mobile.myappfashion D/j2emanue: com.myapp.mobile.myappfashion.UI.Presenters.LoginPresenter#1c27a460
05-23 23:33:35.617 31298-31298/com.myapp.mobile.myappfashion D/j2emanue: retrofit2.Retrofit#13366d19
05-23 23:33:35.617 31298-31298/com.myapp.mobile.myappfashion D/j2emanue2: com.myapp.mobile.myappfashion.UI.Presenters.LoginPresenter#3dc041de
05-23 23:33:35.617 31298-31298/com.myapp.mobile.myappfashion D/j2emanue2: retrofit2.Retrofit#13366d19
Notice the loginpresenter is another object so its not making a local singleton for me that would only be destroyed when i dereference the activitysubcomponent.
what am i missing here ? The reason i want it to be a local singleton is for configuration changes. This way the presenter can be maintained during a config change.
In your application class you create and return a new subcomponent.
public ActivitySubComponent getActivityComponent() {
return appComponent.plus(new PresenterModule(),new UseCaseModule());
}
Now scopes mean that within a scope an object exists only one time. But in your case, you create 2 different components. Those 2 components will share everything in their parent component (they are subcomponents), but anything within their scope will be recreated, but unique to their scope.
Anything within your component will use the same #ActivityScope annotated objects, but if you create 2 components you will have 2 copies of everything.
If you take #Singleton as an example, it does not mean the object will be an actual Singleton. It is the name of the scope that you are supposed to have for your root component, which should only be created once and be kept during the lifetime of the application.
If you were to create 2 AppComponents that are #Singleton you could observe that same behavior—two different instances of your object.
In your example Retrofit is the same, because you use the same AppComponent both times, but LoginPresenter gets recreated along with every ActivitySubComponent that you create.
With Dagger you should try that your components follow the same lifecycle as what they scope, thus your app should hold an AppComponent, and every Activity should have their own ActivityComponent (keep the component as a member variable!). When you create a new Activity you should create a new #ActivityScope scoped component, but not more often than that.
You should remove your getActivityComponent() from the Application and keep a reference to your ActivitySubComponent, because injecting scoped dependencies with the same component will give you the same objects.
activityComponent.inject(this);
activityComponent.inject(this);
// call it as many times as you'd like.
activityComponent.inject(this);
Just don't recreate your component.
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.
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
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);
}
}