Dagger Parent Component scoping - android

If I use Core-component as parent component of my AppComponent , and the scope for the AppComponent is singleton. Is all the dependency derive from corecomponent would be singletone ? I mean Only one instance will be created for parent provided dependencies or multiple ?

#Singleton does not mean "a single instance" as we know it from the desing pattern.
Basically when we talk about singletons we think about a Single instance created through the lifecycle of the application.
When we say #Singleton in Dagger - it is just "a name of a scope". Basically it means - a single instance created through the lifecycle of that Component.
That is the reason some people do not use #Singleton, but cretate #PerApp annotation so it us more clear and obvious.
My point is - you are pointing out the parent has a #Singleton scope. It doesn't matter. It could be the #Whatever scope. What you need to care about is the following:
If dependencies comming from a parent Component are not scoped(you only put #Provides, but no #SomeScope annotation) every time this dependency is used(provided) somewhere it will be a new instance.
On the other hand - if the parent Component has some scoped dependencies like in your case #Singleton or like I said: #WhateverScope - while the Component is alive it will provide only one instance for the time you are able to create, destroy, recreate many child components.
But if you recreate the parent, even if you scope it with #Singleton - you will have a 2nd instance of the scoped dependencies for the same application lifecycle.

Related

Dagger2 scope, instance per component

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.

Creating all modules for DI in the Application class for Android

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.

No injector factory bound with #ContributesAndroidInjector

Please don't mark this duplicate, I've read all the other answers about this issue. I'm not asking what the issue means, I'm asking why this particular code produces this error.
I'm trying to make an Activity component/injector that uses SessionComponent as its parent:
AppComponent:
#Singleton
#Component(modules = [AppModule::class, AndroidSupportInjectionModule::class])
interface AppComponent {
#Component.Builder
interface Builder {
#BindsInstance
fun application(ltiApp: LTIApp): Builder
fun build(): AppComponent
}
SessionComponent:
#SessionScope
#Component(
dependencies = [AppComponent::class],
modules = [SessionModule::class, CommentaryModule::class, EducationCenterModule::class])
interface SessionComponent {
EducationCenterModule
#dagger.Module
abstract class EducationCenterModule {
#EducationScope
#ContributesAndroidInjector()
abstract fun educationCenterActivity(): EducationCenterActivity
}
How come I get an error for injector factory even though I have #ContributesAndroidInjector inside a Module?
Caused by: java.lang.IllegalArgumentException: No injector factory
bound for
Class
If possible, move your #ContributesAndroidInjector into your AppModule, which will likely involve some refactoring between AppComponent and SessionComponent.
dagger.android injects Activity instances by calling getApplication() on the Activity, casting that to a HasActivityInjector, and calling activityInjector().inject(activity) on it (code). In turn, apps that use DaggerApplication (or the code on the dagger.android how-to page) will inject a DispatchingAndroidInjector<Activity>, which injects a Map<Class, AndroidInjector.Builder> that is built using multibindings. Though it's possible to inject into this map directly, you may also use #ContributesAndroidInjector (as you have done here) as a shortcut that produces the multibinding and subcomponent.
Though you have #ContributesAndroidInjector bound inside a #Module, you have two top-level components: AppComponent and SessionComponent. dagger.android is not prepared for this: Your Application likely uses AppComponent for its injection, and because you haven't installed EducationCenterModule into AppComponent, the multibound map will not contain the binding that your #ContributesAndroidInjector method installs.
This probably requires some refactoring, but for important reasons: Through intents, back stacks, and activity management, Android reserves the right to recreate your Activity instance whenever it wants to. Though your Application subclass likely guarantees that an AppComponent will exist by then (by creating and storing that component within onCreate), there is no such guarantee that your SessionComponent will exist, nor any established way for an Android-created Activity instance to find the SessionComponent that it can use.
The most common way to solve this problem is to separate the Android lifecycle from your business logic, such that dagger.android manages your Android components on their own lifecycle, and those Android components create/destroy SessionComponent and other business logic classes as needed. This may also be important if you ever require Service, ContentProvider, or BroadcastReceiver classes, as those will definitely only have access to the application, and may restore or create sessions of their own. Finally, this also means that a Session will necessarily last longer than an Activity instance, which might mean that your Session will not be garbage collected until Android destroys your Activity, and may also mean that you have multiple concurrent SessionComponent instances.
Edit/elaboration: First and foremost you'll need to decide whether sessions outlive Activities, Activities outlive Sessions, or neither. I bet it's "neither", which is fine: at that point I'd write an injectable #Singleton SessionManager that goes inside AppComponent and manages the creation, recreation, and fetching of SessionComponent. I'd then try to divide it so most of the business logic is on the SessionComponent side, and by calling sessionManager.get().getBusinessObject() you can access it from SessionComponent. This also works well to keep you honest, since the business logic side might be easy to unit test using Robolectric or Java, while the Android side might require instrumentation tests in an emulator. And, of course, you can use #Provides methods in your AppComponent modules to pull out your SessionComponent, BusinessObject, or any other relevant instance out of the SessionManager side.
However, if you are 100% sure that you want SessionComponent to be the container for your Activity, and that you don't mind managing the session creation and deletion in your Application subclass. If that's the case, then rather than using DaggerApplication, you can write your Application subclass to implement HasActivityInjector (etc), and delegate the activityInjector() method to a class on a SessionComponent instance that you create. This means that AppComponent would no longer include AndroidSupportInjectionModule, because you no longer inject DispatchingAndroidInjector or its Map. However, this is an unusual structure with implications for your application, so you should consider your component structure carefully before proceeding and document it heavily if you choose the non-standard route.

Dagger2 sub-component confusion in Android

My Goal :
To understand how scope works and how to Implement a UserScope that I can use over multiple Activities and reset/create a new one as required.
Methods I am using :
This Blog: http://frogermcs.github.io/building-userscope-with-dagger2/
It apparently explains the same thing that i am trying to achieve here.
Official Docs
http://frogermcs.github.io/building-userscope-with-dagger2/
Quick brief on Blog
Obviously, There is UserModule and UserComponent. Author has wrapped the creation of UserComponent under UserManager which has ApplicationScope. So UserManager is available at time of log in. when login is successful UserComponent is initialized via UserManager. Simple logic.
Now this already initialized #UserScope is used in couple of Activities, as you can see in the picture.
What I am struggling to understand
Take a look at UserComponent.
public interface UserComponent {
#Subcomponent.Builder
interface Builder {
Builder sessionModule(UserModule userModule);
UserComponent build();
}
UserDetailsActivityComponent plus(UserDetailsActivityComponent.UserDetailsActivityModule module);
RepositoriesListActivityComponent plus(RepositoriesListActivityComponent.RepositoriesListActivityModule module);
LogoutManager logoutManager();
}
Specifically UserDetailsActivityComponent and RepositoriesListActivityComponent are created through UserComponent. Like this,
#Override
protected void onUserComponentSetup(UserComponent userComponent) {
userComponent.plus(new UserDetailsActivityComponent.UserDetailsActivityModule(this)).inject(this);
}
So they first get pre-created in UserComponent through UserManager and then it calls onUserComponentSetup which then creates the appropriate Component and injects the current Activity.
I fail to comprehend with this pattern mentioned above, as I have read in the docs that we use plus(InjectionToBeDoneOn i) when we need the injection on a particular instance of InjectionToBeDoneOn. But why inject this Activity via this Component? What does this accomplish? Wouldn't it make sense to do this the conventional way in onCreate() of the activity with DaggerXYZComponent().Builder().Build().inject(activity)?
Also, I am missing decent material of how UserScope is implemented in Android which has life span from log-in to log-out but not bigger than the #Singleton scope.
we use plus(InjectionToBeDoneOn i) when we need the injection on particular instance of InjectionToBeDoneOn
Not quite. A component has basically 3 kinds of methods
SomeDependency provideDependency() which just creates / provides some dependency to subcomponents, or for manual retrieval (basically a getter)
void inject(MyAndroidFrameworkClass object) that injects an object with its dependencies
SomeSubComponent plus(SubComponentModule module) that creates a subcomponent, adding additional modules
You're mixing up 2. and 3. here.
// user scoped component
userComponent
// create a subcomponent (UserDetailsActivityComponent)
.plus(new UserDetailsActivityComponent.UserDetailsActivityModule(this))
// use the UserDetailsActivityComponent that was just created and inject with it
.inject(this);
UserDetailsActivityComponent is a subcomponent of UserComponent, which is why the userComponent gets extended .plus(somemodule) to create a subcomponent. If your submcomponent does not need additional modules you can also just use .plus() because to Dagger the important thing is the return type or signature in general.
If it returns another component, then it creates a SubComponent.
If it hast one parameter and returns void or the parameters type, then it is an inject method
If it has no parameters and returns some type is is a provides method (1.) to expose some dependency
but why inject this Activity via this Component? What does this accomplish?
If you were to create UserDetailsActivityComponent from scratch, it would only see and know about what it can provide itself. If you have some #Singleton somewhere it could not access any of it, because it is not part of the object graph.
A subcomponent extends another component, adding to the object graph. If you have a #Singleton A and your UserComponentn needs A to provide B, with a subcomponent this will work, without it you will get a cannot be provided error.
Dagger is no magic. It really just builds up a directed graph and checks whether everything is fine. It will complain if some dependencies have cyclic dependencies on one another or if some part of the graph doesn't have access to dependencies it need.
Your UserComponent holds your userdata. For simplicity lets say it holds the UserName. Now UserDetailsActivity might want to display UserName, but it needs some way to get it.
By using the #Singleton AppComponent as a parent you'd have access to some Apis, but not the user scoped ones. You could move the user scoped objects into the #Singleton AppComponent, but then you'd either have to recreate the AppComponent every time the user changes (which kind of defeats the purpose of declaring it #Singleton, or you'd have to find some other means to update / change the user.
If you want to do it the Dagger way, you create a UserComponent that adds the User to the graph. That way subcomponents can access it and do their user things.
When the user changes you have to make sure to destroy any activities / fragments that used the UserComponent, and you just recreate everything—with a new user.
wont it make sense to do in conventional way in OnCreate() of the activity with DaggerXYZComponent().Builder().Build().inject(activity)
You can do that of course. The author just put the calls to app.getAppcomponent().getUserManager().getUserComponent() or something like this into their BaseActivity and BaseUserActivity so that you wouldn't have to repeat the same lines of code every time. This method will basically still be called in onCreate, it just enables you to use the components directly, without fetching them every single time.
You can obviously remove those template methods and inline everything in onCreate, leading to duplicated code, making maintenance harder in the long run.
i am missing decent material of how UserScope is implemented in android which has life span from log-in to log-out but not bigger than #SingleTon scope.
Android doesn't help and it's your job to clean up after yourself. If the user changes you purge everything the UserComponent and its SubComponents touched, and recreate it with the new user.
You will have to store the UserComponent with the current user either in the Application class, some "real" singleton, or some "Manager" in the application component with a #Singleton scope. Every approach has their own benefits I guess.
Scopes just point out to Dagger that one object should exist within a Scope only once. It doesn't matter what you name it, or how many objects are in the Scope. Dagger only needs them to ensure that there are no dependency cycles.

Dagger 2 singleton not working

We are creating a dagger 2 dependency graph
SessionComponent (Session scope) --dependson---> Appcomponent (Singleton scope) ---dependson---> UserMangerComponent (unscoped... suppose to be singleton)
However, when I inject userManager (a dependency provided by UserManagerComponent), It is not being maintained as singleton. Every injection is creating a new UserManager. Please help ...
I cannot set singleton scope to the dependency.
Your proposed structure is incompatible with how Dagger manages scope. Only one component in your app should ever be #Singleton because each binding within a component that is not exposed via a component interface might be #Singleton, but entirely encapsulated in the component implementation. Thus, each component would hold its own instance and you'd end up with 2 instances rather than one.
Either merge the two components into one #Singleton component, create a new scope for your user management, or implement the instance management for your user manager by hand.

Categories

Resources