Dagger2, destruction of a custom scoped module - android

While using Dagger2 I defined #ActivityScope
#Scope
#Retention(RUNTIME)
public #interface ActivityScope { }
and instantiated corresponding ActivityModule in Activity.onCreate() as following.
mActivityComponent = DaggerActivityComponent.builder()
.activityModule(new ActivityModule(this))
.build();
By doing this, I attach module's lifecycle to the lifecycle of the activity. All module's instances will be created for every new activity. In that respect everything works just fine.
My actual question is what is the right/best way to destroy module's components (e.g. remove service callbacks etc.) when activity gets actually destroyed? Does Dagger2 itself offers anything for this?

#Scope annotations provide both compilation time validation, and on module provider methods, it makes Dagger2 create a scoped provider which will allow the creation of only one instance per scope within that particular component.
It doesn't really do anything more than that, so if you need to explicitly unregister callbacks that are within your activity, then you should probably inject these dependencies within your activity, and manually unregister them yourself in onDestroy().

Related

Dagger Parent Component scoping

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.

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.

dagger2 activityscope providing same object of injected dependencies with different activities

Following code creates & shares same object of injected dependencies(ViewsApiEnd ) with different activities rather than creating different per activity.
#ActivityScope
#Component(dependencies = HttpComponent.class, modules = ViewsApiModule.class)
public interface ViewsApiComponent {
void inject(MainActivity activity);
void inject(SecondActivity activity2);
}
module:
#Module
public class ViewsApiModule {
#Provides
#ActivityScope
public ViewsApiEnd providesGitHubInterface(Retrofit retrofit) {
return retrofit.create(ViewsApiEnd.class);
}
}
Scope:
#Scope
#Retention(RetentionPolicy.RUNTIME)
public #interface ActivityScope{
}
Full Source: Github(If anyone could fork & correct scope issue it'll be helpful. Also checke void logInstances() for verifying object creation )
How should I declare ViewsApiComponent if I want dagger2 to create different objects of injected deps( ViewsApiEnd) for different activities(MainActivity , SecondActivity )?
I'm assuming by different object you mean different instances and not different implementations.
The key to this is scopes and how you manage your components. In particular ViewsApiComponent.
Let's start with scopes. A scope in dagger is nothing but an annotation that tells dagger that during the life of a given component, as long as an object annotated with a given scope is required, the instance provided will always be the same. In other words, within the same component scoped dependencies will be singleton.
In your code, this ActivityScope is the scope tied to ViewsApiEnd. Now what people usually don't get about scopes is the first part of - "... as long as the component is alive scoped dependencies are singleton". Basically this says, if your component's instance remains the same, then all your scoped instances will be the same.
In your code this is the case because you pin mViewsApiComponent to the application class and you never recreate it. So in your activities when you do this:
// ...
((MyApp) getApplication()).getGitHubComponent().inject(this);
// ...
You're always using the same component, hence same scopes, hence same instance of ViewsApiEnd.
You need to make sure the scoped dependencies have a proper scoped handling. If you want a dependency that is different per activity, then you want to manage the component that takes care of this dependency on a per activity basis.
There's different ways of doing this. I think the simplest one in your case is to move the creation of ViewsApiComponent to your activities. Make sure you nullify the component in onDestroy. Something like this:
private ViewsApiComponent mViewsApiComponent;
#Override
public void onCreate() {
super.onCreate();
getComponent().inject(this);
}
#Overrides
public void onDestroy() {
super.onDestroy();
mViewsApiComponent = null;
}
private ViewsApiComponent getComponent() {
if (mViewsApiComponent == null) {
mViewsApiComponent = DaggerViewsApiComponent.builder()
.httpComponent(((MyApp) getApplication()).getNetComponent())
.build();
}
return mViewsApiComponent;
}
You can even put this code in a base activity and inherit from it in each activity.
Important thing to notice is that the component is now managed by the activity, or better put it's tied to its life-cycle. This ensures the component lives during the life time of the activity and all scoped dependencies are the same within that activity. For another activity a new component will be created and the new object instances will be used, but within the activity they will remain the same.
Now I've explained all of this just for the sake of trying to help you out with components and scopes, but truth is that what you're currently providing in the component's modules are all objects that should indeed be singleton throughout the app's life. So in fact, the way you have it now is the most correct approach at least in my opinion.
Hope this helps

Categories

Resources