Without using FragmentFactory, scoping dependencies within Fragment is straigth forward. Just create a Subcomponent for your fragment and just create the Fragment's Subcomponent in onAttach().
But when using FragmentFactory you no longer inject dependencies via the subcomponent but instead pass then in Fragment's constructor.
I was wondering if I could still declare a dependency which should only last within the fragment's lifecycle using Dagger. I currently could not think of a way to achieve this.
So instead of binding my dependencies to a certain scope, I just declare the dependency with any scope or just use #Reusable on them.
Also, since fragments are created through FragmentFactory, the created Fragments doesn't exist in the DI graph.
How can we properly scope dependencies to fragment and also be able to add the fragment in the DI graph when using FragmentFactory?
You can accomplish this by making a Subcomponent responsible for creating your Fragment. The Fragment should have the same scope as this Subcomponent.
#Subcomponent(modules = [/* ... */])
#FragmentScope
interface FooSubcomponent {
fun fooFragment(): FooFragment
#Subcomponent.Factory
interface Factory {
fun create(): FooSubcomponent
}
}
After taking care of any cyclic dependency issues, this Subcomponent acts the same as if you had explicitly created a subcomponent in onAttach() using #BindsInstance, except that you can (and must) now use constructor injection on FooFragment.
In order to provide FooFragment in your main component (or parent subcomponent), you will need to install this Subcomponent into a module.
#Module(subcomponents = [FooSubcomponent::class])
object MyModule {
#Provides
#IntoMap
#FragmentKey(FooFragment::class)
fun provideFooFragment(factory: FooSubcomponent.Factory): Fragment {
return factory.create().fooFragment()
}
}
Some caveats:
The scenario you describe (FooFragment depends on some class which depends on FooFragment) is the definition of a cyclic dependency. You will need to inject a Lazy or Provider at some point in this cycle, or Dagger will throw an error at compile time.
If you split this up into two steps, first providing FooFragment and then binding it into your map, the subcomponent will see the #Provides method from its parent component. This is likely to cause a StackOverflowError if you aren't careful.
Related
I've multiple modules in my app, each one of them have his own Module for UI Injection.
Now, i want to have "feed" fragment that have a some pieces from other modules.
So i trying doing it using FragmentContainerView.
I want to Inject the fragment that defined on other module. I'm trying to "include" my other fragment, so i can navigate into this fragment, but if i'm try to inject it, i'm getting this following error:
error: [Dagger/MissingBinding] DotsFragment cannot be provided without an #Inject constructor or an #Provides-annotated method. This type supports members injection but cannot be implicitly provided.
Any idea how can i do it?
HomeModule:
#Module(
includes = [
DotsModule::class
]
)
abstract class HomeModule {
}
DotsModule:
#Module
abstract class DotsModule {
#FragmentScoped
#ContributesAndroidInjector
abstract fun contributeDotsFragment(): DotsFragment
}
HomeFragment:
#Inject
lateinit var dotsFragment: DotsFragment
Just push the Fragment into the container as described in the docs, and don't worry about the injection. You don't even need the explicit inclusion of DotsModule from HomeModule, though you may choose to keep it, especially if HomeModule is in a different Activity. DotsFragment will inject itself in onAttach.
Unlike most classes that use Dagger injection, Fragments can't be injected in their constructors: Android will only call the zero-arg or one-arg Fragment() constructor, so there isn't room for other constructor parameters. When you see your abstract fun contributeDotsFragment() function with #ContributesAndroidInjector, it is not the case that DotsFragment is available on your graph to be constructed or provided. Instead, Dagger does some magic1 that allows the Fragment to have its #Inject-labeled fields populated2 once you call AndroidSupportInjection.inject(this) in onAttach, which might happen in DaggerFragment if you inherit from that. Your Fragment has to assume that anything marked #Inject is null until the Fragment is attached.
Consequently, if you need an instance of DotsFragment, you can just call DotsFragment() or delegate to your FragmentManager's FragmentFactory (which will likely just call DotsFragment()). You can also use overloads to FragmentTransaction.replace(...) that take a Class<Fragment> rather than a Fragment instance.
The Fragment will inject itself later in onAttach, as it would if Android itself called the constructor. If you interact with the Fragment before that, be careful, as its #Inject-annotated fields will not be populated yet. If you need to interact with the Fragment in ways that use Dagger-provided instances, it may be best to create a Bundle that the Fragment can use to initialize itself in onCreate.
1: Behind the scenes, dagger.android creates a code-generated subcomponent instance, probably called DotsModule_BindDotsFragment.DotsFragmentSubcomponent, and registers that subcomponent as an AndroidInjector.Factory<DotsFragment> in a Map. The generated subcomponent receives all the modules you set on the #ContributesAndroidInjector annotation as well as all the scope annotations you set on the abstract fun. In onAttach (yours or DaggerFragment's), the call to AndroidSupportInjection.inject(this) reads from that Map, finds the Injector/Subcomponent, creates an instance, and uses that to inject the Fragment with its own set of #FragmentScope instances.
2 The #Inject-annotated methods will be called too. That's method injection, compared to field injection; the general term is members injection for all of the post-constructor initialization Dagger can do.
In an Android project, there is a facade implemented as the singleton. I thought it is an better idea converting it to DI with HILT SingletonComponent.
#Module
#InstallIn(SingletonComponent::class)
object MyManagerModule {
#Provides
fun provide(#ApplicationContext context: Context): MyManager {
return MyManager(context)
}
}
class MyManager #Inject constructor(#ApplicationContext private val ctx: Context){
//
}
From a few callers, I get the instance of the above MyManager using HILT field injection, i.e.,
#AndroidEntryPoint
class MyCallerFragment : Fragment() {
#Inject lateinit var myManager: MyManager
// ...
In the debugger, I observed the DI instances are actually NOT the same instance (assuming these fragments are in the same activity lifecycle). I think I must have misunderstand the Hilt DI :-( and would love to hear any explanation if you see my blindspots.
TL;DR
You need to use the annotation #Singleton. This will tell Hilt to use the same instance of MyManager throughout your app.
Scope of Bindings
According to the documentation:
By default, all bindings in Hilt are unscoped. This means that each time your app requests the binding, Hilt creates a new instance of the needed type.
and
However, Hilt also allows a binding to be scoped to a particular component. Hilt only creates a scoped binding once per instance of the component that the binding is scoped to, and all requests for that binding share the same instance.
The #Singleton annotation scopes your Hilt binding to the application component. (including all children, which are all components) So Hilt will inject the same instance of your object throughout your entire app.
There is an example in this guide by Google.
The #InstallIn annotation
The annotation #InstallIn() tells Hilt in which component MyManager objects will be injected.
In your case of #InstallIn(SingletonComponent::class), Hilt will make MyManager available for injection in the application component and all children of this component, which doesn't mean that Hilt will supply the same instance though. Since any default component is a child of the application component, MyManager is currently accessible for injection in any component. (according to the documentation)
I had kinda the same question, Scope vs Component in Hilt, and this is what I got:
When you annotate a class with #InstalIn, actually you are defining the lifetime of this dependencies' module. For example, if you use SingletonComponent all dependencies from this class(Module) are stay as long as the application is up.
And for Scope, when you provide a dependency in a module, each time that module is called, a new instance is created. this is why you observed in debugger generated instances are not the same. by using Scopes like #Singleton you are changing the way of instantiation of dagger-hilt on called dependencies.
so generally speaking, Component is for instance lifetime and Scope is for the way of dependency instantiation on each call.
don't miss this article:
https://medium.com/digigeek/hilt-components-and-scopes-in-android-b96546cb07df
I'm looking for a quick confirmation about Dagger 2 scopes in Android.
In many resources online you will find that #ActivityScope and #FragmentScope are added to components that provide bindings for activities and fragments.
I would like to have some confirmation that this implies that there will be 1 instance for all activities / all fragments respectively.
That is, if, say, two activities use the same component for receiving dependencies from the same component annotated with scope 'Activity', both activities will receive the same instance (like singleton annotation would work).
So in that case having #ActivityScope and #FragmentScope annotations would only be useful to segregate between dependency lifetimes between activities versus fragments.
So if I would need a dependency object for which I need a separate instance in two activities, I should scope them explicitly (e.g. #LoginActivityScope).
Could you confirm that this assumption is correct?
Edit:
Reading the docs about subcomponents, it confuses me a bit:
No subcomponent may be associated with the same scope as any ancestor
component, although two subcomponents that are not mutually reachable
can be associated with the same scope because there is no ambiguity
about where to store the scoped objects. (The two subcomponents
effectively have different scope instances even if they use the same
scope annotation.)
This would seem to assume that if you have multiple components using the same annotation, it does create a separate instance when the same scope annotation is used for different components.
I find it a bit unclear as to what a scope instance refers to. This actually refers to the binding?
Does this only apply to subcomponents?
Some clarification about scope vs dependency instances (bindings) would be very helpful.
A scoped component will create a scoped object the first time it is used, then it will hold on to it. If you create the same component a second time it will also create the scoped object the first time it gets used. Components are just objects, they don't hold any global (static) state, so if you recreate the component, you recreate everything along with it.
val component = DaggerScopedComponent.create()
component.getScopedObject() === component.getScopedObject() // always the same object!
// never the same object! two different components, albeit same scope
DaggerScopedComponent.create().getScopedObject() != DaggerScopedComponent.create().getScopedObject()
Dagger generates code, so I would invite you to create a simple example and have a look at the code. e.g. the sample above should be very easy to read
#Singleton class Foo #Inject constructor()
#Singleton #Component interface ScopedComponent {
fun getScopedObject() : Foo
}
If you have a scoped component that lives longer than its subscopes then you have to keep a reference to this component and reuse it. The usual practice is to hold a reference to the component in the object whose lifecycle it shares (Application, Activity, Fragment) if needed.
Let's say we add a subcomponent to the example above
#Singleton class Foo #Inject constructor()
#Singleton #Component interface ScopedComponent {
fun getScopedObject() : Foo
fun subComponent() : SubComponent
}
#Other #Subcomponent interface SubComponent {
fun getScopedObject() : Foo
}
#Scope
#MustBeDocumented
annotation class Other
As long as we use the same #Singleton component we will always get the same #Singleton scoped objects.
// Subcomponents will have the same object as the parent component
component.subComponent().getScopedObject() === component.getScopedObject()
// as well as different Subcomponents
component.subComponent().getScopedObject() === component.subComponent().getScopedObject()
Now on to your questions...
I would like to have some confirmation that this implies that there will be 1 instance for all activities / all fragments respectively.
That is, if, say, two activities use the same component for receiving dependencies from the same component annotated with scope 'Activity', both activities will receive the same instance (like singleton annotation would work).
As shown above, any scoped object provided from the same scoped component will be the same no matter which subcomponent. If you create two #ActivityScope MyActivityComponent then everything scoped #ActivityScoped will be created once per component.
If you want objects to be shared between your Activities' components you have to use a higher scope and keep the reference to the created component.
So in that case having #ActivityScope and #FragmentScope annotations would only be useful to segregate between dependency lifetimes between activities versus fragments.
No, because you can have a #ActivityScope FooActivityComponent and a ActivityScope BarActivityComponent and they would never share a #ActivityScope class FooBar object, which will be created once for every #ActivityScope scoped component.
So if I would need a dependency object for which I need a separate instance in two activities, I should scope them explicitly (e.g. #LoginActivityScope).
#ActivityScope FooActivityComponent and #ActivityScope LoginActivityComponent will never share any #ActivityScope scoped objects. You can use the same scope here. You can also create a different scope if you like to do so, but it would make no difference here.
This would seem to assume that if you have multiple components using the same annotation, it does create a separate instance when the same scope annotation is used for different components.
Yep
I find it a bit unclear as to what a scope instance refers to. This actually refers to the binding? Does this only apply to subcomponents?
You can't have a hierarchy of components like Singleton > ActivityScope > ActivityScope since those duplicated scopes would make it impossible to know whether a #ActivityScope scoped object was part of the first or second one.
You can have two different components of the same scope, both subcomponents of the same parent (they can't "reach" each other), and any #ActivityScope scoped object would be part of the latter #ActivityScope scoped component. You'd have one scoped object per component (as shown in the example above) and you could have two component instances or more.
Singleton > ActivityScope FooComponent
Singleton > ActivityScope BarComponent
I recommend you forget about Android for a bit and just play around with Dagger and the generated code, like with the code shown on top. This is IMHO the quickest way to figure out how things work, once the "magic" is gone and you see that it's just a POJO with a few variables in it.
One of the things I've noticed a lot of developers do is to create a class that inherits from Application and then create a component through dependency injection that includes virtually all the modules that make up their app. This is done in the onCreate method. I find this rather strange. Why would you want to inject every module into an Application class and make it globally available. After all, most of the modules like presenters are bound to a single activity and will never be used for any other activity. So why would you not just create a component in the activity and only include those modules you need, which in the case of an activity would be a presenter class.
I'm not sure I agree with the premise: Most applications create a component in Application#onCreate, but I believe that most applications also have separate components that contain per-activity, per-fragment, or per-service bindings, and those components/modules only exist and are only classloaded when you use the specific activity/fragment/service in question.
Scope and lifecycle
Dagger manages object lifecycle ("scope") through separate components, each of which can have its own set of modules. You annotate your component with one or more scope annotations, and then any bindings you annotate with the same scope (or any classes with that scope annotation and #Inject-annotated constructors) will be created exactly once and stored within the component. This is in contrast to Dagger's default behavior, which is to call a #Provides method or create a new object instance for each call to a component method or each #Inject-annotated field. You are in control of when you create a component instance, so you can control the semantics of your scope: If you were to create a scope annotation called #PerActivity, and you create a new component instance for each Activity instance Android creates, then you can be sure that any bindings marked #PerActivity will return the same instance across the lifetime of that Activity. Likewise, you might create a #UserScope where every user gets a separate component instance. The only standardized scope in JSR-330 is #Singleton, which should apply to the entire application.
However, what if you want to mix scopes, such as having a #PerActivity StatusBarPresenter depend on a #Singleton LoginService? Dagger requires you to keep those in two separate components, such that StatusBarPresenter might be defined in an #PerActivity ActivityComponent and LoginService might be defined in a #Singleton ApplicationComponent. You would need to establish a relationship between this ActivityComponent and ApplicationComponent, which can be done through either components with dependencies or subcomponents.
Components with dependencies
Components with dependencies receive separate code generation, and list their dependencies in the dependencies attribute on the #Component annotation. At that point, you'll need to specify an instance of that Component on their
#Singleton #Component(modules = {FooModule.class, BarModule.class})
interface ApplicationComponent {
Foo foo();
// Bar also exists, but is not listed. Let's say Foo uses it internally.
}
#PerActivity #Component(
modules = {BazModule.class},
dependencies = {ApplicationComponent.class})
interface ActivityComponent {
Baz baz();
}
ActivityComponent activityComponent =
DaggerActivityComponent.builder()
.applicationComponent(yourExistingApplicationComponent)
.build();
ActivityComponent receives its own code generation step, and can compile in parallel with ApplicationComponent; however, ActivityComponent can only access dependency Foo and not Bar. This is because ActivityComponent has ApplicationComponent listed as a dependency, and and ApplicationComponent doesn't list Bar. So Baz, defined on the ActivityComponent, can automatically inject Foo but cannot inject Bar. In fact, if Foo stopped consuming Bar, ApplicationComponent may not even generate the code to create Bar at all.
Subcomponents
In contrast, subcomponents are generated as a part of their parent component, such that the parent component acts as a factory.
#Singleton #Component(modules = {FooModule.class, BarModule.class})
interface ApplicationComponent {
Foo foo();
// This is a subcomponent builder method, which can also return a
// #Subcomponent.Builder. More modern code uses the "subcomponents" attribute
// on the #Module annotation.
ActivityComponent createActivityComponent();
}
#PerActivity #Subcomponent(
modules = {BazModule.class},
dependencies = {ApplicationComponent.class})
interface ActivityComponent {
Baz baz();
}
ActivityComponent activityComponent =
yourExistingApplicationComponent.createActivityComponent();
// or, from somewhere that ApplicationComponent injects:
#Inject Provider<ActivityComponent> activityComponentProvider;
ActivityComponent activityComponent = activityComponentProvider.get();
For subcomponents, the implementation of ActivityComponent is generated at the same time as ApplicationComponent, which also means that ApplicationComponent can evaluate ActivityComponent's needs when it is generating code. Consequently, the code for creating Bar instances will be included in ApplicationComponent if either ApplicationComponent or ActivityComponent consumes it. However, this build step may become slow, because Dagger will need to analyze your entire application's dependency graph.
Application#onCreate
All of this gets back to Application#onCreate, and to what you're seeing:
If you have application-scoped singletons, you probably need to get a hold of them from the application (though technically you could also use a static field).
If you have bindings that are used across your application, even if they're unscoped, you may want to install them in ApplicationComponent just so you don't have to repeat the same binding in each component.
If you're using subcomponents, then all of your code generation for the whole app is generated in one step at the Application level even though the code is generated as separate classes loaded at separate times. Because the application component acts as a factory, the extra classes are hidden, because your one and only reference to a generated class name may be for the ApplicationComponent instance ("DaggerApplicationComponent").
This may result in a smoother developer experience, because you don't need to worry about listing a dependency on the ApplicationComponent interface if you want to access it from an ActivityComponent.
This is also nice for Android, because in the subcomponent case Dagger has more information about which bindings are required, so it can sometimes produce more compact code than components-with-dependencies.
If you're using dagger.android and #ContributesAndroidInjector, you are using subcomponents with some syntactic sugar on top. Note that #ContributesAndroidInjector can be annotated with scope annotations, and can take a list of modules which will be passed along to the subcomponent that it generates. Your call to AndroidInjection.inject(this) will create one of those subcomponent instances, classloading the subcomponent and its modules as needed.
So even though you may have very specific components with different lifecycles, it may look like all of your Dagger configuration happens in ApplicationComponent and Application#onCreate and nowhere else.
How to declare activities and fragments for non-base feature modules?
As we had only a base feature module and a single feature module, we could declare the App class in the feature module and had our graph be loaded there. This meant being able to use ContributesAndroidInjector and the standard dagger approach for android.
Now, adding more feature modules, we can't do the same. The application class must stay in the base feature module, which means it can't declare, for example, activities belonging to features.
My thoughts:
Since AndroidInjection.inject(this) will look for the activity injector in the app class, we can't use it. In other words, no DaggerAppCompatActivity.
So the idea is that activities belonging to feature modules should, instead, create their own component and inject themselves.
Still, it should be possible for fragments to use the #ContributesAndroidInjector thing. Right? The AndroidInjection class will get the injector from the parent activity, so if we fix our activities, they will expose the correct injector so that fragment code can be left as is.
For this reason, the feature activities must implement HasFragmentInjector and have a #Inject annotated DispatchingAndroidInjector for fragments.
But two things don't work here.
Feature fragments and activities still need some #Singleton annotated objects from the base feature component graph, so our SpecialFeatureComponent must somehow be linked to the BaseFeatureComponent.
It seems that the only way of doing so is by using the dependencies parameter:
#Component(
dependencies = [BaseFeatureComponent::class],
modules = [SpecialFeatureModule::class] // #contributes fragment
)
interface SpecialFeatureComponent
The SpecialActivity creates this component, passes the BaseFeatureComponent to its builder, and injects itself.
However, compilation fails due to MissingBinding errors. Some of the objects in the special feature module need #Provide, #Singleton annotated objects from the base feature component, and dagger doesn't seem to find them properly. (these objects are not in BaseFeatureComponent, but rather in its attached modules)
How to fix this?
I have read that exposing them directly in BaseFeatureComponent, rather than in its dependency modules, should fix the issue, but it's not something we would like to do, as there are lots of them and it would be yet another list to be maintained.
As is, the unscoped SpecialFeatureComponent depends on the #Singleton scoped BaseFeatureComponent. This is not possible, so we have to add a ActivityScope annotation.
#ActivityScope
#Component(
dependencies = [BaseFeatureComponent::class],
modules = [SpecialFeatureModule::class] // #contributes fragment
)
interface SpecialFeatureComponent
Now we are told that the subcomponents generated by #ContributesAndroidInjector for fragments, unscoped, may not reference scoped bindings. So we add a #FragmentScope annotation there.
#Module
abstract class SpecialFeatureModule {
#FragmentScope
#ContributesAndroidInjector
internal abstract fun specialFragment(): SpecialFragment
}
Now we are told that these subcomponents may not reference bindings with a different scope! Which are #Singleton objects provided by the base feature graph.
How to fix this?