Does Dagger2 Initialize Objects it does not use? - dagger-2

I have a #Module class that has many #Provides methods. My #Component takes a dependency on this Module class. Ex:
#Singleton
#Component(modules = { MyModule.class})
public interface MyComponent {
ObjectA getObjectA();
ObjectB getObjectB();
}
#Module
public class MyModule {
#Provides
#Singleton
ObjectC provideObjectC() {
return new ObjectC();
}
#Provides
#Singleton
ObjectD provideObjectD() {
return new ObjectD();
}
#Provides
#Singleton
ObjectA provideObjectA(ObjectC objectC) {
return new ObjectA(objectC);
}
#Provides
#Singleton
ObjectB provideObjectB(ObjectD objectD) {
return new ObjectB(objectD);
}
}
I create an instance of the component using the Dagger builder, and provide a new instance of MyModule. If I only call myComponent.getObjectA() will it also construct ObjectB (and its dependencies), or are those left unconstructed?

Ran a manual test of the code I provided in the question with logging. If your injection only uses ObjetA, it will create ObjectA and ObjectC, but it will not create ObjectB or ObjectD.

It's been a while since I've used Dagger but you should specify those as parameters in the provide statement for it to work correctly. It will manage calling your other provider methods to grab the singleton instances.
#Provides
#Singleton
ObjectB provideObjectB(ObjectD objectD) {
return new ObjectB(objectD);
}
Or else you can also specify it as an injected constructor. See Dagger 2 injecting parameters of constructor

Related

Dagger2: wildcard with Generics

I'm new to Dagger2 and DI in general, but I'm interested to populate a map with injected keys/values. The problem is that it works if I provide the exact types, I can't make it work with wildcards, any solution for that?
#Module
public class SimpleIssueModule
{
#Provides
#Singleton
#IntoMap
#StringKey("simple_issue")
public SimpleIssue provideSimpleIssue()
{
return new SimpleIssue();
}
}
#Module
public class DaggerFactoryModule
{
#Provides
#Singleton
public Factory provideFactory(Map<String, Provider< ? extends Issue>> map)
{
return new Factory(map);
}
}
If you want a map of Provider< ? extends Issue>> map, then you need to use Issue as the type returned in your module. Dagger will not do any casting or guessing on its own.
#Provides
#Singleton
#IntoMap
#StringKey("simple_issue")
public Issue provideSimpleIssue() {
return new SimpleIssue();
}
what to do in case I need a Module that provides a base class (Issue) into a Map and also need a provider of the concrete class (SimpleIssue) and I would like it to be Singleton (same instance returns in both cases)
In this case you provide the #Singleton of SimpleIssue.
#Provides
#Singleton
public SimpleIssue provideSimpleIssue() {
return new SimpleIssue();
}
// or you can use constructor injection, dropping the method above...
#Singleton
public class SimpleIssue {
#Inject
public SimpleIssue(...) {
}
}
Then you bind this instance into a Map. There is no need for a scope, since the implementation should declare it (as done above).
#Provides
#IntoMap
#StringKey("simple_issue")
public Issue provideSimpleIssue(SimpleIssue issue) {
return issue;
}
// or alternatively with `#Binds` when using an abstract class / interface
// this leads to actually better performing dagger code
#Binds
#IntoMap
#StringKey("simple_issue")
public Issue provideSimpleIssue(SimpleIssue issue);

how it works #BindsInstance dagger 2

I have recently updated dagger 2.8 to 2.9 dagger. and documentation of the last release have been added as follows:
-Added #BindsInstance for component builders to easily bind instances that are constructed outside of the graph.
-Producers: Added ProducerMonitor.ready (), which is called when all of a producer's inputs are available.
-Removed #Provides(type =...) usage. Use the annotations in dagger.multibindings instead. #Produces.type was also removed.
-All binding methods are now validated, even if they are unused in a particular #Component
-#Component.dependencies can no longer include #Modules.
I want to know how these new features:
Thank you!!
Note: I am new to dagger 2, but you want to be able to make maximum use of this library.
#bindsInstance is used for removing constructor from module and chaining modules where you get component.
Without #BindsInstance
#Module
public class AppModule {
private final Application application;
public AppModule(Application application) {
this.application = application;
}
#Provides
#Singleton
Application provideApplication() {
return application;
}
#Provides
#Singleton
public SharedPreferences providePreferences() {
return application.getSharedPreferences("store",
Context.MODE_PRIVATE);
}
}
These modules(ToastMakerModule, and SensorControllerModule) are for learning purposes they get context and instantiate , may not be practical for real examples
public class ToastMaker {
private Application application;
public ToastMaker(Application application) {
this.application = application;
}
public void showToast(String message) {
Toast.makeText(application, message, Toast.LENGTH_SHORT).show();
}
}
#Module
public class ToastMakerModule {
#Singleton
#Provides
ToastMaker provideToastMaker(Application application) {
return new ToastMaker(application);
}
}
#Singleton
#Component(modules = {AppModule.class, ToastMakerModule.class, SensorControllerModule.class})
public interface AppComponent {
void inject(MainActivity mainActivity);
// DaggerAppComponent.build() returns this Builder interface
#Component.Builder
interface Builder {
AppComponent build();
Builder appModule(AppModule appModule);
Builder sensorControllerModule(SensorControllerModule sensorControllerModule);
Builder toastMakerModule(ToastMakerModule toastMakerModule);
}
}
Build component like this
appComponent = DaggerAppComponent
.builder()
.appModule(new AppModule(this))
.sensorControllerModule(new SensorControllerModule())
.toastMakerModule(new ToastMakerModule())
.build();
With #BindsInstance
#Module
public class AppModule {
#Provides
#Singleton
public SharedPreferences providePreferences(Application application) {
return application.getSharedPreferences("data",
Context.MODE_PRIVATE);
}
}
Component
#Singleton
#Component(modules = {AppModule.class, ToastMakerModule.class, SensorControllerModule.class})
public interface AppComponent {
void inject(MainActivity mainActivity);
#Component.Builder
interface Builder {
AppComponent build();
// #BindsInstance replaces Builder appModule(AppModule appModule)
// And removes Constructor with Application AppModule(Application)
#BindsInstance
Builder application(Application application);
}
}
and build component like this
appComponent = DaggerAppComponent
.builder()
.application(this)
.build();
#BindsInstance lets the component host the dependency directly and hence the lifetime is the lifetime of the component. This can be used to avoid #Singleton scope. Does it even matter? Avoiding singleton scope helps DaggerAppComponent access the provider without a costly DoubleCheck. So yes, it is possible to still use the module and not use any scopes. However, using module still means that DaggerAppComponent is going to use a Provider factory to inject the dependency. Using #BindsInstance, the provider is not needed at all unless the dependency is injected lazily via Lazy<> or Provider<>.
Any dependencies (like String constants etc) that is known as AppComponent is being created are good candidates for #BindsInstance. Note this is based on Dagger 2.19.

About Dagger 2. Connection of #inject and #provide

If there is #inject, then it means there must be #provide?
inject field gets its value from #provide method of module?
Yes if you use Module
#Module
public class SomeModule {
#Provides
Unscoped unscoped() {
return new Unscoped();
}
#Provides
#Singleton
Scoped scoped() {
return Scoped();
}
}
BUT classes with #Inject constructor get automatically appended to your scoped component even if no module is specified for it:
#Singleton
public class Scoped {
#Inject
public Scoped() {
}
}
public class Unscoped {
#Inject
public Unscoped() {
}
}
If there is #Inject annotation then it's dependency can be provided in two ways :
By Using Provides annotation in module
#Provides
TasksPresenter provide TasksPresenter(TasksRepository tasksRepository, TasksContract.View tasksView) {
return new TasksPresenter(tasksRepository,tasksView);
}
By Using Constructor Injection
#Inject
TasksPresenter(TasksRepository tasksRepository, TasksContract.View tasksView) {
mTasksRepository = tasksRepository;
mTasksView = tasksView;
}
One thing to observe here is Constructor Injection solve two thing
Instantiate object
Provides the object by adding it to Object graph.

Dagger2 - where define the scopes

I have a small question to Dagger2.
But first let me show the sample code:
#Singleton
#Component(module={ApplicationModule.class})
public Interface ApplicationComponent {
}
#Module
public class ApplicationModule {
#Provides
public Context provideContext() {
return context;
}
}
I know that the objects from the component now are "Singletons"..
My question... Did that have any effect to the Module? Is the Module also Singleton?
No, the Module will not be singleton unless you specify the scope for the #Provides annotated provider methods as well.
#Singleton
#Component(module={ApplicationModule.class})
public Interface ApplicationComponent {
Context context;
}
#Module
public class ApplicationModule {
#Provides //unscoped, every injection is new instance
public Context context() {
return context;
}
#Provides
#Singleton //scoped, one instance per component
public Something something() {
return new Something();
}
}

Dagger 2 Activity context/ApplicationContext modules

I'm struggling with dagger 2 in order to understand how i can pass a context or another according to my needs.
- First I have an ApplicationModule annotated #Singleton since it provides hi level objects like the webservice object, the model ..., generally those objects are passed the ApplicationContext (since the y need to live during the whole Application lifetime)
#Singleton
#dagger.Component(modules = {
AppModule.class
})
public interface AppComponent {
void inject(MyApp application);
Model model();
Context context();<--- should provide the application Context for the Object above (model)
...
the implementation looks like that
#dagger.Module
public class AppModule {
private final Application app;
public ApplModule(Application app) {
this.app = app;
}
#Provides
#Singleton
Model provideModel(Bus bus) {
return new Model(bus);
}
#Provides
#Singleton
Context provideApplicationContext() {
return app.getApplicationContext();
}
...
secondly I have an Activity Scope componenet in with I provide the current activity and different views which need a Context.
#ActivityScope
#Component(
dependencies = AppComponent.class
, modules = {ActivityModule.class}
)
public interface ActivityComponent {
void inject(MyActivity activity);
Context context(); <---should provide the activity's context
MyView homeView(); <----takes a Context as a contructor parameter
#Module
public class ActivityModule {
private final Activity activity;
public ActivityModule(Activity activity) {
this.activity = activity;
}
#Provides
#ActivityScope
public Activity activity() {
return activity;
}
#Provides
#ActivityScope
#Named("viewcontext") <----- if I removed this I get an error from Dagger
public Context context() {
return activity;
}
#Provides
#ActivityScope
MyView provideZeView(Bus bus, Model model) { <---- previously receiving the ApplicationContext as a parameter
MyView v = new MyView(activity, bus, model); <---- must pass the activity otherwise passing the Context reveived is the ApplicationContext
return v;
}
so Here are my questions:
I used scopes in order to have a better "granularity" over what is passed and i still get the applicationContext
If I remove the #Named qulifier i get an error
previously the Views where produced by another module with a dependence to the ActivityModule but still getting the ApplicationContext
Well the point is I am certainly missing something...but I can't figure what, maybe I misundestood the use of Scopes
you can use qualifiers like this. in two separate files define the following:
#Qualifier
#Retention(RetentionPolicy.RUNTIME)
public #interface ActivityContext {
}
#Qualifier
#Retention(RetentionPolicy.RUNTIME)
public #interface ApplicationContext {
}
then in your ActivityModule do this:
#Provides
#ActivityScope
#ActivityContext
public Context context() {
return activity;
}
and likewise in your appmodule do this:
#Provides
#Singleton
#ApplicationContext
Context provideApplicationContext() {
return app.getApplicationContext();
}
now we have a way to ask for whatever type of context we need based on the qualifier #ApplicationContext and #ActivityContext.
so for example in your activity you could do this:
#Inject #ApplicationContext
Context c;
which would inject an application context.
and in a module you could do this for example:
#Provides
#ActivityScope
LoginPresenter provideLoginPresenter(#ActivityContext Context context) {
return new LoginPresenter(context);
}
to provide an activity context. this is just an example.
#Named is a requirement if you want to provide multiple objects of the same type from your module.
As far as your second question, in regards to passing the correct Activity context, you need to have this in your ActivityComponent:
Activity activity();