Dagger 2 inject subcomponent.builder - dagger-2

I'm seeing some code where a SubComponent.Builder is being injected. For instance:
class MyClass #Inject constructor(val mySubcomponentBuilder: MySubComponent.Builder) {
fun someFunc(knownAtRuntime: String) {
mySubcomponentBuilder.withSomethingIKnowAtRuntime(knownAtRuntime).build()
}
}
Why do we (and where/when should we) do this (i.e. inject subcomponent.builders)?
Is this sort of like assisted injection where we can provide instances that are needed on the graph that we only know during runtime?

Yes, a subcomponent builder is sort of like assisted injection, but for a whole subgraph of injectable Dagger dependencies.
What are subcomponents?
Subcomponents are additional components that derive from parent components. In Guice they are analogous to child injectors; in Dagger 1 they are analogous to subgraphs. They let you add additional bindings on top of the parent bindings, as specified through additional modules.
In the sense that they allow bindings from different graphs, they can be compared to component dependencies, but they are generated at the same time as the parent component and can transparently use bindings defined in the parent component. The dagger.android and Hilt packages both use subcomponents.
Each component can have their own scope, and subcomponents aren't an exception: you might see a #Singleton ApplicationComponent with a subcomponent of #RequestScope RequestComponent for servers or #ActivityScope ActivityComponent for Android. Each request or activity would get their own component instance. That would let you inject a #RequestScoped MetricsService or #ActivityScoped User, which would be the same instance within the same request/activity but different instances between the requests/activities.
What are subcomponent builders?
Subcomponents might require modules to be instantiated if they have non-empty constructors, or they might require instances to be bound through #BindsInstance. Subcomponent Builders allow you to specify those just like you would for a Component Builder.
You also have the choice of using a Subcomponent.Factory, analogous to Component.Factory but with all of the Builder parameters as the arguments to one method call.
Why would I inject subcomponent builders? What are the alternatives?
Though you can declare subcomponents as factory methods specified on the parent component, this prevents Dagger from pruning unused dependencies because it can't see who is requesting the subcomponent. By declaring subcomponents on modules and injecting their builders into the graph, Dagger can remove entire subcomponents if you don't call them at all.

Related

How to use AndroidInjector.Factory?

I'm migrating from Dagger 2.21 and Builder is changed to Factory. How is AndroidInjector.Factory supposed to be used? I have this component
interface AppComponent : AndroidInjector<MainApplication> {
#Component.Factory
interface Factory : AndroidInjector.Factory<MainApplication> {
fun create(#BindsInstance emailId: String) : AppComponent
}
}
But it fails with "error: #Component.Factory types must have exactly one abstract method. Already found: create(T)".
AndroidInjector.Factory already seems to have a create which binds MainApplication instance. How am i supposed to bind another value? The only workaround i found is not to extend from AndroidInjector.Factory like this but then why have AndroidInjectory.Factory?
interface AppComponent : AndroidInjector<MainApplication> {
#Component.Factory
interface Factory {
fun create(#BindsInstance instance: MainApplication,
#BindsInstance emailId: String) : AppComponent
}
}
You don't need (and as you noticed you actually can't) to extend AndroidInjector.Factory when you have additional parameters needed to create the component.
You should extend AndroidInjector.Factory when you are using dagger-android to inject dependencies into android components using AndroidInjection.inject(). To make this injection possible you need to follow some rules, in particular your component's factory should extend AndroidInjector.Factory.
AndroidInjector.Factory defines AndroidInjector<T> create(#BindsInstance T instance), which will be used by dagger-android to instantiate this subcomponent, so you can't add any additional parameters.
Update:
Dagger and dagger-android are two different libraries. Dagger is a general DI framework, it does not contain any android-specific code. Component.Factory comes from dagger itself and you are free to add a method with any number of parameters, which will be used to create your subcomponent.
Dagger-android in its turn is an android-specific library which greatly reduces boilerplate code that you need to write by generating it. But you loose some flexibility and have to follow the structure defined by the library. Here your factory method cannot have any additional parameters and the factory should extend AndroidInjector.Factory.
Generally you can also mix both approaches in one project, but I would not recommend to do so to make your code consistent.

Why Dagger Component requires #Scope annotation

When you want to provide scoped dependency, Dagger requires to annotate Component with this Scope too. What is the reason for that?
#Singleton // <- for what?
#Component(modules = [MyModule::class])
interface MyComponent {
//...
}
#Module
interface MyModule {
#Binds
#Singleton
fun provideDependency(impl: DepImpl): Dep
}
Scoping in Dagger means "give this binding the same lifetime as the component that contains it", so the component itself holds onto the instance so it can provide the same instance repeatedly.
Components are also hierarchical: through subcomponents and component dependencies you can have multiple components coexisting in the same application. In a typical Android app, you might have an Application component that provides bindings for several Activity components, and and each of those Activity components provides bindings for several Fragment components.
Though Dagger could theoretically infer scopes based on which module installs it, instead of requiring you to specify which scope applies to which component, this makes it more difficult to work with classes that declare scope and #Inject annotations without an explicit Dagger binding. If you're in an Activity component that inherits bindings from an Application component (often "singleton"), and an Activity binding relies on an #ApplicationScope-annotated class with an #Inject constructor, how would Dagger know to put it in the Application component instead of the Activity component? This might also make your code harder for humans to understand—I know I'd have trouble following it.

In Dagger, does an Inject annotated class also act as a Provider?

I am new to Dagger (version 2.16 on Android) and based on my reading so far, I understand that for a component, there should be a provider (#Provides or #Binds) encapsulated in a module (#Module). Going through a lot of samples, I see code which has some objects which are not offered in any Module, nor are they being instantiated using new.
It is also my understanding that in order to access the dependencies in a module, a consumer class needs to inject itself in the component graph (components usually offer a method to inject classes). The code examples are not doing this either.
Here's some code demonstrating both my concerns. RecipePresenter is not being provided in any module, but still RecipeActivity is using it.
A possible explanation I could think of is that #Inject, in addition to requesting the dependency also adds/injects the requesting class (RecipePresenter in the linked code) into the component graph. But assuming there are multiple components/subcomponents, which component does the class using #Inject constructor gets attached to? If my understanding is correct, why do activities and fragments have to inject themselves manually in the component - shouldn't they be auto-injected if they declare a variable annotated with #Inject?
RecipePresenter has an #Inject-annotated constructor, which allows it to be provided. The #Inject annotation on the recipePresenter field within RecipeActivity does not help, just the #Inject-annotated constructor.
class RecipePresenter #Inject constructor(
private val useCase: RecipeUseCase,
private val res: StringRetriever) :
RecipeUseCase.Callback {
From the Dagger User's Guide:
Use #Inject to annotate the constructor that Dagger should use to create instances of a class. When a new instance is requested, Dagger will obtain the required parameters values and invoke this constructor.
If the class with the #Inject-annotated constructor also has a defined scope, then the binding will only affect components with the same scope annotation: An #ActivityScope class wouldn't be accessible from a #Singleton component, for instance. If the class has no scope defined, then the binding could appear on any and all components where it is needed, which is fine: The implementation is always the same, defined by the constructor itself.
#Inject has different meanings based on what it's annotating:
When you annotate a field with #Inject, it indicates that the DI system should set that field based on values from the DI system when it injects that object.
When you annotate a method with #Inject, it indicates that the DI system should call that method with parameters based on values from the DI system when it injects that object.
When you annotate a constructor with #Inject, it indicates that the DI system is allowed to call that constructor in order to create that object (which triggers the field and method injection above). In that sense it does act like a built-in Provider.
In Dagger specifically, #Provides methods take precedence over #Inject constructors (if they both exist for the same class), and unlike in the JSR-330 spec Dagger requires an #Inject-annotated constructor even when no other constructors are present. From the Dagger User's Guide:
If your class has #Inject-annotated fields but no #Inject-annotated constructor, Dagger will inject those fields if requested, but will not create new instances. Add a no-argument constructor with the #Inject annotation to indicate that Dagger may create instances as well.
Classes that lack #Inject annotations cannot be constructed by Dagger.
Finally, to answer your question about why activities and fragments need to inject themselves: Android is allowed to reflectively create Activity/Fragment/View objects without Dagger's help or participation. This means that nothing triggers the field and method injection described above, until you call a members-injection method on the component that instructs Dagger to populate those fields and call those methods. Consequently, you should never see an #Inject-annotated constructor on Application, Activity, Service, Fragment, View, ContentProvider, and BroadcastReceiver subclasses in Android: Android will ignore the #Inject annotation, so you might as well take control over injection yourself, manually or through dagger.android.

How to create a library exposing named configured singletons generated at runtime with Dagger 2?

I'm considering migrating to Dagger 2 some libraries. This library expose a configurable client, each configuration can be named and later retrieved in a singleton fashion.
Let me show a pseudo-code of how the library works from the user perspective:
// initialization
ClientSDK clientA = new ClientSDK.Builder()
.configuration().attributes().here()
.apiKey("someString") // set api key / credentials
.build();
LibraryAuthenticationManager customAuthManager = new MyCustomAuthenticationManager();
ClientSDK clientB = new ClientSDK.Builder()
.configuration().attributes().here()
.apiKey("someStringElse")
.customAuthManager(customAuthManager) // override some default
.baseApiUrl("https://custom.domain.com/and/path") // override some default setting
.build();
ClientSDK.setSingleton("clientA", clientA);
ClientSDK.setSingleton("clientB", clientB);
And when I need an instance elsewhere:
// usage everywhere else
ClientSDK clientB = ClientSDK.singleton("clientB");
clientB.userManager(); // "singleton" using the configuration of clientB
clientB.subscriptionsManager(); // "singleton" using the configuration of clientB
clientB.currentCachedUser(); // for clientB
clientB.doSomething(); // action on this instance of the ClientSDK
ClientSDK instances are created by the user of the library and the ClientSDK statically keep a map of singletons associated to the name.
(The actual behavior of the SDK is slightly different: the naming is automatic and based on a mandatory configuration parameter.)
It's like I have lot of singleton classes with a single point of entry (the ClientSDK) but since I can have multiple configuration of the ClientSDK each with his own singletons instances this are not really singletons.
If I would try write a library like that with Dagger 2 I would do something like:
class ClientSDK {
#Inject SDKConfiguration configuration;
#Inject LibraryAuthenticationManager authManager;
...
}
The problem is that I need each instance of the ClientSDK to have its own configuration and authManager (and many other services) injected. And they need to be definable (the configuration) and overridable (the actual implementation) from the library user.
Can I do something like this with Dagger 2? How?
I've seen I can create custom Scopes but they are defined at compile time and the library user should be the one defining them.
(the library is an Android Library, but this shouldn't be relevant)
Thanks
It sounds like you should be creating stateful/configurable Module instances and then generating separate Components or Subcomponents for each ClientSDK you build.
public class ClientSDK {
#Inject SDKConfiguration configuration;
#Inject LibraryAuthenticationManager authManager;
// ...
public static class Builder {
// ...
public ClientSDK build() {
return DaggerClientSDKComponent.builder()
.configurationModule(new ConfigurationModule(
apiKey, customAuthManager, baseApiUrl)
.build()
.getClientSdk();
}
}
}
...where your ConfigurationModule is a #Module you write that takes all of those configuration parameters and makes them accessible through properly-qualified #Provides methods, your ClientSDKComponent is a #Component you define that refers to the ConfigurationModule (among others) and defines a #Component.Builder inner interface. The Builder is important because you're telling Dagger it can no longer use its modules statically, or through instances it creates itself: You have to call a constructor or otherwise procure an instance, which the Component can then consume to provide instances.
Dagger won't get into the business of saving your named singletons, but it doesn't need to: you can save them yourself in a static Map, or save the ClientSDKComponent instance as an entry point. For that matter, if you're comfortable letting go of some of the control of ClientSDK, you could even make ClientSDK itself the Component; however, I'd advise against it, because you'll have less control of the static methods you want, and will lose the opportunity to write arbitrary methods or throw exceptions as needed.
You don't have to worry yourself about scopes, unless you want to: Dagger 2 tracks scope lifetime via component instance lifetime, so scopes are very easy to add for clarity but are not strictly necessary if you're comfortable with "unscoped" objects. If you have an object graph of true singleton objects, you can also store that component as a conventional (static final field) singleton and generate your ClientSDKComponent as a subcomponent of that longer-lived component. If it's important to your build dependency graph, you can also phrase it the other way, and have your ClientSDKComponent as a standalone component that depends on another #Component.

Dagger - Inject provide beans both to field variable and constructor at same this

I have a class with the following structure.
Is it "legal" in Dagger to #Inject beans in field variables and constructors at the same time, as I have done below? If no - I have a MyActivityModule and MyApplicationModule, how can I get the dependencies from MyApplicationModule and add them to the constructor I use in the provideWhatEvery in the MyActivityModule ?
#Inject SmsFormatter mSmsFormatter;
#Inject SmsGuardiansUtils smsGuardiansUtils;
#Inject BipperMediaPlayer bipperMediaPlayer;
#Inject MixPanelUtils mMixpanelUtils;
#Inject
public ImHereController(View view, Context context, AlarmModel alarmModel, ActionBarListener actionBarListener,
FragmentController fragmentController){
super(view, context, alarmModel, actionBarListener, fragmentController);
}
You can inject fields and constructors as you have. The constructor parameters will be resolved first and injected upon construction, and after that the fields will be injected.
The other parts of your question are unclear - it doesn't matter whether you add dependencies by field injection or constructor injection - if you wanted to add them all with constructor injection you could.
The only time you must use field injection is where you have an object whose instantiation you cannot control, and therefore dagger cannot itself instantiate (like Activity and Application subtypes.)
All that said, I would not use both without some compelling reason - constructor injection is more semantically clear and you can make the instance variables final. Alternately, field injection is more terse, and possibly more readable in cases. I would pick one, and not both.