Dagger hilt predefined components for non android scopes - android

I have the following module that is used in the data layer of my application which is a plain Android Library.
#Module
interface MapperModule {
#Binds
fun bindDomainToDataMapper(domainToDataMapperImp: DomainToDataMapperImp)
: DomainToDataMapper<TodoTaskEntity, ToDoTaskModel>
#Binds
fun bindDataToDomainMapper(dataToDomainMapperImp: DataToDomainMapperImp)
: DataToDomainMapper<ToDoTaskModel, TodoTaskEntity>
}
I am just wondering what the #InstallIn scope should be as this is the Data Layer so is not specific to any android components.
I was thinking of using #InstallIn(SingleComponent::class) but I don't want these classes to be singleton.
Any ideas of what this should be?

Hilt has predefined components for Android that are managed for you. However, there may be situations where the standard Hilt components do not match the object lifetimes or needs of a particular feature
Custom component limitations
Custom component definitions currently have some limitations:
Components must be a direct or indirect child of the
SingletonComponent. Components may not be inserted between any of the
standard components. For example, a component cannot be added between
the ActivityComponent and the FragmentComponent.
To create a custom Hilt component, create a class annotated with #DefineComponent. This will be the class used in #InstallIn annotations.
The parent of your component should be defined in the value of the #DefineComponent annotation. Your #DefineComponent class can also be annotated with a scope annotation to allow scoping objects to this component.
#DefineComponent(parent = SingletonComponent::class)
interface MyCustomComponent
A builder interface must also be defined. If this builder is missing, the component will not be generated since there will be no way to construct the component. This interface will be injectable from the parent component and will be the interface for creating new instances of your component. As these are custom components, once instances are built, it will be your job to hold on to or release component instances at the appropriate time.
Builder interfaces are defined by marking an interface with #DefineComponent.Builder. Builders must have a method that returns the #DefineComponent type. They may also have additional methods (like #BindsInstance methods) that a normal Dagger component builder may have.
#DefineComponent.Builder
interface MyCustomComponentBuilder {
fun fooSeedData(#BindsInstance foo: Foo): MyCustomComponentBuilder
fun build(): MyCustomComponent
}
While the #DefineComponent.Builder class can be nested within the #DefineComponent, it is usually better as a separate class. It may be separated into a different class as long as it is a transitive dependency of the #HiltAndroidApp application or #HiltAndroidTest test. Since the #DefineComponent class is referenced in many places via #InstallIn, it may be better to separate the builder so that dependencies in the builder do not become transitive dependencies of every module installed in the component.
For the same reason of avoiding excessive dependencies, methods are not allowed on the #DefineComponent interface. Instead, Dagger objects should be accessed via entry points.
#EntryPoint
#InstallIn(MyCustomComponent::class)
interface MyCustomEntryPoint {
fun getBar(): Bar
}
class CustomComponentManager #Inject constructor(
componentBuilder: MyCustomComponentBuilder) {
fun doSomething(foo: Foo) {
val component = componentBuilder.fooSeedData(foo).build();
val bar = EntryPoints.get(component, MyCustomEntryPoint::class.java).getBar()
// Don't forget to hold on to the component instance if you need to!
}
Conclusion:
Even if you create a custom component indirectly, it will look like a Singleton.

Even if you use #InstallIn(SingleComponent::class) without #Singleton annotation these object won' t be singleton. They will be non-scoped objects, and for every request, you will have new instance for these classes.
I was thinking of using #InstallIn(SingleComponent::class) but I don't want these classes to be singleton.
It means you can use #InstallIn(SingleComponent::class) without #Singleton annotation.

Related

How to use Provider<T> from Dagger in Hilt?

I want to use the Dagger Provider<T> interface in Hilt for async initialization as described in section Deferring Dagger Init using Provider of this article. Until now I've only worked with Hilt. How can I use this Dagger feature with Hilt? My goal is to async (non main thread) initialize a class, that will then be provided as singleton.
Hilt has predefined components for Android that are managed for you. However, there may be situations where the standard Hilt components do not match the object lifetimes or needs of a particular feature. In these cases, you may want a custom component. However, before creating a custom component, consider if you really need one as not every place where you can logically add a custom component deserves one.
For example, consider a background task. The task has a reasonably well-defined lifetime that could make sense for a scope. Also, if there were a request object for that task, binding that into Dagger may save some work passing that around as a parameter. However, for most background tasks, a component really isn’t necessary and only adds complexity where simply passing a couple objects on the call stack is simpler and sufficient. Before commiting to adding a custom component, consider the following drawbacks.
Adding a custom component has the following drawbacks:
Each component/scope adds cognitive overhead.
They can complicate the graph with combinatorics (e.g. if the component is a child of the ViewComponent conceptually, two components likely need to be added for ViewComponent and ViewWithFragmentComponent).
Components can have only one parent. The component hierarchy can’t form a diamond. Creating more components increases the likelihood of getting into a situation where a diamond dependency is needed. Unfortunately, there is no good solution to this diamond problem and it can be difficult to predict and avoid.
Custom components work against standardization. The more custom components are used, the harder it is for shared libraries.
With those in mind, these are some criteria you should use for deciding if a custom component is needed:
The component has a well-defined lifetime associated with it.
The concept of the component is well-understood and widely applicable. Hilt components are global to the app so the concepts should be applicable everywhere. Being globally understood also combats some of the issues with cognitive overhead.
Consider if a non-Hilt (regular Dagger) component is sufficient. For components with a limited purpose sometimes it is better to use a non-Hilt component.For example, consider a production component that represents a single background task. Hilt components excel in situations where code needs to be contributed from possibly disjoint/modular code. If your component isn’t really meant to be extensible, it may not be a good match for a Hilt custom component.
Custom component limitations
Custom component definitions currently have some limitations:
Components must be a direct or indirect child of the SingletonComponent.
Components may not be inserted between any of the standard components. For example, a component cannot be added between the ActivityComponent and the FragmentComponent.
Adding a custom Hilt component
To create a custom Hilt component, create a class annotated with #DefineComponent. This will be the class used in #InstallIn annotations.
The parent of your component should be defined in the value of the #DefineComponent annotation. Your #DefineComponent class can also be annotated with a scope annotation to allow scoping objects to this component.
#DefineComponent(parent = SingletonComponent::class)
interface MyCustomComponent
A builder interface must also be defined. If this builder is missing, the component will not be generated since there will be no way to construct the component. This interface will be injectable from the parent component and will be the interface for creating new instances of your component. As these are custom components, once instances are built, it will be your job to hold on to or release component instances at the appropriate time.
Builder interfaces are defined by marking an interface with #DefineComponent.Builder. Builders must have a method that returns the #DefineComponent type. They may also have additional methods (like #BindsInstance methods) that a normal Dagger component builder may have.
#DefineComponent.Builder
interface MyCustomComponentBuilder {
fun fooSeedData(#BindsInstance foo: Foo): MyCustomComponentBuilder
fun build(): MyCustomComponent
}
While the #DefineComponent.Builder class can be nested within the #DefineComponent, it is usually better as a separate class. It may be separated into a different class as long as it is a transitive dependency of the #HiltAndroidApp application or #HiltAndroidTest test. Since the #DefineComponent class is referenced in many places via #InstallIn, it may be better to separate the builder so that dependencies in the builder do not become transitive dependencies of every module installed in the component.
For the same reason of avoiding excessive dependencies, methods are not allowed on the #DefineComponent interface. Instead, Dagger objects should be accessed via entry points.
#EntryPoint
#InstallIn(MyCustomComponent::class)
interface MyCustomEntryPoint {
fun getBar(): Bar
}
class CustomComponentManager #Inject constructor(
componentBuilder: MyCustomComponentBuilder) {
fun doSomething(foo: Foo) {
val component = componentBuilder.fooSeedData(foo).build();
val bar = EntryPoints.get(component, MyCustomEntryPoint::class.java).getBar()
// Don't forget to hold on to the component instance if you need to!
}

How inject dagger non empty constructor module dependencies to hilt

Hilt is not supportting non epmty constructor modules. If we need to migrate partially to Hilt from dagger , how we can inject dependencies from legacy dagger modules having non empty constrctors to hilt components such as HiltViewModel.
// Legacy Dagger module
#Module
public class DaggerModule {
private final Boolean customBoolean;
DaggerModule(Boolean customBoolean) {
this.customBoolean = customBoolean;
}
#Provides
#Singleton
CustomClass provideCustomClass() {
return CustomClass(customBoolean);
}
}
#Module
public class AnotherDaggerModule {
#Provides
#Singleton
AnotherClassDepndsOnCustomClass provideAnotherClass(CustomClass customClass) {
return AnotherClassDepndsOnCustomClass(customClass);
}
}
// Migrated Hilt module
#HiltViewModel
class HiltViewModel #Inject constructor(
  private val anotherClass: AnotherClassDepndsOnCustomClass
) : ViewModel() {
  ...
}
Since we are not using components to pass some custom parameters while initialising modules, is there any solution which I'm not aware already exists?
While running the app, the app crashing with error DaggerModule must be set.
You'll need to refactor.
The documentation on Migrating to Hilt describes this case under the heading "Handling Component Arguments", since instantiable modules would otherwise be treated as component arguments passed through a Builder or Factory:
Hilt components cannot take component arguments because the initialization of the component is hidden from users. [...]
If your component has any other arguments either through module instances passed to the builder or #BindsInstance, read this section on handling those. Once you handle those, you can just remove your #Component.Builder interface as it will be unused.
Under "Component arguments" the Hilt documentation confirms that a refactor is required:
Because component instantiation is hidden when using Hilt, it is not possible to add in your own component arguments with either module instances or #BindsInstance calls. If you have these in your component, you’ll need to refactor your code away from using these.
You can consider some of these structures:
Replacement module / subclassing Modules
In your example, you might need to create a replacement Module that provides a binding for CustomClass. This might be as straightforward as subclassing the Module and providing a public no-arg constructor that provides the super(value) constructor call your module needs. If your Module would only ever get a single value in your graph (but might get a different value in a separate application), then this might be enough.
#Module
public class AdaptedDaggerModule extends DaggerModule {
AdaptedDaggerModule() {
super(true);
}
}
Note that module subclasses are somewhat limited in utility, and should not be used for testing overrides.
Custom subcomponents
However, you also wrote "components are created in respective modules where we need to use" in a comment, and you can continue doing so using custom subcomponents in Hilt, with more comprehensive documentation in javadoc or the main Dagger subcomponent documentation. Because you would create this component through an explicit call to a Builder or Factory, you could provide the Module instance there. Subcomponents inherit bindings from their parent components, so you could avoid specifying your entire list of Modules.
Note that doing this as a subcomponent is mostly valuable when you have a dense tree with multiple references to the instance you're providing in the constructor. If this is simply a matter of combining graph-based constructor arguments with one-off constructor arguments, assisted injection is probably a better option.
/** Subcomponents are usually declared on modules. You can also reuse one you have. */
#Module(subcomponents={YourSubcomponent.class})
public interface IncludeThisInYourHiltModuleList {}
#Subcomponent(modules={DaggerModule.class, AnotherDaggerModule.class})
public interface YourSubcomponent {
AnotherClassDepndsOnCustomClass anotherClass();
#Subcomponent.Builder
interface Builder {
Builder daggerModule(DaggerModule daggerModule); // arbitrary name
YourSubcomponent build(); // arbitrary name
}
}
#HiltViewModel
class HiltViewModel #Inject constructor(
private val yourSubcomponentBuilder: YourSubcomponent.Builder
) : ViewModel() {
fun yourMethod() {
val subcomponent =
yourSubcomponentBuilder.daggerModule(DaggerModule(false)).build()
val anotherClass = subcomponent.anotherClass()
// ...
}
}
Constructor values in Hilt-managed components
The most difficult case would be where your Module would want separate values in each of your Hilt-managed components, e.g. each Activity needing to pass a different constructor argument. In that case you might need to rephrase the customBoolean (or other parameters) as deriving the value from the Activity instance itself. This maintains Hilt's expectation that it can create an Activity component for each Activity instance that Android unpredictably creates or recreates, and it can do so without specifying any other constructor parameters.

Circle features dependency with subcomponents

In my current Android project I have a feature A that display feature B, and now I need to be able to display feature A from feature B. Which create a circle feature dependency, generating a StackOverflow error on build time.
#Subcomponent(modules = [SubComponentA.Module::class])
interface SubComponentA {
fun plus(module: Module): SubComponentB
#dagger.Module
class Module {
// Provide stuff
}
}
-------------
#Subcomponent(modules = [SubComponentB.Module::class])
interface SubComponentB {
fun plus(module: Module): SubComponentA
#dagger.Module
class Module {
// Provide stuff
}
}
Is there a way to achieve this Dagger graph without a build time error?
Thanks!
If A produces B and B produces A, I imagine it would be difficult to get an instance of either one to act as the other's parent. That's not a problem, though: Dagger components do not have to represent the exact same ownership and access chain that your model objects or application UI represents. The important part of the Dagger graph is whether the objects you want are directly injectable and whether they have the correct Dagger-managed lifetime ("scope").
You clarified in the comments:
To add more context: Feature A is an article that can open another article or a Feature B, which is a detail view of a Hike. Inside the Feature B (Hike detail) we can access to an article (Feature A) and so on.
If the Article and Hike aren't directly related to each other in a nesting or ownership sense—you might start the app and navigate directly to either Articles or Hikes—then I would have the main Component act as the owner of both Subcomponents, such that neither Subcomponent is the parent of the other. Because Subcomponents can access all the bindings of their parent component tree, you'll be able to inject a SubcomponentA builder/factory1 from Component, SubcomponentA, or SubcomponentB, and you'll likewise be able to inject a SubcomponentB builder/factory from Component, SubcomponentA, or SubcomponentB. You won't be able to get to SubcomponentA bindings from SubComponentB (i.e. get to Article subcomponent Dagger bindings from the Hike subcomponent) or vice versa, but of course you can use a Module field or #BindsInstance binding to pass details about the Article or Hike you just navigated from. You could even pass the subcomponent instance itself, but in your position I'd probably just keep data model objects or identifiers to avoid keeping a long memory-expensive chain of objects.
If it is the case that Articles have zero or more Hikes and every Hike has exactly one Article, and that the Hike has reason to directly access all the Dagger bindings ("ArticleInteractionLogger", maybe) associated with its parent Article, then that's a good reason that SubcomponentB would be a subcomponent of SubcomponentA. However, then you won't be able to get to a Hike (SubcomponentB) instance without first getting an Article (SubcomponentA) instance, and navigating to a different Article means you would not inject the bindings directly from the Hike subcomponent you were just in.
All that said, it sounds like your motivation for subcomponents is cross-navigation, in which case I'd just leave the Dagger object graph out of it, have both Subcomponents installed on the parent Component, and save the history elsewhere—as subcomponent #BindsInstance fields or in a separate NavigationHistoryManager class of your own design.
Note 1: You're using the plus abstract factory method model from Dagger 1, but it is more idiomatic to define a Builder or Factory that you can directly inject. This avoids having to keep or inject the Component or Subcomponent instance directly to get to the plus method (which could be named anything). However, to use this you'll need to specify the Subcomponent in the subcomponents attribute of the #Module annotation for a Module on your parent Component.
#Subcomponent(modules = [SubComponentA.Module::class])
interface SubComponentA {
// Remove: fun plus(module: Module): SubComponentB
#dagger.Module class Module { /* ... */ }
#Subcomponent.Factory
interface Factory {
fun create(module: Module): SubComponentA
}
}
#Subcomponent(modules = [SubComponentB.Module::class])
interface SubComponentB {
// Remove: fun plus(module: Module): SubComponentA
#dagger.Module class Module { /* ... */ }
#Subcomponent.Factory
interface Factory {
fun create(module: Module): SubComponentB
}
}

Un-scoped binding in modules vs Constructor injected binding in Dagger

I have an unscoped binding inside the appModule and the same class as constructor injected using the Singleton scope. when I add a declaration inside the appComponent for Foo, the generated code picks up the module binding without any DoubleCheck i.e unscoped binding over the constructor injected Singleton binding why is it so?
#Module
public class AppModule {
#Provides
public Foo provideFoo() {
return new Foo();
}
}
#Component(module = AppModule.class)
#Singleton
public interface AppComponent {
Foo getFoo();
}
#Singleton
class Foo #Inject constructor(){
//..
}
There is some hierarchy in how dependencies are provided, I haven't found documentation on this yet, but I ran into the same thing as well. If I recall it correctly from my testing, the hierarchy of where Dagger tries to get the dependency from is the following:
Bound instances (via component builder methods; i.e. #BindsInstance)
Module provision
Constructor injected classes
Somewhere in this list provisioned dependencies should probably also be included (provisioned from a parent component your component has a dependency on), but I dont know where it would be then in the hierarchy.
Usually there is no need to provide the same dependency in multiple ways, why would you both want to provide the dependency via a Module and via constructor injection? If there are multiple dependencies, but they live in a different scope, try using different scopes instead of just the singleton one.
I can think of one reason where you want to use this mechanic, and thats when you normally want a certain dependency to be provided via either constructor or module injection, but specifically for a test build, you want to overwrite that dependency with a different dependency by changing how the component is composed in a test build (i.e. then changing the DaggerComponent so it requires a #BindsInstance method to overwrite the default dependency with a test dependency).

What is the purpose of adding scope modifiers to #Components in Dagger2?

Say I have a class (or a #Provides method) annotated with #Singleton. This makes sense to me, whenever a dependency is provided from this class, the same instance will always be returned.
What I am trying to understand is why there is then a requirement for components that use that module to also be marked as #Singleton. The purpose of adding a scope to a component does not make sense to me. A component is an interface, that dagger uses to implement the actual injection.
If I try to compile my app with my dependency class marked as #Singleton, but do not have #Singleton marked on a component that injects this dependency, I get an error.
com.example.whatever.MyComponent (unscoped) cannot depend on scoped components
Adding #Singleton to the component makes this error go away, but I want to understand why. Can anyone help me understand this? Thanks!
Adding #Singleton on your component allows you to use #Singleton on your provider methods in your module you specify for your component. Scoped providers make it so that you can have only one instance from that given module without any additional magic beyond what Dagger2 generates for you.
#Singleton
#Component(module={BlahModule.class})
public interface SingletonComponent {
Blah blah();
}
#Module
public class BlahModule {
#Provides
#Singleton
public Blah blah() {
return new BlahImpl();
}
}
According to the Dagger 2 design doc, it is meant to make intention clear ("a declaration of intention") and to allow for better compile-time checking.
That declaration enables dagger to enforce the following constraints:
A given component may only have bindings (including scope annotations
on classes) that are unscoped or of the declared scope. I.e. a
component cannot represent two scopes. When no scope is listed,
bindings may only be unscoped.
A scoped component may only have one
scoped dependency. This is the mechanism that enforces that two
components don’t each declare their own scoped binding. E.g. Two
Singleton components that each have their own #Singleton Cache would
be broken.
The scope for a component must not appear in any of its
transitive dependencies. E.g.: SessionScoped -> RequestScoped ->
SessionScoped doesn’t make any sense and is a bug.
Singleton is
treated specially in that it cannot have any scoped dependencies.
Everyone expects Singleton to be the “root”.
This also makes some sense given the implementation of Dagger 2: Because Scopes are implemented via memoization and the user is responsible for instantiating a new Component instance for every new scope, then it makes sense that the Component has a lifetime (and set of provisions) to which the scope also applies.

Categories

Resources