What is the difference between addsTo and includes in Java's Dagger? - android

I am not sure what the difference is? When should I use which?
http://square.github.io/dagger/javadoc/index.html

includes indicates which modules current module is composed of. For example, it's useful for aggregating all your modules statically:
#Module(
includes = { AndroidModule.class, NetworkModule.class, StorageModule.class }
)
public class RootModule() {
}
// other file
objectGraph = ObjectGraph.create(new RootModule());
Instead of dynamically:
objectGraph = ObjectGraph.create(
new AndroidModule(),
new NetworkModule(),
new StorageModule());
Thus, fully utilizing compile time graph validation.
addsTo relates specifically to parent-child modules' relations. It indicates that module is an extension of some module and is used as .plus() parameter. E.g. having two modules:
#Module(
//...
)
public class ParentModule() {
//...
}
#Module(
addsTo = { ParentModule.class },
//...
)
public class ChildModule () {
//...
}
this config means that after parentGraph = ObjectGraph.create(new ParentModule()); you can execute childGraph = parentGraph.plus(new ChildModule()); somewhere in your code to create extended, usually, short-lived child graph.

Related

Android Tests: use Dagger2 + Gradle

I understand how Dagger2 works,
I understand it allows to easily swap dependencies, so we can use mocks for testing.
Point is that I am not sure I understand how am I supposed to provide different Dagger2 Components implementations for testing and for debug/production.
Would I need to create 2 Gradle productFlavors (e.g "Production"/"Test")
that would contain 2 different Components definition?
Or can I specify that I want to use the mock Component for test compile and the non mock Component for non test builds?
I am confused, please some clarification would be great!
Thanks a lot!
Unit testing
Don’t use Dagger for unit testing
For testing a class with #Inject annotated constructor you don't need dagger. Instead create an instance using the constructor with fake or mock dependencies.
final class ThingDoer {
private final ThingGetter getter;
private final ThingPutter putter;
#Inject ThingDoer(ThingGetter getter, ThingPutter putter) {
this.getter = getter;
this.putter = putter;
}
String doTheThing(int howManyTimes) { /* … */ }
}
public class ThingDoerTest {
#Test
public void testDoTheThing() {
ThingDoer doer = new ThingDoer(fakeGetter, fakePutter);
assertEquals("done", doer.doTheThing(5));
}
}
Functional/integration/end-to-end testing
Functional/integration/end-to-end tests typically use the production
application, but substitute fakes[^fakes-not-mocks] for persistence,
backends, and auth systems, leaving the rest of the application to
operate normally. That approach lends itself to having one (or maybe a
small finite number) of test configurations, where the test
configuration replaces some of the bindings in the prod configuration.
You have two options here:
Option 1: Override bindings by subclassing modules
#Component(modules = {AuthModule.class, /* … */})
interface MyApplicationComponent { /* … */ }
#Module
class AuthModule {
#Provides AuthManager authManager(AuthManagerImpl impl) {
return impl;
}
}
class FakeAuthModule extends AuthModule {
#Override
AuthManager authManager(AuthManagerImpl impl) {
return new FakeAuthManager();
}
}
MyApplicationComponent testingComponent = DaggerMyApplicationComponent.builder()
.authModule(new FakeAuthModule())
.build();
Option 2: Separate component configurations
#Component(modules = {
OAuthModule.class, // real auth
FooServiceModule.class, // real backend
OtherApplicationModule.class,
/* … */ })
interface ProductionComponent {
Server server();
}
#Component(modules = {
FakeAuthModule.class, // fake auth
FakeFooServiceModule.class, // fake backend
OtherApplicationModule.class,
/* … */})
interface TestComponent extends ProductionComponent {
FakeAuthManager fakeAuthManager();
FakeFooService fakeFooService();
}
More about it in the official documentation testing page.

Dagger2: Component cannot depend on multiple scoped components

Yes, I know this has been asked before, and yes, I know it is "by design".
But I'd like to do something like this:
#Component(modules = {RealmModule.class})
public interface RealmComponent {
Realm realm();
}
#Component(modules = {RepositoryModule.class})
public interface RepositoryComponent {
PersonRepository personRepository();
ScheduleRepository schedulesRepository();
}
#Component(dependencies = {RealmComponent.class, RepositoryComponent.class})
public interface AppDataComponent
extends RealmComponent, RepositoryComponent {
}
#ApplicationScope
#Component(dependencies = {AppContextComponent.class,
AppDataComponent.class,
AppDomainComponent.class,
AppPresentationComponent.class,
AppUtilsComponent.class})
public interface ApplicationComponent
extends AppContextComponent, AppDataComponent, AppDomainComponent, AppUtilsComponent, AppPresentationComponent {
void inject(CustomApplication customApplication);
void inject(DashboardActivity dashboardActivity);
}
However, what I get is unscoped, every time I inject a JobManager or a ScheduleRepository or anything else, I get a new instance. The only way I could "fix" that was this.
#Module
public class JobManagerModule {
private JobManager jobManager;
#Provides
public JobManager jobManager(Context context) {
if(jobManager == null) {
jobManager = new JobManager(context, new Configuration.Builder(context).networkUtil(
new WifiOrMobileNetworkUtil(context)).build());
}
return jobManager;
}
}
Not a fan.
So, how is one meant to structure and rip apart the dependency tree, without making one big gigantic über blob component that has every single module listed and every single provision method (instead of these "subcomponent" component dependencies)?
I tried using subcomponents for this, but then you have to provide every single module for the final ApplicationComponent.
I'm not sure what to do here. I tried specifying #Singleton for every first-level component and #SubcomponentScope for every AppDataLevelComponent, I also tried making a new scope for every single subcomponent, but both of them failed with "cannot depend on multiple scoped components".
EDIT: Apparently in order to get scoped providers, marking the components with the scope is not enough - you must specify the scope for the #Provides annotated methods too.
#Module
public class RepositoryModule {
#Provides
#Singleton
public PersonRepository personRepository() {
return new PersonRepositoryImpl();
}
#Provides
#Singleton
public ScheduleRepository schedulesRepository() {
return new SchedulesRepositoryImpl();
}
}
In the meantime, I ended up with this übercomponent.
#Singleton
#Component(modules = {
AppContextModule.class,
DbMapperModule.class,
DbTaskModule.class,
RealmModule.class,
RepositoryModule.class,
InteractorModule.class,
ServiceModule.class,
PresenterModule.class,
XmlPersisterModule.class
})
public interface ApplicationComponent
extends AppContextComponent, AppDataComponent, AppDomainComponent, AppUtilsComponent, AppPresentationComponent {
Where the xyzComponent classes are just interfaces to store the provision methods...
(Please note that this structure is an anti-pattern as described by Martin Fowler, and you should organize modules based on features / activities, and make them into subscoped components using component dependencies. Component dependencies are used to subscope your superscope components, and "inherit" dependency providers.)
I had same problems like you not while ago and ended using the same ubercomponent approach except I use #Subcomponents in order organize the things and not to have all modules listed in the ubercomponent (I call it "top" or "app's" component).
You may see an example here:
How to migrate missing inject from module with complete = false from Dagger 1 to Dagger 2

Dagger ObjectGraph plus() Modules that include modules in the root module keep returning No no-args constructor

I have an Android app that uses Dagger. There are certain sections of the entire app that I want to add scoped ObjectGraphs for several activities that share a common scope. The following module is in the root ObjectGraph
#Module(
injects = {
MyApplication.class,
},
complete = false,
library = true)
public class BasicContextManagerModule {
private Context applicationContext;
public BasicContextManagerModule(Context applicationContext) {
this.applicationContext = applicationContext;
}
#Provides
Context getApplicationContext() {
return applicationContext;
}
}
Then I try to add the following Module through existingObjectGraph.plus(new FileManagerModule());
#Module(
injects = {
MyListActivity.class,
MyFileDetailActivity.class,
MyFileInfoActivity.class,
},
includes = BasicContextManagerModule.class
)
public class FileManagerModule {
#Provides
FileManager provideFileManager(Context context) {
return new FileManager(context);
}
}
But the result is
java.lang.UnsupportedOperationException: No no-args constructor com.myapp.core.modules.BasicContextManagerModule$$ModuleAdapter
Can someone help me understand why the plus won't allow this? I read from the dagger documentation that plus extends the object graph and you can have includes and addsTo Modules. But I haven't been able to achieve this.
includes means the module will live in the same subgraph, and Dagger will instantiate it if you don't pass an instance.
addsTo means the referenced module is expected to be in the graph (actually in a parent graph) but Dagger won't provide it for you.
What you want is addsTo.

What should go in the "injects" in a Dagger #Module?

Guice user, trying to understand a bit of the benefits of Dagger here.
Let's say I have the following
MyActivity.java
---------------
public class MyActivity {
#Inject MyImplicitClass myImplicitClass;
#Inject #Named("foo") MyExplicitClass myNamedExplicitClass;
...
}
MyImplicitClass.java
------------
public class MyImplicitClass {
#Inject
MyImplicitClass(MyExplicitClass myExplicitClass) {
...
}
...
}
MyModule.java
---------------
#Module(injects = { ? }) {
#Provides provideExplicitClass() {
return new MyExplicitClass();
}
#Named("foo") #Provides provideNamedExplicitClass() {
return new MyExplicitClass();
}
}
So, my question is, what should go in the Injects?
I know for a fact that MyActivity needs to go. Or rather, whatever "this" needs to have DaggerInjector.inject(this)
Does anything else?
Would Dagger the implicit construction injection class (MyImplicitClass) and/or the class explicitly provided in the module (MyExplicitClass) to also be specified?
MyExplicitClass wouldn't even make sense if I need it to be annotated.
However, the javadoc makes me feel I should error the side of inclusion
http://square.github.io/dagger/javadoc/dagger/Module.html#injects()
Dagger 1 does it's validation at compile therefore it needs all of the entry point you're going to use at runtime (objectgraph.get(entrypoint), objectgraph.inject(entrypoint)).
Entry points are the injectable classes which are at the top of the dependency graph. In Android most of the time those are your Activities, Fragments, Services, Broadcast Receivers. All compontents which are not created by Dagger. You would also list classes which are not part of any entry point's dependency tree again becoming an entry point of their own.
Those entry point are use to do validation by Dagger. Dagger will start validation at the entry points and tricle down the dependency tree and validate dependencies annotated with #Inject and #Provides from your Modules.
So in conclusion only entry points needs to be provided in the #Module(injects = { * })
In your example because the MyImplicitClass is an #Inject dependency of your entry point MyActivity you don't need to specify it in the #Modules(injects={}). The same is true for your MyExplicitClass dependency. Therefore from your example the only class that needs to be added to the list is in fact MyActivity.
Base on your example code, if you had something like this.
// MyIndependantClass.java
-----------------
public class MyIndependantClass {
#Inject
MyIndependantClass(MyExplicitClass myExplicitClass) {
}
}
// MyActivity.java
---------------
public class MyActivity {
#Inject MyImplicitClass myImplicitClass;
#Inject #Named("foo") MyExplicitClass myNamedExplicitClass;
...
protected void onCreate (Bundle savedInstanceState) {
MyIndependantClass myIndependantClass = MyObjectGraph.get(MyIndependantClass.class);
...
}
}
The fact that MyIndependantClass is created using ObjectGraph.get() and is not a direct dependency of your MyActivity entry point you would need to declare that class part of the injects list.
// MyModule.java
---------------
#Module(injects = { MyActivity.class, MyIndependantClass.class }) {
#Provides provideExplicitClass() {
return new MyExplicitClass();
}
#Named("foo") #Provides provideNamedExplicitClass() {
return new MyExplicitClass();
}
}

Right place to place injects attribute in dagger plus'ed modules

I have a doubt regarding which is the right place to set the injects attribute (not annotation) in dagger, when there are plus'ed graphs.
We have this scenario:
#Module(injects = ClassWithInjects.class, complete = false)
public final class BaseModule {
#Provides
DependencyA providesA() { return new A(); }
#Provides
DependencyB providesB() { return new B(); }
...
}
class ClassWithInjects {
#Inject
A a;
#Inject
B b;
}
that was working fine when doing:
ObjectGraph graph = ObjectGraph.create(new BaseModule()).plus(new Object[0]); // no modules used to plus by default
graph.inject(new ClassWithInjects());
Afterwards we added a new module that is the only one supposed to provide B from that moment on:
#Module(injects = ClassWithInjects.class, complete = false)
public final class BaseModule {
#Provides
DependencyA providesA() { return new A(); }
...
}
#Module(addsTo = BaseModule.class, complete = false)
public final class AdditionalModule {
#Provides
DependencyB providesB() { return new B(); }
}
class ClassWithInjects {
#Inject
A a;
#Inject
B b;
}
That is failing with
IllegalStateException No binding for B required by class
ClassWithInjects
when doing this:
ObjectGraph graph = ObjectGraph.create(new BaseModule()).plus(new AdditionalModule());
graph.inject(new ClassWithInjects());
It works when I move the
injects = ClassWithInjects.class
attribute from BaseModule to AdditionalModule.
So, where am I supposed to place the injects attribute when using plus'ed modules? Does it have to be in one of the modules used as parameters in the call to .plus(...)?
According to this github issue I have to put the injects in the module that has the bindings for the instance I try to inject dependencies into (ClassWithInjects in the example above) but, what if I have several modules providing the different bindings needed to fulfill all the dependencies of that instance? Do I have to create a module that includes all the others and put there the injects attribute?
Have you seen the Android sample here: https://github.com/square/dagger/tree/master/examples/android-activity-graphs/src/main/java/com/example/dagger/activitygraphs
Comparing that setup to what you have, the "injects" were defined on the Module that was plussed. I think a current limitation is that you have to define those on every plussed Module in the case where you have multiple.

Categories

Resources