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!
}
Related
Please someone explain me why when we use dependency injection and initialize the object in the module the provides function return type is interface but in body function we return the actual object. This is example
#Provides
#Singleton
fun providePrefsManager(#ApplicationContext context: Context): PrefsManager {
return PrefsManagerImpl(context)
}
Why here we return PrefsManager instead of PrefsManagerImpl ?
Code to the interface, not the implementation. #Provides methods provide according to their return values, so if you return PrefsManagerImpl, Dagger will only know how to inject PrefsManagerImpl. By returning PrefsManager, Dagger lets you inject PrefsManager directly, so the injecting class doesn't need to be aware of PrefsManagerImpl or any other implementation at all.
More specifically to dependency injection: The concept behind dependency injection is that, for the class you're writing, it's the caller or DI framework that controls which instance or implementation your class receives. This is an "inversion of control" compared to a self-contained class that maintains complete control of which classes or dependencies it uses.
As such, the class you're writing should be as general as possible when specifying its dependencies, which gives you flexibility about which implementations you can supply.
For example: If you need a sort algorithm, it would defeat the flexibility of dependency injection if you always asked specifically for a hypothetical MyBinarySortImpl; instead, you should make your request more general, such as injecting an interface like BinarySorter or Sorter (both also hypothetical). Your caller or dependency injection framework can still supply a MyBinarySortImpl, but by being as general as possible you also free your caller to supply a WellTestedNativeBinarySortImpl or a VeryFastRadixSortImpl. If your implementation needs to be a binary sort, you can specify that, and if it doesn't you can leave it general.
In your specific case, your #Provides method provides a binding of PrefsManager; the implementation happens to be a PrefsManagerImpl. However, the class that consumes PrefsManager is asserting that it doesn't need anything specific to PrefsManagerImpl, it can work when it only uses the interface as described through PrefsManager. That injecting class can now be tested using a FakePrefsManager or a mocking-framework-created mock(PrefsManager), or even a wrapper you could write like LoggingPrefsManager(PrefsManagerImpl(context)). By operating through interfaces rather than implementations, it keeps your options open.
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.
So I am currently in the process of learning dagger 2, and from the tutorials that I've read so far, for a dependency to be injected, the #Inject annotation gets placed inline with fields (for Activities/Fragments) or constructors. However I see that as an issue if I'm not the owner of parts of the code and can't add the required annotations for this technique to work, or if I don't want other parts of the code to know that dagger exists.
The application structure I have at the moment is:
App Module - where I'd like to put my DI code in (e.g. dagger modules, etc).
Presentation Module - Views/ViewModels etc.
Domain Module - Use Cases etc.
Data Module - Repositories etc.
With pretty much this style of classes contained in my application:
class ExampleViewModelImpl(useCase: ExampleUseCase): ViewModel() in Presentation (gets initialised from an Activity or similar).
class ExampleUseCaseImpl(repository: ExampleRepository): ExampleUseCase in Domain
class ExampleRepositoryImpl(dao: ExampleDao): ExampleRepository in Data
With the structure above, what is the minimum number of classes outside of the App Module that I need to touch in order to utilize dagger with as much automated dependency injection as possible? Code examples of how this is achieved would be great.
I am unsure of some terminologies, and wasn't able to find a solution online. If there are good resources which explains what I'm asking, that would also be great.
if I don't want other parts of the code to know that dagger exists.
#Inject is a standard (JSR 330) which Dagger implements. Adding those annotations doesn't have anything to do with Dagger and can be used the same way with other DI frameworks. If it's your code you should just add those #Inject annotations where appropriate. Think of them as documentation: What constructor/field/method must be injected to create & use this object?
The only place where your classes will know that Dagger exists is at the same place where you'd be creating the objects otherwise, too.
Going down that path, of course you could use Dagger without any #Inject annotations, but you'd be writing a lot of unnecessary boilerplate and missing out on the most powerful feature of Dagger at the same time (code generation).
#Inject annotation gets placed inline with fields (for Activities/Fragments) or constructors. However I see that as an issue if I'm not the owner of parts of the code and can't add the required annotations for this technique to work
That's what #BindsInstance with the #Component.Builder is for (add an object to the component) and what #Provides annotated methods are for (create and initialize an object from a module)
If you really want to write code without #Inject, then you'd do exactly this for all of your objects. This means a lot of modules, and even more #Provides annotated methods. It will work, but I don't see the point in writing all those methods if a single #Inject on the constructor has the same effect.
In my opinion the best thing about Dagger is that I can add / remove / change constructor parameters and don't have to touch any other parts of my code since Dagger will generate new code with the new arguments. In your case you'd have to also change the parameters to the #Provides method as well as the constructor invocation.
Next let's look at how to remove #Inject from fields. Basically you don't want to do field injection, so instead of writing an injection method in the component, you'd write provision methods.
#Component
class MyComponent {
fun inject(activity: MyActivity)
}
class MyActivity {
#Inject lateinit var myDep: Dependency
fun onCreate() {
component.inject(this)
}
}
Removing the #Inject we need to use the provision methods instead.
#Component
class MyComponent {
fun provisionMyDependency() : Dependency
}
class MyActivity {
lateinit var myDep: Dependency
fun onCreate() {
myDep = component.provisionMyDependency()
}
}
It will work and everything, but again, you will miss out on the single best feature of Dagger: Code generation. The example above looks alright because I only added a single dependency, but think about what happens to those 2 different implementations when you add / remove / change dependencies, how well it will scale. If you prefer to do things manually any refactoring will become arduous.
With the structure above, what is the minimum number of classes outside of the App Module that I need to touch in order to utilize dagger with as much automated dependency injection as possible?
Your question (especially the title) is in direct conflict with your goal. If you don't want to use those annotations, then you can't use Dagger code generation & injection but have to resort to do it manually as highlighted above.
with as much automated dependency injection as possible
To best utilize Dagger you add #Inject on the constructor and/or fields of every class that should end up on your dependency graph and let Dagger do its thing.
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.
I’m new to Dagger 2. I have this scenario, I wan't to inject an object across my app (in presenters, in api)
I do not have a way to provide it initially. It is not created till after authentication at some stage in my app.
From the documentation http://google.github.io/dagger/
I see Lazy loading might be a way to solve this e.g
#Inject
Lazy<Grinder> lazyGrinder;
and then get the value like this using:
lazyGrinder.get().grind();
My questions are:
Can I safely swap the object after this with a new one?
Are there any other recommended ways to do this?
Thanks
This isn't a good match for Lazy. Lazy is a great way to delay expensive object initialization, but it implies some semantics that you don't want or need, particularly regarding the "safely swap" behavior you want.
To put it simply, Lazy is a Provider wrapper that memoizes locally:
If you never call get, Dagger never creates the object in question.
The first call to get creates and stores the object instance.
The second call to get returns the same instance, and so on forever, regardless of whether the object was marked as Singleton.
This makes Lazy an excellent choice for an expensive object that would otherwise be a field (but may never be used). However, if the reference is likely to change (as your will), Lazy will simply be confusing: It will store the value at first use and never locally update, so multiple out-of-date copies might be floating around in your application regardless of what the "right" value is at any given time.
To borrow the use of Grinder from your example, better solutions include:
Using a #Provides method that returns a field in a Module, which can be updated later. You'll need to inject Provider<Grinder> for every long-lived object instance, because injected references to Grinder alone won't update. This still might be the best bet if you have a lot of short-lived objects.
The reference is implicitly singleton, but is not annotated as such, because you're controlling the instance yourself. Dagger will call your getGrinder method frequently.
#Module public class YourModule {
private Grinder grinder;
public void setGrinder(Grinder grinder) {
this.grinder = grinder;
}
#Provides public Grinder getGrinder() {
return grinder;
}
}
/* elsewhere */
YourModule module = new YourModule();
YourComponent component = DaggerYourComponent.builder()
.yourModule(module)
.build();
/* ... */
module.setGrinder(latestAndGreatestGrinder);
As EpicPandaForce mentioned in the comments, create/bind a singleton GrinderHolder, GrinderController, or AtomicReference object that provides the current instance and allows for updating. That way it's impossible to inject a Grinder directly, but easy and obvious to inject the object that fetches the current correct Grinder. If your singleton GrinderHolder implementation doesn't create the Grinder until the first time you ask for it, then you have effectively created a Lazy singleton on your own.
If you aren't able to provide the object at the time of Component creation, don't add it to your Component graph! That is asking for confusing graph dependencies and inconsistency. A better solution to what you are considering is a #Subcomponent approach, which allows you to create a new component which inherits the dependencies from the parent, but also adds new one. Here's an example:
#Component
interface RegularComponent {
#AppInstanceId String appInstanceId(); // unique per app install; not related to logging in
AuthenticatedComponent newAuthenticatedComponent();
}
#Subcomponent
interface AuthenticatedComponent {
Set<Friend> friends();
#AccountId String accountId();
}
Here, the #AccountId in the subcomponent could use the appInstanceId to provide the account ID (if it needed to) since the Subcomponent shares dependencies with its parent component.
If you need to supply state to your modules for the subcomponent (with the accountId, auth token, etc) feel free to pass it in as a parameter to the #Module and store it in a private final field. You can read more on how to supply subcomponent modules in the documentation.