Dagger 2 injection in Fragments and swapping Modules - android

I've started using Dagger 2 and I'm not sure if I'm doing things right as I ran in the following complication:
Let's say I have a HouseModule initialized with a House and a WindowModule initialized with a Window.
Now I have a HouseFragment which is supposed to general Information about the house.
Thus I created a HouseComponent including the HouseModule.
So far so good.
Now there are multiple nested HouseDetailsFragments within the HouseFragment(ViewPager) which show information about the House and the Window.
I created a HouseDetailsComponent including the HouseModule and the WindowModule.
My dependency graph looks like this:
// provides application wide dependencies (Application context, SharedPref, Repository,...)
#Component(modules = {ApplicationModule.class})
public interface ApplicationComponent {
}
// provides general activity dependencies (Navigator,... )
#Component(dependencies = ApplicationComponent, modules = {ActivityComponent.class})
public interface ActivityComponent{
void inject(MainActivity mainActivity);
}
// provides house fragment specific dependencies (HousePresenter,...)
#Component(dependencies = ApplicationComponent, modules = {ActivityComponent.class, HouseComponent.class})
public interface HouseComponent extends ActivityComponent {
void inject(HouseFragment fragment);
}
// provides house details fragment specific dependencies (HouseDetailsPresenter,...)
#Component(dependencies = ApplicationComponent, modules = {ActivityComponent.class, HouseComponent.class, WindowComponent.class})
public interface HouseDetailsComponent extends ActivityComponent {
void inject(HouseDetailsFragment detailsFragment);
}
Component creation becomes increasingly complex this way and I wonder how to inject dependencies to the HouseFragment and especially the HouseDetailsFragments best.
e.g. to build the HouseDetailsComponent I have to do the following:
// casting omitted
DaggerHouseDetailsComponent.builder()
.applicationComponent(getActivity().getApplication().getApplicationComponent())
.activityModule(getActivity().getActivityModule())
.houseModule(getParentFragment().getHouseModule())
.windowModule(new WindowModule(window)).build().inject(this);
I dislike the knowledge (and casting) required about parent fragments and activities.
Is there a simpler way to achieve what I want to do? Any other suggestions about the dependency graph?
Also how do I show a different House in the HouseFragment? i figured creating a new HouseModule and then swapping the other out... but how do I do that? There are no accessors afaik.

I've been using submodules to simplify the hierarchy.
Also Dagger 2.11+ supports Android and makes injections for it easier.

Related

Injecting presenters with MVP/Dagger 2

I've been learning about DI and Dagger 2 recently, and I feel like I'm ending up with more boilerplate than what I started with. My setup includes
AppComponent -- provides Application, Resources and other networking-related stuff. #Singleton Scoped.
Feature1Component, Feature2Component.... -- provide repositories used across screens. #Feature scoped, subcomponents of AppComponent. Has 'plus' methods for screen components.
Screen1Component, Screen2Component... -- this is where the feeling of boilerplate comes in - each screen component just has an 'inject' method for the Fragment/Activity that is being used to depict the screen. Subcomponent of FeatureXComponent. Each corresponding module ends up looking just like so:
#Module
public class Screen1Module
{
private final Screen1Contract.View view;
public Screen1Module(Screen1Contract.View view)
{
this.view = view;
}
#Provides
public Screen1Contract.View provideView()
{
return view;
}
}
So as a result, for each screen, I end up writing a Component and Module since I can only call the following:
((MyApplication) getActivity().getApplication())
.getFeature1Component()
.newScreen1Component(new Screen1Module(this))
.inject(this);
when I'm initializing my view (Activity or Fragment), for the benefit of injecting just the presenter.
Is there a way to do this which does not involve as much boilerplate? I was hoping to inject screens at the "Feature" level, but I guess that isn't really possible since screens have a shorter life cycle than features.
This is why dagger-android is born since dagger 2.10. It simplifies injection of Android core types.
#ContributesAndroidInjector replaces subcomponents. DaggerActivity and others get rid of getComponent()....inject(this)
its official doc
and a pretty good article I found recently

Using Dagger Multibinding across Gradle modules

Fellow Dagger users...am I trying to do something impossible? Below is a degraph generated Gradle module view of my app.
Imagine my app has a navigation drawer. I want the list items in the drawer to be added based on a collection populated via a multibinding. The contributors to this multibinding are across > 1 Gradle module.
For example, a list item called "Account" is added from the user module, a 2nd item called "Terms & Conditions" is added from the legal module and a "Search" navigation entry is added from the search Gradle module. These modules can be thought of as stand-alone apps that when bundled together form the complete app experience. They are runnable on their own.
The Dagger documentation on this looks like a copy-paste of the Guice but with one big complication. It states;
"Dagger allows you to bind several objects into a collection even when
the objects are bound in different modules using multibindings. "
...but it means Dagger #Modules, right? Is it possible to populate a multibinding across Gradle modules? I've tried something akin to this and it wasn't what I expected (not all contributions were collected);
Parent app
#Module
abstract class PluginModule {
#Multibinds #NavigationContribution abstract Set<NavigationEntry> navigables();
}
legal Gradle module that contains the Dagger #Module
#Module
class LegalModule {
#Provides
#NavigationContribution
#IntoSet
static NavigationEntry provideLegalNavigation() {
return ...;
}
}
user Gradle module that contains the Dagger #Module
#Module
class AccountModule {
#Provides
#NavigationContribution
#IntoSet
static NavigationEntry provideAccountNavigation() {
return ...;
}
}
Upon running the app only the contributions under the app 'context' are available upon calling;
#Inject #NavigationContribution Set<NavigationEntry> navigationEntries();
The other contributions can be accessed but 'manually' by getting hold of the dagger.Component of the child Gradle module and exposing a method to get the entries back.
This defeats the purpose of the multibinder for my application. Is this possible? If not, why?
Update: triage with Jeff
So, the top-level module annotations look like this;
#ApplicationLifecycle
#Component(
modules = {
OceanLifeModule.class,
AddSpotActivityModule.class,
ModelModule.class,
PluginModule.class
},
dependencies = {
NavigationComponent.class,
LegalComponent.class,
PreferenceComponent.class,
DomainComponent.class
}
)
#jeff-bowman - all I was missing here was the includes = {} on the PluginModule. A blog post by Zac Sweers on the same topic here pointed me in the right direction.

Android | Dagger 2. Injecting different subclasses into Fragment depending of a condition

I am working with MVP and Dagger 2 DI. I have a Fragment that I reuse in a few activities. I have an interface type for presenter as a property of the Fragment, say MVPPresenter. Depending in which activity the Fragment is being used, I need to inject different presenters into it (each presenter is an implementation of MVPPresenter). So I need a way to inject each implementation of MVPPresenter into the Fragment as I need.
Currently, I have a terrible solution, which works, but it is simply wrong and creates unnecessary objects that are never used. Here is the code:
public class MyFragment {
...
#Inject
public void setPresenter(#NonNull ProfilePresenter presenter) {
if (mAdapter instanceof ProfileAdapter) {
this.presenter = presenter;
}
}
#Inject
public void setPresenter(#NonNull ContactsPresenter presenter) {
if (mAdapter instanceof ContactsAdapter) {
this.presenter = presenter;
}
}
...
}
Here is my Module:
#Module
class PresentersModule {
#Provides
#Singleton
ProfilePresenter ProfilePresenter() {
return new ProfilePresenter();
}
#Provides
#Singleton
ContactsPresenter ContactsPresenter() {
return new ContactsPresenter();
}
}
You see, depending on Adapter type, I assign presenter, or do not. I know this is stupid and all. Problem is that Dagger needs exact type to inject to be specified and Interface type wont work.
What is the proper way of dealing with such cases?
You have, as I see it, three solutions of varying degrees of weight.
Inject two choices as you have now: If you know all of your Fragment's use-cases up front, and you don't need to vary the dependency graphs any more than on a single class, you can do so easily using a similar method to what you have now. My variant uses Providers, which are bound automatically for any object in your graph, so that you don't unnecessarily create whole trees of objects; also, #Inject methods can take an arbitrary parameter list, so you can do all of your method injection in one method if you choose.
#Inject
public void setPresenter(
#NonNull Provider<ContactsPresenter> contactsPresenterProvider,
#NonNull Provider<ProfilePresenter> profilePresenterProvider) {
if (mAdapter instanceof ContactsAdapter) {
this.presenter = contactsPresenterProvider.get();
} else if (mAdapter instanceof ProfileAdapter) {
this.presenter = profilePresenterProvider.get();
}
}
The other two solutions involve multiple components: Instead of saying "there is one way of binding my graph together", you're effectively asking Dagger to generate multiple options for you, which means that your graphs can vary widely but stay consistent. This technique might be more useful if you reuse objects in different ways for different sections of your application, like if you have a Profile section and a Contacts section, each of which using a common A injecting a common B injecting a common C injecting a different D. To consistently support two deep graphs like that, child components are a much better option.
Use component dependencies: As in rst's answer, you can use component dependencies to isolate your fragments. They did a pretty good job of explaining, so I'll not repeat that here. You should be aware, though, that component dependencies can only consume bindings that are exposed on the component you depend on: Even if Foo and Bar are bound on DiComponent, you won't be able to access them from your ProfileComponent or ContactsComponent unless you put Foo getFoo() and Bar getBar() on your DiComponent. (That said, component dependencies don't have to be Dagger components, either; they can be arbitrary types that you implement yourself or let Dagger implement for you.)
Use subcomponents: Though rst alluded to subcomponents, I think they warrant a bit more explaining, particularly because they are a core component of the recently-released dagger.android functionality, and because Fragments and other UI pieces can be difficult to extract with component dependencies—subcomponents implicitly and automatically inherit bindings from the surrounding component, so you don't have to explicitly expose bindings on your DiComponent. See other differences at this SO question.
#Component
public interface DiComponent {
ProfileComponent getProfileComponent(); // Dagger generates implementations
ContactsComponent getContactsComponent(); // as part of DiComponent.
}
#Subcomponent(modules={ContactsModule.class})
public interface ContactsComponent {
void inject(MyFragment myFragment);
}
#Module
public interface ContactsModule {
#Binds MvpPresenter bindMvpPresenter(ContactsPresenter contactsPresenter);
}
#Subcomponent(modules={ProfileModule.class})
public interface ProfileComponent {
void inject(MyFragment myFragment);
}
#Module
public interface ProfileModule {
#Binds MvpPresenter bindMvpPresenter(ProfilePresenter profilePresenter);
}
In the above, the root DiComponent doesn't have a binding for MvpPresenter, so in itself it can't inject MyFragment. However, ProfileComponent and ContactsComponent can, and each will use different graphs configured in the corresponding Modules (but silently inheriting common bindings from DiComponent's modules). If the graphs vary differently further down, like with each MvpPresenter using the same Validator but with a different ProfileValidationRule versus ContactsValidationRule, you could bind ValidationRule to those different classes in your different Modules to get different behavior.
(For completeness, you would usually also have the option to use a factory like AutoFactory and pass in a parameter like the presenter to your specific container like Fragment. However, this is only really an option if you're creating your instances, and not really an option when Android forces a zero-arg public constructor so it can create Fragment instances at will.)
Looking through the names you've given to mvp-presenters, one could conclude, their complementary mvp-views should rather be separated and implemented in different fragments.
But if you wish to maintain things as-is, having only single setPresenter method declared in your fragment, probably the easiest way to deal with your problem would be to introduce separate components with complementary modules for providing desirable presenter implementations.
For this solution to work you would need to adjust your fragment to contain single declaration of setPresenter method with MVPPresenter type as an argument:
#Inject
public void setPresenter(#NonNull MVPPresenter presenter) {
this.presenter = presenter;
}
Afterwards, you'd need to provide components exposing inject(...) method and declaring usage of appropriate module. As those dependency graphs would be dependent on main component instance, they should get their own scope (tied to activity or fragment, depending on what class is actually holding the graph object).
For instance, if you were using DiComponent for providing all your dependencies with scope defined via #Singleton annotation, you'd need to declare #MyFragmentScope annotation and provide components, dependent on above-mentioned DiComponent, in order to declare injectable presenters:
import javax.inject.Scope;
#Scope
public #interface MyFragmentScope {
}
Your dependent components would look like:
#MyFragmentScope
#Component(dependencies = DiComponent.class, modules = ProfileModule.class)
public interface ProfileComponent {
void inject(MyFragment fragment);
}
with complementary module:
#Module
public class ProfileModule {
#Provides
#MyFragmentScope
MVPPresenter providesProfilePresenter() {
return new ProfilePresenter();
}
}
Note: return type is MVPPresenter, not concrete implementation.
Similarly you'd need to create ContactsComponent and ContactsModule for your ContactsPresenter.
Eventually you should use proper component instance to perform the injection. Now instead of using
diComponent.inject(myFragment)
you should use component which would provide desirable dependency.
At this point you would actually have a switch defining which presenter should be used.
In case of ProfilePresenter injecting you'd need to use:
DaggerProfileComponent.builder()
.diComponent(diComponent)
.build()
.inject(myFragment);
Or in case of ContactsPresenter injecting you'd need to use:
DaggerContactsComponent.builder()
.diComponent(diComponent)
.build()
.inject(myFragment);
It's rather common practice to use separate components for smaller parts of application like activities. It's possible to either declare such components as regular dependent ones or as sub components (see #Subcomponent documentation for reference). Starting from Dagger 2.7 there is a new way of declaring Subcomponents via #Module.subcomponents. Due to this fact there's an opportunity to decouple AppComponent from Activities Subcomponents. You may refer to sample GitHub repository from frogermcs for reference. He also has a great complementary blog post on this topic.

How to inject same Dagger 2 dependency with different implementations based on the Context?

I have a Dagger 2 dependency that is provided differently if it’s an Activity or some other context instance (e.g., a Service). Both would refer to the same interface but their implementation varies. How could I organise this using Dagger 2?
Right now, I’m trying with two different components, ActivityComponent and ContextComponent with their respective modules as follows:
#ActivityScope
#Subcomponent(
modules = {
ActivityModule.class,
ContextModule.class
})
public interface ActivityComponent {
}
#Module
public class ActivityModule {
#Provides
#MyActivityQualifier
public MyObject provideMyObject() {
}
}
#ContextScope
#Subcomponent(
modules = {
ContextModule.class
})
public interface ContextComponent { }
#Module
public class ContextModule.class {
#Provides
public MyObject provideMyObject() {
}
}
Then, if I’m using MyObject in an Activity, I have to add the qualifier as follows:
#Inject #MyActivityQualifier MyObject myObject;
This feels wrong but I’m not sure why. Is there a better way?
You are using subcomponents.
The question is: Can or should both objects be "visible" (usable, injectable, ...) at the same time?
Yes: You will have to use some sort of qualifier. Or else you can't distinguish them.
No: You can "hide" them by not exposing the dependency from the dependent component. You would have to use a normal component instead of #Subcomponent in this case, and just don't add the getMyInterface() method to your parent component.
And there aren't any other options, because you have 2 components dependent on each other. So your approach looks fine with the information you provide.
Note you can also just qualify one of your implementations. In my project, I use a qualified #Named("public") annotation for a common implementation without user data. If I don't add any qualifier I will just get the other one (unqualified).
With independent components, e.g. ActivityAComponent and ActivityBComponent you could just switch which implementation gets provided using different modules.

Dagger Module Inclusion Cycle

I'm still new to Dagger and trying to get a hang of things. I wanted to split my modules into logical groups which each provide their own functionality, yet basically would act the same as if it were in one Module.
For instance, let's say I have my main application module defined as follows:
//com.example.android.MyAppModule.java
#Module(
includes = AnalyticsModule.class,
injects = { <snip> }
)
public class MyAppModule {
// various provides
}
And I have another module defined like this which sets up an ErrorReporter interface and provides the concrete implementation to it.
// com.example.android.analytics.AnalyticsModule.java
#Module(
addsTo = MyAppModule.class,
injects = { MyApp.class }
)
public class AnalyticsModule(){
// ErrorReporter is a public interface and ErrorReporterImpl is a package-local final concrete class that implements it
#Provides #Singleton
ErrorReporter providesErrorReporter(ErrorReporterImpl reporter) { return reporter };
}
In my Application class I set up the object graph like this:
// com.example.android.MyApp.java
public class MyApp extends Application {
#Inject ErrorReporter errorReporter;
#Override
public void onCreate() {
super.onCreate();
applicationGraph = ObjectGraph
.create(new MyAppModule())
.plus(new AnalyticsModule());
applicationGraph.inject(this);
errorReporter.initialize();
}
}
When I run the dagger compiler I get something like this:
Graph validation failed: Module Inclusion Cycle:
0. com.example.android.analytics.AnalyticsModule included by com.example.android.MyAppModule
1. com.example.android.modules.MyAppModule included by com.example.android.analytics.AnalyticsModule
0. com.example.android.analytics.AnalyticsModule
What am I doing wrong here? I assume it has something to do with includes/addsTo, but when I remove those I get other errors.
If I remove includes = AnalyticsModule.class from MyAppModule I get something like this:
com.example.android.analytics.ErrorReporter could not be bound with key com.example.android.analytics.ErrorReporter required by com.example.android.MyApp for com.example.android.MyAppModule
Everything is fine if I completely forgo an AnalyticsModule and then hand off the providesErrorReporter to MyAppModule, but then I have to make my concrete impl class public so I can use it in the other module.
#Module(includes = AnalyticsModule.class) is useful for composing multiple modules into one module. In this case, using .plus(new AnalyticsModule()) is wrong because AnalyticsModule has already been included by the includes line in the annotation. So you should remove the call to plus().
#Module(addsTo = MyAppModule.class) is for allowing a call to .plus(new AnalyticsModule()). Since we've removed that call, we should remove the addsTo as well.
#Module(complete = false) is necessary for AnalyticsModule because some of its dependencies can't be satisfied on its own. This is okay; as long as MyAppModule has complete = true (the default), Dagger will do the necessary error checking.
Admittedly, the "Module Inclusion Cycle" error was a little bit unclear. The cycle was caused by A including B which addsTo A.
addsTo= is there to specify that this graph is an extension to the referenced module in a parent/child graph. .plus() is the run-time version of this. You only need .plus() if you have shorter-lived graph-managed instances. This corresponds roughly to the notion of "scope" in Guice and other DI containers.
In your example you are doing:
applicationGraph = ObjectGraph
.create(new MyAppModule())
.plus(new AnalyticsModule());
This ends up creating two graphs in a parent/child relationship, which isn't what you need here. IN an android app you want one graph for the Application.
You can simply remove addsTo and MyAppModule will automatically instantiate AnalyticsModule, or you can pass both in if you want to avoid the dynamic initialization like so:
applicationGraph = ObjectGraph.create(new MyAppModule(), new AnalyticsModule());
The module inclusion cycle is because you have a cyclical relationship between these two modules and modules must themselves form an acyclic graph of configuration. MyAppModule includes AnalyticsModule which in turn includes MyAppModule. (addsTo is a less rigorous includes= used to specify things obtained from parent graphs)

Categories

Resources