I have a class other than an Activity with injected fields :
public Class1 extends Class2 {
#Inject A a;
}
public Class2 extends Class3 {
#Inject B b;
}
public Class3 {
#Inject C c;
}
And I am providing Class1 like this:
#Provides
Class1 provideClass1(){
return new Class1();
}
My question : when providing Class1 will Dagger inject B and C at the level of the super class? Or should I use constructor injection and call super inside the constructors?
Activity, Fragment, and Service are ideal sites for property injection because you have no control over their constructor and their instantiation is handled by the Android OS.
For the rest of your classes, constructor injection gives much more control and makes it easier to unit test. When writing a unit test, you simply pass in the test doubles you want when you are initialising the system under test. You also make it easier to write immutable classes fulfilling Effective Java item 15.
As for your other question, Dagger 2 is smart enough to inject properties in a super class. In other words, if you have a ConcreteService extends BaseService and you call component.inject(this) inside ConcreteService Dagger 2 will know to inject the properties of BaseService if they are annotated correctly.
However, in your example you have a mix of property injection (the #Inject annotations inside Class1 and Class2) and constructor injector (the #Provides method for Class1 which simply uses the constructor). This is not enough to have all the properties in the super classes of Class1 to be injected because you have specified the medium of provision of Class1 to be a simple call to the constructor. On the other hand, if you obtained the instance of Class1 through a component (through provision methods) or through requesting injection on a class which has a #Inject annotated Class1 property then you would get a fully-formed instance.
Related
I have been using Dagger 2 in my project. I understand that the lifetime of scoped object is the same as the lifetime of component (with the same scope). What about the lifetime of component then?
For example, I have a component:
#MyApp
#Component(modules = {
ApplicationModule.class})
public interface ApplicationComponent {
// Injection methods
void inject(MyApplication mainApplication);
}
I build component by:
public class MyApplication extends Application {
#Override
public void onCreate() {
super.onCreate();
buildApplicationComponent();
mApplicationComponent.inject(this);
}
private void buildApplicationComponent() {
mApplicationComponent = DaggerApplicationComponent.builder()
.applicationModule(new ApplicationModule(this))
.build();
}
}
Currently, the code to build component is in Application class. But
is it so that if I build component in Fragment the ApplicationComponent would have the same lifetime as the fragment & if I execute it in Application class the component would have the same lifetime as the whole application? Or how the lifetime of component is defined?
It is plain Java. You have object no matter where. The object provides you dependencies. If you use the same instance you will have the same dependencies or basically the same "Scope/lifetime". If you create somewhere new object it means new other object\dependencies it can provide so "another Scope/lifetime". If you share the object between different objects (You create it in the Application class, but reuse it in another fragment) you are in the "same Scope/lifetime".
But in general I see only "old way" of using Dagger 2 here. This is how your classes should end up everywhere. No need to find reference to any Components and etc or try to instantiate them on your own or clear them on your own in Fragments or Actvities. You have some code for Dagger 2 in one place and then some Annotations in you classes "which do the real work of your project". No need for complicated "connection" between the two parts and I see here a lot of cases like this which are part of tutorials that date back to 2016-2017...
class Repostory #Inject constructor(
private val dependency1: Dependency1
) {}
class Activity or Fragment {
#Inject lateinit var dependency2: Dependency2
}
This is a great example for right usage of Dagger2.
https://github.com/google/iosched
Here is an article about the app:
https://medium.com/#JoseAlcerreca
Jose Alcerreca is one of the Google Lead Devs responsible for creating the guidelines to write Android apps.
#Module
abstract class ActivityBindingModule {
#ActivityScoped
#ContributesAndroidInjector(modules = [MainActivityModule::class])
internal abstract fun mainActivity(): MainActivity
}
Just try to compile something like this. There is the exaxt same class in the app I gave you. Check the generated code and also the docs. If you see ContributesAndroidInjector there is explained:
Generates an {#link AndroidInjector} for the return type of this method. The injector is
implemented with a {#link dagger.Subcomponent} and will be a child of the {#link dagger.Module}'s component.
If you check the AndroidInjector docs you will see that it is the one that is injectin the Activity. And there it will be the implementation:
#Subcomponent(modules = {MainActivityModule.class})
#ActivityScoped
public interface MainActivitySubcomponent extends AndroidInjector<MainActivity> {
#Subcomponent.Builder
abstract class Builder extends AndroidInjector.Builder<MainActivity> {}
}
So in general these annotations do the magic for you when a new activity starts. They create the Subcomponent, they use it and when the Activity Lifecycle ends the Subcomponent is also dead. Dagger clears the reference. Dagger knows how Activities work or Fragments and etc. There was the old way when you craete the component in the Activity, use it, call inject on your own. Or put some component in the AppClass and then clear it on your onw. But now there is a bigger amount of annotations.
one object if injected into 2 subcomponents under same custom scope, every time new instance is created of that object. I want same instance to be passed to all subcomponents
this is the module
#CustomScope
#Module
public class EventBusModule {
PublishSubject<Boolean> bus = PublishSubject.create();
#CustomScope
#Provides
public PublishSubject<Boolean> provideRxBus() {
return bus;
}
}
these are my subcomponents
#Module
public abstract class ActivityBindingModule {
#CustomScope
#ContributesAndroidInjector(modules = {HomeActivityModule.class,
EwayBillFragmentProvider.class, EventBusModule.class})
abstract HomeActivity mainActivity();
#CustomScope
#ContributesAndroidInjector(modules =
{EwayBillDetailActivityModule.class, EventBusModule.class})
abstract EwayBillDetailActivity ewayBillDetailActivity();
}
these subcomponents are written inside ActivityBindingModule which is added to my application component. Now I want same instance of my PublishSubject object in both the subcomponents, I am fairly new to dagger and I want to know what am I doing wrong?
You'll need to move your bus into Application scope, which typically means annotating it with #Singleton (if that's how you've annotated your top-level component that ActivityBindingModule is installed into). You'll also need to move your method into a Module installed on that component, which might as well be ActivityBindingModule.
#Module
public abstract class ActivityBindingModule {
#Singleton
#Provides
public PublishSubject<Boolean> provideRxBus() {
// Dagger stores the instance in your Application component, so you don't have to.
return PublishSubject.create();
}
/* ... your #ContributesAndroidInjector Activity bindings remain here ... */
}
First, an explanation of what you see: #ContributesAndroidInjector creates a subcomponent for each object it annotates, marked with the scope annotations and modules you put on the #ContributesAndroidInjector method and annotation, so that your call to AndroidInjection.inject(this) in onCreate creates a new instance of that subcomponent and uses it to inject the Activity instance.
Your #CustomScope (which may be better-named as #ActivityScope here) on the #Provides PublishSubject<Boolean> method means that your instance will share the same lifecycle as the component that is also annotated with that scope annotation. Here, that's each automatically-generated subcomponent. Furthermore, because your Module is a non-abstract class with public no-arg constructor, Dagger will automatically create a new instance every time it creates a Component that requires your module, which means a different bus for each Activity instance. (It can't and won't do so for Modules that are abstract classes or interfaces.)
You want your bus object to be the same instance between Activities, which means that #CustomScope/#ActivityScope is much too short: You want the object to outlast any single Activity's lifecycle. This means that you'll either need to store the instance elsewhere and pass it into each Activity, or you'll need to store the instance in your Application component itself. I'd recommend the latter, because this is one of the problems Dagger was created to solve, and because this will automatically make the bus available across your application: Dagger subcomponents inherit access to all of the bindings in their parent components. That gives the code you see above. (Note that by doing this, you'll keep the instance of PublishSubject around even when there is no Activity showing, when your application is running in the background; if you want the same instance between Activities, this is a necessary consequence, but choose this carefully to avoid too much background memory use.)
One alternative is that you keep track of the bus instance yourself, and insert it into each Activity. You could do this by having your Module take a parameter, but that is rather tricky to do with dagger.android (which powers #ContributesAndroidInjector). You could also write a #Provides method that delegates to a WeakReference, or use the #Singleton technique above to write a holder that temporarily stores your bus between Activities. However, because Android keeps a lot of control over your transitions between Activities and the Activity lifecycle, it may be the best you can do to keep the bus in #Singleton scope as I did in the code above.
Is there a way in Dagger2 or in Dagger2 Android Injection support to inject the member instances without specifying the class names of the fragments.
I have a modular project where
The following line is asking to provide a binder Factory for the injectable class.
#Override
public void onAttach(Context context) {
AndroidSupportInjection.inject(this);
super.onAttach(context);
}
But my intention is to provide the injecting members through different modules in the project, where the I wouldn't need to specify the class name of the Fragment at all.
Is this possible in Dagger2 injection or not?
Is there a way in Dagger2 or in Dagger2 Android Injection support to inject the member instances without specifying the class names of the fragments.
tl;dr No. You always need a component that contains a .inject(FragmentInQuestion fragment) method for every Fragment you want to inject.
Dagger uses annotation processing at compile time to resolve all of your .inject(SomeFragment fragment) methods where it looks up fields annotated with #Inject and creates code to inject them.
It needs the actual class name, since it will only generate code for the class used with its fields.
class Fragment {
// no #Inject annotated fields, part of framework!
}
class SomeFragment extens Fragment {
#Inject SomeDependency dep;
}
class OtherFragment extens Fragment {
#Inject OtherDependency dep;
}
Declaring one generic component that injects all your fragments is impossible.
.inject(Fragment fragment) would not inject any properties, since Fragment does not contain any #Inject annotated fields. So neither of the dep fields in the above sample would be provided and they both would be null.
You could create a BaseFragment that contains common objects and write an injection for it, but again, any annotated fields of its children would not be supplied.
You could try some other workarounds, but in the end it would always mean that you'd be injecting or be working with a base type. While this might be feasible in some situations, I don't think it would work on more than some special edge cases.
The Android Injection part of Dagger 2 takes this approach and creates a generic interface that your components have to implement along with a Builder to bind the type in question.
interface AndroidInjector<T> {
inject(T instance)
}
By implementing this interface AndroidInjection can then look up (and create) the correct component and inject your Fragment. For the reasons mentioned above this will always be the actual class of your Fragment, and not some base type.
So by using the Android Injection part you won't be able to use some common base class, and even without it you'd have a hard time.
If you're looking for a way to move out the call of AndroidInjection.inject(this) to somewhere else you should look at the Google Android Architecture Components sample project where they use FragmentLifecycleCallbacks to inject the fragments globally at the right time.
The project I'm working on has a number of utility classes that require activity context.
I don't want to have to declare a new #Provides method for each activity that uses the dependency. i.e. I don't want this:
#Provides
static Navigator providesNavigator(ActivityOne activity) {
returns new Navigator(activity);
}
// ...and in another module
#Provides
static Navigator providesNavigator(ActivityTwo activity) {
returns new Navigator(activity);
}
So instead I declare these utilities in a single ActivityUtilitiesModule and pass our BaseActivity which all other activities extend. Now i don't have to declare my Navigator dependency x number of times.
#Provides
static Navigator(BaseActivity activity) {
return new Navigator(activity);
}
However, Dagger does not know how to satisfy the dependency for BaseActivity. This means for every Activity i need to create a provides method that will satisfy the BaseActivity dependency with the specific Activity being used. e.g.:
#Provides
static BaseActivity providesBaseActivity(ActivityOne activity) {
return activity;
}
This is better - I only need to repeat this one provider per activity, rather than repeating a provider for every utility class per activity, but it still feels like an unwelcome additional step in Dagger set up, and another thing that makes the code harder to understand.
Is there a pattern which allows me to avoid having to supply this BaseActivity provider per activity?
Please use Constructor Injection. Having provide* methods that only call the constructor are only noise and code to maintain. Add #Inject to the class constructor and possible scopes onto the class.
#SomePossibleScope
class Navigator {
#Inject
Navigator(Activity activity) { }
}
If you do this, you probably don't need your ActivityUtilitiesModule at all.
If your class depends on an Activity or BaseActivity then you need to provide it. And yes, you will have to tell Dagger about it in some way.
If you were to use an abstract class or interface you should make use of #Binds instead.
#Binds BaseActivity bindsBaseActivity(ActivityOne activity);
Compared to #Provides Dagger might optimize this code further, reducing the number of method calls and object creations, as well as a few less lines of code.
I don't know why your Utils depend on the Activity, but if they would only need a Context then you could also just provide the application context to them without a need to bind or provide your actual Activity.
I personally just bind the current Activity to the types it implements using the syntax above. And if you're using Constructor Injection properly that's more often than not the only lines of code that you'll find in my modules, making them very readable.
I have some misunderstandings of the way that dagger works:
There are only two ways to satisfy dependency: whether the #Provide method returns the instance, or the class should have #Singleton annotation, is that right? Must the class constructor have #Inject annotation in the latter case?
As I see, the ObjectGraph generates all the injection stuff. And it's said, that its inject(T instance) should be called to inject fields. However, I can just annotate my field with #Inject and it goes (field's class is #Singletone). ObjectGraph is not needed to satisfy such a dependency, right?
What about injects{} in #Module, what specifically does it give? Plz, provide an example of benefit when you keep all the injectable classes list.
Yes, there are two ways: #Provide methods in modules and #Singleton classes with #Inject at constructor.
Must the class constructor have #Inject annotation in the latter case?
Yes, otherwise object wouldn't be created by Dagger.
I don't think #Singleton with field injection could work. Because #Singleton at class with constructor injection means Dagger is responsible to keep one instance of this class. And it can create this class using constructor injection if all dependencies are satisfied. However, #Singleton with field injection seems misuse to me, cause keeping single instance of this class is user's responsibility now. Dagger can't instantiate this object itself.
Are you sure this configuration compiles and runs? And if it is check #Inject fields, they should be null by my understanding.
injects={} in #Module returns set of classes passed to ObjectGraph.inject(T class) or ObjectGraph.get(Class<T> class). Referring documentation:
It is an error to call ObjectGraph.get(java.lang.Class) or ObjectGraph.inject(T) with a type that isn't listed in the injects set for any of the object graph's modules. Making such a call will trigger an IllegalArgumentException at runtime.
This set helps Dagger to perform static analysis to detects errors and unsatisfied dependencies.
You can find some examples in this thread.