I have a BIG Android app that needs to run different code for depending on the OS version, the manufacturer, and many other things. This app however needs to be a single APK. It needs to be smart enough at runtime to determine which code to use. Until now we have been using Guice but performance issues are causing us to consider migrating to Dagger. However, I've been unable to determine if we can achieve the same use case.
The main goal is for us have some code that runs at startup to provide a list of compatible Modules. Then pass that this list to Dagger to wire everything up.
Here is some pseudocode of the current implementation in Guice we want to migrate
import com.google.inject.AbstractModule;
#Feature("Wifi")
public class WifiDefaultModule extends AbstractModule {
#Override
protected void configure() {
bind(WifiManager.class).to(WifiDefaultManager.class);
bind(WifiProcessor.class).to(WifiDefaultProcessor.class);
}
}
#Feature("Wifi")
#CompatibleWithMinOS(OS > 4.4)
class Wifi44Module extends WifiDefaultModule {
#Override
protected void configure() {
bind(WifiManager.class).to(Wifi44Manager.class);
bindProcessor();
}
#Override
protected void bindProcessor() {
(WifiProcessor.class).to(Wifi44Processor.class);
}
}
#Feature("Wifi")
#CompatibleWithMinOS(OS > 4.4)
#CompatibleWithManufacturer("samsung")
class WifiSamsung44Module extends Wifi44Module {
#Override
protected void bindProcessor() {
bind(WifiProcessor.class).to(SamsungWifiProcessor.class);
}
#Feature("NFC")
public class NfcDefaultModule extends AbstractModule {
#Override
protected void configure() {
bind(NfcManager.class).to(NfcDefaultManager.class);
}
}
#Feature("NFC")
#CompatibleWithMinOS(OS > 6.0)
class Nfc60Module extends NfcDefaultModule {
#Override
protected void configure() {
bind(NfcManager.class).to(Nfc60Manager.class);
}
}
public interface WifiManager {
//bunch of methods to implement
}
public interface WifiProcessor {
//bunch of methods to implement
}
public interface NfcManager {
//bunch of methods to implement
}
public class SuperModule extends AbstractModule {
private final List<Module> chosenModules = new ArrayList<Module>();
public void addModules(List<Module> features) {
chosenModules.addAll(features);
}
#Override
protected void configure() {
for (Module feature: chosenModules) {
feature.configure(binder())
}
}
}
so at startup the app does this:
SuperModule superModule = new SuperModule();
superModule.addModules(crazyBusinessLogic());
Injector injector = Guice.createInjector(Stage.PRODUCTION, superModule);
where crazyBusinessLogic() reads the annotations of all the modules and determines a single one to use for each feature based on device properties. For example:
a Samsung device with OS = 5.0 will have crazyBusinessLogic() return the list { new WifiSamsung44Module(), new NfcDefaultModule() }
a Samsung device with OS = 7.0 will have crazyBusinessLogic() return the list { new WifiSamsung44Module(), new Nfc60Module() }
a Nexus device with OS = 7.0 will have crazyBusinessLogic() return the list { new Wifi44Module(), new Nfc60Module() }
and so on....
Is there any way to do the same with Dagger? Dagger seems to require you to pass the list of modules in the Component annotation.
I read a blog that seems to work on a small demo, but it seems clunky and the extra if statement and extra interfaces for components might cause my code to balloon.
https://blog.davidmedenjak.com/android/2017/04/28/dagger-providing-different-implementations.html
Is there any way to just use a list of modules returned from a function like we are doing in Guice? If not, what would be the closest way that would minimize rewriting the annotations and the crazyBusinessLogic() method?
Dagger generates code at compile-time, so you are not going to have as much module flexibility as you did in Guice; instead of Guice being able to reflectively discover #Provides methods and run a reflective configure() method, Dagger is going to need to know how to create every implementation it may need at runtime, and it's going to need to know that at compile time. Consequently, there's no way to pass an arbitrary array of Modules and have Dagger correctly wire your graph; it defeats the compile-time checking and performance that Dagger was written to provide.
That said, you seem to be okay with a single APK containing all possible implementations, so the only matter is selecting between them at runtime. This is very possible in Dagger, and will probably fall into one of four solutions: David's component-dependencies-based solution, Module subclasses, stateful module instances, or #BindsInstance-based redirection.
Component dependencies
As in David's blog you linked, you can define an interface with a set of bindings that you need to pass in, and then supply those bindings through an implementation of that interface passed into the builder. Though the structure of the interface makes this well-designed to pass Dagger #Component implementations into other Dagger #Component implementations, the interface may be implemented by anything.
However, I'm not sure this solution suits you well: This structure is also best for inheriting freestanding implementations, rather than in your case where your various WifiManager implementations all have dependencies that your graph needs to satisfy. You might be drawn to this type of solution if you need to support a "plugin" architecture, or if your Dagger graph is so huge that a single graph shouldn't contain all of the classes in your app, but unless you have those constraints you may find this solution verbose and restrictive.
Module subclasses
Dagger allows for non-final modules, and allows for the passing of instances into modules, so you can simulate the approach you have by passing subclasses of your modules into the Builder of your Component. Because the ability to substitute/override implementations is frequently associated with testing, this is described on the Dagger 2 Testing page under the heading "Option 1: Override bindings by subclassing modules (don’t do this!)"—it clearly describes the caveats of this approach, notably that the virtual method call will be slower than a static #Provides method, and that any overridden #Provides methods will necessarily need to take all parameters that any implementation uses.
// Your base Module
#Module public class WifiModule {
#Provides WifiManager provideWifiManager(Dep1 dep1, Dep2 dep2) {
/* abstract would be better, but abstract methods usually power
* #Binds, #BindsOptionalOf, and other declarative methods, so
* Dagger doesn't allow abstract #Provides methods. */
throw new UnsupportedOperationException();
}
}
// Your Samsung Wifi module
#Module public class SamsungWifiModule {
#Override WifiManager provideWifiManager(Dep1 dep1, Dep2 dep2) {
return new SamsungWifiManager(dep1); // Dep2 unused
}
}
// Your Huawei Wifi module
#Module public class HuaweiWifiModule {
#Override WifiManager provideWifiManager(Dep1 dep1, Dep2 dep2) {
return new HuaweiWifiManager(dep1, dep2);
}
}
// To create your Component
YourAppComponent component = YourAppComponent.builder()
.baseWifiModule(new SamsungWifiModule()) // or name it anything
// via #Component.Builder
.build();
This works, as you can supply a single Module instance and treat it as an abstract factory pattern, but by calling new unnecessarily, you're not using Dagger to its full potential. Furthermore, the need to maintain a full list of all possible dependencies may make this more trouble than it's worth, especially given that you want all dependencies to ship in the same APK. (This might be a lighter-weight alternative if you need certain kinds of plugin architecture, or you want to avoid shipping an implementation entirely based on compile-time flags or conditions.)
Module instances
The ability to supply a possibly-virtual Module was really meant more for passing module instances with constructor arguments, which you could then use for choosing between implementations.
// Your NFC module
#Module public class NfcModule {
private final boolean useNfc60;
public NfcModule(boolean useNfc60) { this.useNfc60 = useNfc60; }
#Override NfcManager provideNfcManager() {
if (useNfc60) {
return new Nfc60Manager();
}
return new NfcDefaultManager();
}
}
// To create your Component
YourAppComponent component = YourAppComponent.builder()
.nfcModule(new NfcModule(true)) // again, customize with #Component.Builder
.build();
Again, this doesn't use Dagger to its fullest potential; you can do that by manually delegating to the right Provider you want.
// Your NFC module
#Module public class NfcModule {
private final boolean useNfc60;
public NfcModule(boolean useNfc60) { this.useNfc60 = useNfc60; }
#Override NfcManager provideNfcManager(
Provider<Nfc60Manager> nfc60Provider,
Provider<NfcDefaultManager> nfcDefaultProvider) {
if (useNfc60) {
return nfc60Provider.get();
}
return nfcDefaultProvider.get();
}
}
Better! Now you don't create any instances unless you need them, and Nfc60Manager and NfcDefaultManager can take arbitrary parameters that Dagger supplies. This leads to the fourth solution:
Inject the configuration
// Your NFC module
#Module public abstract class NfcModule {
#Provides static NfcManager provideNfcManager(
YourConfiguration yourConfiguration,
Provider<Nfc60Manager> nfc60Provider,
Provider<NfcDefaultManager> nfcDefaultProvider) {
if (yourConfiguration.useNfc60()) {
return nfc60Provider.get();
}
return nfcDefaultProvider.get();
}
}
// To create your Component
YourAppComponent component = YourAppComponent.builder()
// Use #Component.Builder and #BindsInstance to make this easy
.yourConfiguration(getConfigFromBusinessLogic())
.build();
This way you can encapsulate your business logic in your own configuration object, let Dagger provide your required methods, and go back to abstract modules with static #Provides for the best performance. Furthermore, you don't need to use Dagger #Module instances for your API, which hides implementation details and makes it easier to move away from Dagger later if your needs change. For your case, I recommend this solution; it'll take some restructuring, but I think you'll wind up with a clearer structure.
Side note about Guice Module#configure(Binder)
It's not idiomatic to call feature.configure(binder()); please use install(feature); instead. This allows Guice to better describe where errors occur in your code, discover #Provides methods in your Modules, and to de-duplicate your module instances in case a module is installed more than once.
Is there any way to just use a list of modules returned from a
function like we are doing in Guice? If not, what would be the closest
way that would minimize rewriting the annotations and the
crazyBusinessLogic() method?
Not sure this is the answer you're looking for, but just in case you do have other options and for other community members I will describe completely different approach.
I would say that the way you used Guice until now is an abuse of DI framework, and you will be much better off leveraging this opportunity to remove this abuse instead of implementing it in Dagger.
Let me explain.
The main goal of dependency injection architectural pattern is to have construction logic segregated from functional logic.
What you basically want to achieve is standard polymorphism - provide different implementations based on a set of parameters.
If you use Modules and Components for that purpose, you will end up structuring your DI code according to business rules governing the need for these polymorphic implementations.
Not only will this approach requires much more boilerplate, but it also prevents emergence of cohesive Modules that have meaningful structure and provide insights into application's design and architecture.
In addition, I doubt you will be able to unit test these business rules "encoded" inside dependency injection logic.
There are two approaches which are much better IMHO.
First approach is still not very clean, but, at least, it doesn't compromise the large scale structure of dependency injection code:
#Provides
WifiManager wifiManager(DeviceInfoProvider deviceInfoProvider) {
if (deviceInfoProvider.isPostKitKat() ) {
if (deviceInfoProvider.isSamsung()) {
return new WifiMinagerSamsungPostKitKat();
} else {
return new WifiMinagerPostKitKat();
}
} else {
return new WifiMinagerPreKitKat();
}
}
The logic that chooses between implementation still resides in DI code, but, at least, it did not make it into the large scale structure of that part.
But the best solution in this case is to make a proper object oriented design, instead of abusing DI framework.
I'm pretty sure that the source code of all these classes is very similar. They might even inherit from one another while overriding just one single method.
In this case, the right approach is not duplication/inheritance, but composition using Strategy design pattern.
You would extract the "strategy" part into a standalone hierarchy of classes, and define a factory class that constructs them based on system's parameters. Then, you could do it like this:
#Provides
WiFiStrategyFactory wiFiStrategyFactory(DeviceInfoProvider deviceInfoProvider) {
return new WiFiStrategyFactory(deviceInfoProvider);
}
#Provides
WifiManager wifiManager(WiFiStrategyFactory wiFiStrategyFactory) {
return new WifiMinager(WiFiStrategyFactory.newWiFiStrategy());
}
Now construction logic is simple and clear. The differentiation between strategies encapsulated inside WiFiStrategyFactory and can be unit tested.
The best part of this proper approach is that when a new strategy will need to be implemented (because we all know that Android fragmentation is unpredictable), you won't need to implement new Modules and Components, or make any changes to DI structure. This new requirement will be handled by just providing yet another implementation of the strategy and adding the instantiation logic to the factory.
All that while being kept safe with unit tests.
Related
I am a newbie in using Dagger and DI. I am trying to use AndroidInjection resolver for injecting dependencies into fragments of its activity.
Generally, I understood that, in the case of using Dagger.android, I have to create MyAppComponent and install AndroidInjectionModule in order to use AndroidInjection.inject(Activity/Fragment/etc..). In this way, I have provided Subcomponents' interfaces with Builders to make Dagger able to generate appropriate injectors.
But what if I have Subcomponent, i.e. DeviceFragmentSubcomponent that has a dependency on the module with parameterized constructor?
#Subcomponent(modules = {DeviceModule.class})
public interface DevicePageFragmentSubcomponent extends AndroidInjector<DevicePageFragment>{
#Subcomponent.Builder
public abstract class Builder extends AndroidInjector.Builder<DevicePageFragment>{
public abstract Builder setDeviceModule(DeviceModule deviceModule);
}
}
#Module
public class DeviceModule {
private Device mDevice;
public DeviceModule(Device device) {
mDevice = device;
}
#Provides
public Device provideDevice(){
return mDevice;
}
}
What should be done to set DeviceModule instance within DeviceActivity for using AndroidInjection.inject(this) in its fragments?
Is it possible to add required modules not at the moment of creation application's dependency tree, but on the arbitrary event?
The Android Injection part of Dagger can (currently) only be used along with AndroidInjection.inject(this), where it will inject the given Android Framework type with a predefined module.
As such, there is no way to pass in a parameter or module.
Your first option would be not to use the Android Injection part of Dagger. Just create your component as you see fit and inject your object.
The second option would be to not use a parameter / module. In theory, if your Activity can create a DeviceModule, so can Dagger, given that it has access to the Activity—and by using the Android Injection parts, the component injecting your type has access to it.
You did not specify what dependency Device has or why you need to pass it to the DeviceModule from your fragment.
Let's say your Device depends on DevicePageFragment.
class Device {
#Inject Device(DevicePageFragment fragment) { /**/ } // inject the fragment directly
}
You can access the fragment and do what you would do. If that's not your case, let's say you need to read the arguments Bundle. You could modify your Module to not take a device, but rather to create it iself, and getting rid of the constructor argument as well.
#Module
public class DeviceModule {
// no constructor, we create the object below
// again we take the fragment as dependency, so we have full access
#Provides
public Device provideDevice(DevicePageFragment fragment){
// read your configuration from the fragment, w/e
long id = fragment.getArguments().getLong("id")
// create the device in the module
return new Device(id);
}
}
In the end it really depends on your usecase.
What I tried to show is that you have access to the object that you are trying to inject. This means that whatever you can do within this object, you can do within Dagger. There is no need for parameterized modules, since you can extract those parameters from the target, as seen above.
I'm working on setting up Dagger 2 into my android project. It is my first time with this framework and everything goes well so far. But I'm seeing different approaches on the way you can set up this framework in your project and I wonder which one is better, because I compare both and for me the result is kind of the same.
I followed this guide: https://github.com/codepath/android_guides/wiki/Dependency-Injection-with-Dagger-2
Searching on Internet all of them use this approach.
It use #Module and #Component to define the dependencies.
and your application ends up like this:
public class MyApp extends Application {
private NetComponent mNetComponent;
#Override
public void onCreate() {
super.onCreate();
// Dagger%COMPONENT_NAME%
mNetComponent = DaggerNetComponent.builder()
// list of modules that are part of this component need to be created here too
.appModule(new AppModule(this)) // This also corresponds to the name of your module: %component_name%Module
.netModule(new NetModule("https://api.github.com"))
.build();
// If a Dagger 2 component does not have any constructor arguments for any of its modules,
// then we can use .create() as a shortcut instead:
// mNetComponent = com.codepath.dagger.components.DaggerNetComponent.create();
}
public NetComponent getNetComponent() {
return mNetComponent;
}
}
But I found another way (I haven't tested it):https://google.github.io/dagger/android.html
And It looks like completely different, using different classes and annotations.
It uses something like this:
#Subcomponent(modules = ...)
public interface YourActivitySubcomponent extends AndroidInjector<YourActivity> {
#Subcomponent.Builder
public abstract class Builder extends AndroidInjector.Builder<YourActivity> {}
}
#Module(subcomponents = YourActivitySubcomponent.class)
abstract class YourActivityModule {
#Binds
#IntoMap
#ActivityKey(YourActivity.class)
abstract AndroidInjector.Factory<? extends Activity>
bindYourActivityInjectorFactory(YourActivitySubcomponent.Builder builder);
}
#Component(modules = {..., YourActivityModule.class})
interface YourApplicationComponent {}
public class YourApplication extends Application implements HasDispatchingActivityInjector {
#Inject DispatchingAndroidInjector<Activity> dispatchingActivityInjector;
#Override
public void onCreate() {
super.onCreate();
DaggerYourApplicationComponent.create()
.inject(this);
}
#Override
public DispatchingAndroidInjector<Activity> activityInjector() {
return dispatchingActivityInjector;
}
}
So, my questions are:
which one is better?
What are the reasons for choosing one approach instead of the other?
The method of setting up Dagger 2 for Android that is now prescribed in official Dagger 2 documentation has a number of advantages and should be preferred. The advantages are just those elaborated there, namely:
Copy-pasting code makes it hard to refactor later on. As more and more developers copy-paste that block, fewer will know what it actually does.
More fundamentally, it requires the type requesting injection (FrombulationActivity) to know about its injector. Even if this is done through interfaces instead of concrete types, it breaks a core principle of dependency injection: a class shouldn’t know anything about how it isinjected.
Let's apply those reasons to your first example.
Reason 1
Assume we have an Activity that wants to use your NetComponent. Let's call it NetActivity. The onCreate(Bundle savedInstanceState) method of that NetActivity will look something like this:
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
((MyApp) getApplicationContext()).getNetComponent().inject(this);
}
This code has all the visual appeal of toenail clippings scattered on oatmeal (not my simile) and will end up copy-pasted in all of the injection site Activities where you use NetComponent. If you use more complicated components, such as this example from the docs:
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// DO THIS FIRST. Otherwise frombulator might be null!
((SomeApplicationBaseType) getContext().getApplicationContext())
.getApplicationComponent()
.newActivityComponentBuilder()
.activity(this)
.build()
.inject(this);
// ... now you can write the exciting code
}
Even worse. It can easily degenerate into a magical piece of code that must be copied and pasted throughout the injection sites. If it changes, it's easy to forget to update just one site and have your app crash.
Reason 2
One of the great advantages of dependency injection is injection sites need not know or care about their injectors, just as dependencies do not know or care about their dependents. To return to ourNetActivity, we have:
((MyApp) getApplicationContext()).getNetComponent().inject(this);
The Activity "knows" about its injector (NetComponent), and the Activity is now coupled with a concretion MyApp and the method getNetComponent() from the same. If either of these classes changes, NetActivity will have to change as well.
The advantages of following the new way of injection inside Activity and Fragments available in Dagger versions 2.10 and forward are just the opposite of these disadvantages:
You end up with less copy-paste code
Types requesting injection no longer have to know or care about their injectors or the sources of their injectors.
Furthermore, as pointed out in this blog, preferring subcomponents over dependent components reduces the method count of your app.
While using subcomponents may initially seem more difficult, there are some clear advantages. However, for the purposes of learning Dagger dependent components may be initially easier to understand. If the second example is too complicated initially, you can graduate to the preferred method when you have gained finesse.
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.
I'm trying to learn DI through Dagger 2 and apply it to our product. The Application-level things annotated with #Singleton are straightforward enough (e.g. SharedPreferences). In thinking of our architecture, there are several dependencies that are asynchronous in nature which I've imagined scoped at a #ForSession scoping.
Our authentication token/account info, acquired from the Android AccountManager. Could be synchronous in the case of an existing, valid session. Could be asynchronous if no existing session and the AccountManager has to show the complete the login flow.
Once we have a valid session token and session information:
Provide an Endpoint to fulfil dependencies so that our networking layer knows where to find the API.
acquire our "user" information from a network API.
pull additional supporting information from a network API (or local cache).
pull localized back-end strings from a network API (or local cache).
Get a component going that relies on a bound Service. Provide that component asynchronously only when the bound Service binding is complete.
The presentation layer should be gated on the receipt of the collection of these items. Aside from some sort of "loading" display, there's not much it can do without any of the above.
It feels like these dependencies fit the use-case for #ProducerModule and #Produces. I feel like I could have #Produces ListenableFuture<> methods for each of these dependencies, with perhaps a SettableFuture<> as the implementation. Perform whatever work is required, call set() on that future, dependency is met.
Where I get antsy is with this quote from the Producers guide.
As in the above example, producer modules can be used seamlessly with ordinary modules, subject to the restriction that provided types cannot depend on produced types.
For the "gate presentation on everything being available" I can envision a composite object that could get #Inject with the unwrapped T of the futures. But is that even legal?
This is the closest I've come but it's explicitly calling the constructor of the composite, not injecting it. Is there a way to do this cleaner?
#ProducerModule
public class SessionModule {
#Produces
#ForSession
static ListenableFuture<User> produceSignedInUser(SessionManager sessionManager) {
return sessionManager.getSignedInUserFuture();
}
#Produces
#ForSession
static ListenableFuture<BoundService> produceBoundService(SessionManager sessionManager) {
return sessionManager.getBoundServiceFuture();
}
#Produces
#ForSession
static CompositeSessionInfo produceComposite(User user, BoundService service) {
return new CompositeSessionInfo(user, service);
}
}
Then the component:
#ForSession
#ProductionComponent(modules = SessionModule.class)
public interface SessionComponent {
ListenableFuture<CompositeSessionInfo> getCompsiteSessionInfoFuture();
}
And somewhere I want to gate I can do something like:
SessionComponent component = Dagger_SessionComponent.builder()
.executor(executor)
.build();
Futures.addCallback(component.getCompsiteSessionInfoFuture(),
new FutureCallback<CompositeSessionInfo> {
public void onSuccess(CompositeSessionInfo result) {
releaseTheHounds(result);
}
public void onFailure(Throwable t) {
reportError(t);
}
});
Am I way off on my understanding of this part of it? And an aside: why are the #Produces methods declared static? Is this required? (EDIT: the static sure isn't required, but I'm unsure what the intent was other than to not have instance fields in the Module).
EDIT:
I decided to create a proof of concept project to abstract out my ideas from my actual project. Everything works as I'd like except that I'm unable to #Inject any of my #Produced items, either the end result "composite" data or intermediate results. If I expose a getter in the component, I can get them so that's what I've done.
My current plan is to have this #Producer based asynchronous stuff off in a separate injectable module, then have the resultant dependencies get fed into a #Provides style module that feeds elsewhere so that they can be #Injected.
EDIT EDIT:
Updated the proof of concept to have a common precursor dependency to more closely mimic my needs. Still can't #Inject. I believe this about as good as I'll get.
Alright, since it appears I'm going it alone I'll post my final conclusions as my own answer to hopefully help out someone else looking to do something similar.
I updated my proof of concept project one more time. Now, once all of the asynchronous dependencies are met the new single composite dependency is an actual #Module, #Produced by the newly renamed SessionProductionComponent, then that module is registered as a component called the SessionProvisionComponent. This component is a standard #Component with #Provide methods to provide dependencies through the standard #Inject mechanism.
#Produces
#ForSession
public SessionProvisionModule produceSessionProvisionModule(Application app, SomeAsyncDependency someAsyncDependency, AnotherAsyncDependency anotherAsyncDependency) {
SessionProvisionModule module = new SessionProvisionModule(someAsyncDependency, anotherAsyncDependency);
((App) app).createSessionProvisionComponent(module);
return module;
}
Now in the MainActivity, when I need to acquire the session information it looks like so:
App app = (App) getApplication();
sessionProductionComponent = app.getSessionProductionComponent();
if (app.getSessionProductionComponent() == null) {
sessionProductionComponent = app.createSessionProductionComponent(new SessionProductionModule());
}
Futures.addCallback(sessionProductionComponent.getSessionProvisionModuleFuture(),
new FutureCallback<SessionProvisionModule>() {
#Override
public void onSuccess(SessionProvisionModule result) {
app.getSessionProvisionComponent().inject(MainActivity.this);
}
#Override
public void onFailure(Throwable t) {
// handle failure
}
});
Once the Future succeeds I can inject() the MainActivity and any annotated fields get #Injected with dependencies as one would expect.
In this way, I can actually have #Inject after #Produce.
Not as clean as I'd like, but still better than without DI. Now any number of asynchronous dependencies, operating on whatever timeframe, can be satisfied in any order and once all of them are ready a single Future is set and a SessionProvisionComponent is made ready to inject dependencies with those #Produced dependencies.
Mostly happy.
I'm currently developing an Android MVP Application, and I'm trying to separate my dependencies in different Dagger2 Modules.
The problem I'm having is about changing a module in Unit Test Time. The scenario is the following:
LoginComponent, which uses two modules: LoginModule and HTTPModule
LoginModule in one of its methods requires an OkHttp instance, which is provided by HTTPModule.
The code is the following:
#Singleton
#Component(modules = {LoginModule.class, HTTPModule.class})
public interface LoginComponent {
}
#Module(includes = {HTTPModule.class})
public class LoginModule {
#Provides
#Singleton
public MyThing provideMyThing(OkHttpClient client) {
// Do things with it
}
}
#Module
public class HTTPModule {
#Provides
#Singleton
public OkHttpClient provideOkHttpClient(){
// Return the OkHttpClient
}
}
The thing is, at test time I would need to change the OkHttpClient that is returned (by making it accept all the certificates, as when I run it on the JVM it does not accept the LetsEncrypt certificate).
Also I would need that because I need to declare that MyTest.class can be injected with module, and as MyTest.class is under the app/src/test/ folder, it's not visible for the classes that are placed under app/src/main/. What I've done until now is to copy and paste the Component and the modules to the /test/ folder, and make the injected class declaration there. But I know there must be a proper way to achieve what I'm looking for.
Another thing I've tried is annotating the methods with custom Scopes (creating a #TestScope annotation). However this leads me to the same problem that I had commented before: I cannot make the MyTest.class visible to the component, because it's placed under the /test/ folder.
I've already checked other similar questions, such as this one and this another one, but this last one is for running tests with Robolectric, and by now I'm able to unit test most of my code with JUnit4 only (Android Studio 2-Beta 8).
If anyone could point me to the right direction, I would be more than grateful.
Thanks in advance!
You're using Dependency injection in a way that still keeps your code tightly coupled. Generally speaking you want your dependencies to be interfaces instead of actual classes. This keeps your code nice and loose, easy to read, modify and maintain.
Hide your network operations behind an interface to allow you to modify the network implementation whenever you need to. This could be done for testing - in your case, but it will also allow you to switch out the network library if you'll want to or need to in the future without changing any other code.
Try something like this:
#Module
public class HTTPModule {
#Provides
#Singleton
public NetworkProvider provideNetworkProvider(){
// Return the Network provider
}
}
The network abstraction layer:
public interface NetworkProvider {
// Methods to send requests and receive async responses
}
The OkHttp implementation:
public class OkHttpNetworkProvider implements NetworkProvider {
// Implement NetworkProvider. This is the only class that
// knows about OkHttp and its components
}
Now you can create a mock version of NetworkProvider and use it for testing, whether via a test module or directly.