I am using Dagger2 for DI. How should I group classes into a Component?
Does Dagger satisfy dependencies of all classes defined in a single #Component interface at runtime?
For example, if I have the following component:
#Component(modules = {ModuleA.class, ModuleB.class, ModuleC.class})
public interface Component {
ClassA getClassA();
ClassB getClassB();
ClassC getClassC();
}
Here ClassA, ClassB and ClassC have overlapping dependencies but not exactly same.
Now when I call:
ClassA classA = DaggerComponent.create().getClassA();
Does Dagger2 create objects to satisfy all classes present in the Component or the classes only to satisfy ClassA?
Analyzed the files created by Dagger on build.
It does in fact create the objects required only by classA when the following statement is called:
DaggerComponent.create().getClassA();
Related
This could be a completely backwards design but I wanted to know if this is possible. I just started on a project that has an existing framework called Core. Core contains protocols and when Core is loaded, it does this
for className in classNames {
let clazz = objc_getClass(className)
...
}
Basically in the Core module it has an array of classNames that are just strings and then creates the class with objc_getClass()
All of these classes conform to CoreType protocol.
Let's say I have another smaller module, let's call it SubCore that pulls in as a dependency Core, and has its own class that conforms to CoreType.
Core has CoreType protocol and instantiates the classes when the SDK is loaded
SubCore depends on Core with Carthage and defines MyClass that conforms to CoreType
let classNames = ["MyClass", ...] // classes that conform to CoreType that are defined in Core, and one class, MyClass, that is defined in SubCore
for className in classNames {
let clazz = objc_getClass(className)
...
}
In an example app, I pull in both Core and SubCore through Carthage and try to load the SDK. When the above block is run, clazz is nil for the className "MyClass".
I'm guessing this is because MyClass is defined in SubCore and Core does not know what SubCore is. Is that correct? More importantly, is there a way to do have an interface defined in Core, have SubCore define a class that conforms to that protocol, and have it instantiated when Core is loaded like I try to above?
From the dagger 2 Documentation I noticed that you can have a #Singleton annotated class. What is the purpose of marking a class as #Singleton as I have tried to do this in my code but a singleton object is NOT produced. I'm not clear on what use marking my class with this annotation serves.
From the documentation please focus on the following statement:
The #Singleton annotation on an injectable class also serves as
documentation. It reminds potential maintainers that this class may be
shared by multiple threads.*
#Singleton
class CoffeeMaker {
// ...
}
UPDATE: After reviewing froger_mcs answer I see that in Dagger 2 you can provide injections either by a module OR by a constructor injection. So the following class, although not in a module, can be injected:
#Singleton
public class MyClass {
#Inject
public MyClass() {
}
}
In this version the constructor is injected for us and in an Android activity you would just do the following and it will get provided:
#Inject
MyClass myClass;
//then in onCreate actually inject(this) from your graph of course.
#Singleton (and any other scope annotation) makes your class a single instance in your dependencies graph (it means that this instance will be "singleton" as long as Component object exists).
In short - everytime you're injecting #Singleton annotated class (with #Inject annotation) it will be the same instance as long as you inject it from the same Component.
For more I'm referring my blog post about how #Singleton and other scopes annotations works in Dagger 2: http://frogermcs.github.io/dependency-injection-with-dagger-2-custom-scopes/
#Singleton does not really create a Singleton, it is just a Scope, it is advised to not use #Singleton as it is misleading, it gives the impression that we are infact getting a Singleton, but we are not.
Let's say you annotate your database dependency with #Singleton and link with a Component, now let's say that you initialise this Component in Activities A and B, you will have different instances of your database in your two Activities which is something most people don't want.
How do you overcome this?
Initialise your Component once in your Application class and access it statically in other places like Activities or Fragments, now this could soon get out of hand if you have more than 20 Component's as you cannot initialise all of them in your Application class, doing so will also slow down your app launch time.
The best solution according to me is to create a real Singleton, either double checked or of other variants and use this statically as getInstance() and use this under #Provides in your Module.
I know it breaks my heart too, but please understand that #Singleton is not really a Singleton, it's a Scope.
What is singleton?
Singleton Pattern in android
A single instance of a class providing a global point of access to itself during the entire application's lifetime.
#Singleton annotation in Dagger
A single instance of a class which is unique to a specific component, its access is limited to the scope of the component.
Purpose of singleton
To provide a single instance of a class within the dependency graph(component). a component is usually initialized in the application level as it executes only ones for the entire applications lifetime and is accessible by all activities and fragment.
Let's take an example:
CoffeeComponent.kt
#Singleton
#Component
interface CoffeeComponent {
fun getCoffeeMaker():CoffeeMaker
fun inject(activityA: ActivityA)
fun inject(activityB: ActivityB)
}
CoffeeMaker.kt
#Singleton
class CoffeeMaker #Inject constructor()
CoffeeAplication.kt
class CoffeeApplication : Application() {
private val component by lazy {
DaggerCoffeeComponent.builder().build()
}
fun getAppComponent(): CoffeeComponent = component
}
Recommended Practise
Always go for lazy initialization of your component.Scenario: say, you are team decided to add an onboarding/tutorial screen or incorporate some other design which doesn't require the component during the initial screens, it will help minimize the startup delay. Always remember, component initialization is expensive.
ActivityA.kt
import dagger.Lazy
class ActivityA: AppCompatActivity() {
#Inject
lateinit var coffeeMaker1:Lazy<CoffeeMaker>
#Inject
lateinit var coffeeMaker2:Lazy<CoffeeMaker>
private val component by lazy {
(application as CoffeeApplication).getAppComponent()
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
btn_activityB.setOnClickListener { startActivity(Intent(this, NewActivity::class.java)) }
component.inject(this)
println("Activity A CoffeeMaker 1 - ${coffeeMaker1.get()}")
println("Activity A CoffeeMaker 2 - ${coffeeMaker2.get()}")
}
}
If your class is expensive to construct, use the dagger's Lazy initialization, please don't confuse it with kotlin's Lazy. You have to import
import dagger.Lazy
#Inject
lateinit var coffeeMaker1:Lazy<CoffeeMaker>
ActivityB.kt
class ActivityB: AppCompatActivity() {
#Inject
lateinit var coffeeMaker1:Lazy<CoffeeMaker>
#Inject
lateinit var coffeeMaker2:Lazy<CoffeeMaker>
private val component by lazy {
(application as CoffeeApplication).getAppComponent() }
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_new)
component.inject(this)
println("Activity B CoffeeMaker 1 - ${coffeeMaker1.get()}")
println("Activity B CoffeeMaker 2 - ${coffeeMaker2.get()}")
}
}
you will get the Log output as
Note:
If you want to share a singleton instance between activities, lazily initialize them in the application level, if you initialize them in an activity you will end up with different instance as the components are different
Well you can manually create an annotation,which will help to create a singleton object.
#Scope
#Retention(RetentionPolicy.CLASS)
public #interface MyApplicationScope {
}
When #MyApplicationScope annotation is added with #Provides annotation than it makes dagger to create an object only once and use same object in future. Do remember to add this annotation to the component interface also otherwise you will get the scope related error during compilation.
If you are using #Singleton annotation then you may end up creating the new objects every time when you will create your component with .build().
#Singleton is inherit #Scope, so in documentation says
Identifies scope annotations. A scope annotation applies to a class
* containing an injectable constructor and governs how the injector reuses
* instances of the type. By default, if no scope annotation is present, the
* injector creates an instance (by injecting the type's constructor), uses
* the instance for one injection, and then forgets it. If a scope annotation
* is present, the injector may retain the instance for possible reuse in a
* later injection. If multiple threads can access a scoped instance, its
* implementation should be thread safe. The implementation of the scope
* itself is left up to the injector.
<p>In the following example, the scope annotation {#code #Singleton} ensures
* that we only have one Log instance:
*
* <pre>
* #Singleton
* class Log {
* void log(String message) { ... }
* }</pre>
You get the point right? whatever annotation you use or you create a custom one, and they inherit from #Scope it will ensure as singleton.
I'm using dagger.
Having the following classes:
class A {
#Inject
MyClass myClass;
}
class B extends A {
myClass.do();
}
When trying to compile this I'm getting
No injectable members on B . Do you want to add an injectable
constructor?
When moving myClass to B everything compiles. Any idea what might be the problem ?
Dagger can't know all subtypes of A so it doesn't know that it needs to generate adapters for classes like B.
Adding a no-arg constructor with #Inject will force the generation of code that can thus be used to perform injection on instances of B. You can also list B.class in the injects= list of a module to force adapter generation.
I am trying to impelement an interface base domain class with grails and I am runing into error at run time. The error type is Could not determine type for: com.testApp.MyInterface , at table: C.
// in /src/groovy directory
interface MyInterface{
int returnSomething()
}
// in domain class directory
class A implements MyInterface{
int returnSomething(){
//a first implementation here
}
}
class B implements MyInterface{
int returnSomething(){
//a second implementation here
}
}
class C {
....
MyInterface type
..............
}
I think because I'm not specifying the real implementation of "MyInterface " in domain class C, grails is getting into trouble when injecting beans at startup. But I would like to keep MyInterface as abstract as possible because differents classes will implement it, various ways. Is there a way to overcome this error? Can I build my models without domain classes extension (I want to avoid that as far as it is possible)?
You need domain classes only if you want to persist data. Without knowing the specifics of your situation, you may want to make classes A, B, and C services rather than domain classes. It has been my experience that sub-classes and interfaces work better in the service realm than the domain class realm. I would imagine that each returnSomething() implementation would operate over distinct domain classes that do not require interfaces.
When a class implements an interface all we have to do is mock that interface.
However there are some cases when a class doesn't implement an interface, in that case binding the class to a mock leads guice to get the mocked object dependencies.
To clarify:
class A {
#Inject B;
}
class B{
#Inject C;
}
bind(a.class).toInstance(mock(B.class));
In this scenario, I don't care B's dependencies, but guice stills tries to inject C inside B.
Is there a way to avoid this without defining an interface?
First of all, I strongly recommend against using dependency injection in unit tests. When you're unit testing single class you should create it and pass its dependencies directly, through a constructor or methods. You won't have these problems then.
It's another story when you're writing integration tests though. There are several solutions to your problem.
Make sure all your classes receive dependencies only through injectable constructors. This way Guice won't inject anything because the object will be created by Mockito.
Use providers (and scoping, if needed). The following is equivalent to your attempt sans injection into B (I assume that you really meant bind(B.class).toInstance(mock(B.class)):
bind(B.class).toProvider(new Provider<B> {
#Override
public B get() {
return mock(B.class);
}
}).in(Singleton.class);
You should tweak the scope to satisfy your needs.
Using Mockito to partially solve this was quite easy.
You will need to use #Mock and #InjectMocks annotations like this
ATest{
#Mock B;
#InjectMocks A;
public void setUp(){
MockitoAnnotations.initMocks(this);
}
}
This way Mockito will do the inject instead of guice, there are a couple of restrictions to successfully inject the mock.
This works pretty well until your code have a strong dependency on a class.
Lets say inside A i have something like C obj = new C(); and C have injected fields.