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.
Related
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.
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).
I started to migrate Dagger application to Hilt, first I'm converting AppComponent to Hilt auto-generated ApplicationComponent. Therefore I've added #InstallIn(ApplicationComponent::class) annotation to each module related to this component.
Now I get the following error:
error: [Hilt] All modules must be static and use static provision
methods or have a visible, no-arg constructor.
It points to this module:
#InstallIn(ApplicationComponent::class)
#Module
class AccountModule(private val versionName: String) {
#Provides
#Singleton
fun provideComparableVersion(): ComparableVersion {
return ComparableVersion(versionName)
}
}
Previously in Dagger, it was possible to pass arguments in the constructor. Looks like Hilt doesn't allow this.
How can I pass arguments to Hilt module?
#InstallIn(ApplicationComponent::class)
#Module
class AccountModule {
#Provides
#Singleton
fun provideComparableVersion(application: Application): ComparableVersion {
return ComparableVersion((application as MyApplication).versionName)
}
}
If you don't want to see MyApplication, then you can use an interface.
Unfortunately for now Dagger Hilt is design using monolithic component, where there's only one Application Component and one Activity Component auto generated by it. Refer to https://dagger.dev/hilt/monolithic.html
Hence the modules for it must be static and use static provision methods or have a visible, no-arg constructor.
If you put an argument to the module, it will error out stating
[Hilt] All modules must be static and use static provision methods or have a visible, no-arg constructor.
From my understanding, you'll trying to get the BuildInfo version number, perhaps the easiest way is to use the provided BuildInfo.VERSION_NAME as below.
#InstallIn(ApplicationComponent::class)
#Module
class AccountModule() {
#Provides
#Singleton
fun provideComparableVersion(): ComparableVersion {
return ComparableVersion(BuildInfo.VERSION_NAME)
}
}
If you like to set it yourselves instead of relying on BuildInfo.VERSION_NAME, you can define static const variable that exist differently across flavour.
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.
I've looked at a couple different articles which seem to suggest two different ways of doing custom scoping in Dagger 2:
MVP Presenters that Survive Configuration Changes Part-2 (Github repo):
Uses unique custom scopes for each fragment, e.g. #Hello1Scope and #Hello2Scope for Hello1Fragment and Hello2Fragment respectively
Tasting Dagger 2 on Android:
Uses a single custom scope for all fragments, e.g. #PerFragment.
From what I understand, it seems that, as in method 2, it should be okay to have a single scope defined that can be used for all fragments (i.e., #PerFragment). In fact (please correct me if I'm wrong), it seems like the name of the custom scope is irrelevant, and it's only where the subcomponent is created (i.e. in Application, Activity, or Fragment) that matters.
Is there any use case for defining a unique scope for each fragment such as in case 1?
After reading the answer by #vaughandroid, and What determines the lifecycle of a component (object graph) in Dagger 2? I think I understand custom scopes well enough to answer my own question.
First, here are a couple rules when dealing with components, modules, and scoping annotations in dagger2.
A Component must have a (single) scope annotation (e.g. #Singleton or #CustomScope).
A Module does not have a scope annotation.
A Module Method may have a (single) scope that matches its Component or no scope, where:
Scoped: means a single instance is created for each instance of the component.
Unscoped: mean a new instance is created with each inject() or provider call
NOTE: Dagger2 reserves #Singleton for the root Component (and it's modules) only. Subcomponents must use a custom scope, but the functionality of that scope is exactly the same as #Singleton.
Now, to answer the question: I would say create a new named scope for each conceptually different scope. For example, create a #PerActivity, #PerFragment, or #PerView annotation that indicates where the component should be instantiated, and thus indicating its lifetime.
Note: this is a compromise between two extremes. Consider the case of a root component and n subcomponents you will need:
at least 2 annotations (#Singleton and #SubSingleton), and
at most n+1 annotations (#Singleton, #SubSingleton1, ... #SubSingletonN).
Example:
Application:
/** AppComponent.java **/
#Singleton
#Component( modules = AppModule.class )
public interface AppComponent{
void inject(MainActivity mainActivity);
}
/** AppModule.java **/
#Module
public class AppModule{
private App app;
public AppModule(App app){
this.app = app;
}
// For singleton objects, annotate with same scope as component, i.e. #Singleton
#Provides #Singleton public App provideApp() { return app; }
#Provides #Singleton public EventBus provideBus() { return EventBus.getDefault(); }
}
Fragment:
/** Fragment1Component.java **/
#PerFragment
#Component( modules = {Fragment1Module.class}, dependencies = {AppComponent.class} )
public interface Fragment1Component {
void inject(Fragment1 fragment1);
}
/** Fragment1Module.java **/
#Module
public class Fragment1Module {
// For singleton objects, annotate with same scope as component, i.e. #PerFragment
#Provides #PerFragment public Fragment1Presenter providePresenter(){
return new Fragment1Presenter();
}
}
/** PerFragment.java **/
#Scope
#Retention(RetentionPolicy.RUNTIME)
public #interface PerFragment {}
Your understanding is correct. The named scopes allow you to communicate intention, but they all work the same way.
For scoped provider methods, each Component instance will create 1 instance of the provided object.
For unscoped provider methods, each Component instance will create a new instance of the provided object whenever it needs to inject it.
The lifetime of the Component instance is important though. 2 different instances of the same component will provide different object instances, even scoped ones.
Scope names should indicate the lifetime of the provided object (which matches that of the Component instance) so #PerFragment makes much more sense to me.
From a quick look at the "MVP Presenters..." tutorial, it's not clear to me exactly what the author's intention is with having separate scopes. Since the names are just throwaway ones, I wouldn't read too much into it.