Trying to solve a dependency cycle using dagger - android

dagger-android 2.16
I have a dependency cycle error in my Dagger module. I think I know what the problem is but not sure how to solve it.
This is the error message:
Found a dependency cycle:
public interface LoginFragmentSubcomponent extends AndroidInjector<LoginFragment> {
presentation.login.request.LoginRequest is injected at
mobileui.login.di.LoginActivityModule.provideLoginResponseListener(…, loginRequest)
presentation.login.response.LoginResponseListener is injected at
mobileui.login.di.LoginActivityModule.provideLoginRequest(…, loginPresenter)
presentation.login.request.LoginRequest is injected at
mobileui.login.di.LoginActivityModule.provideLoginPresenter(…, loginRequest)
mobileui.login.LoginPresenter is injected at
mobileui.login.LoginFragment.loginPresenter
This is the module below where I am getting the error
#Module
class LoginActivityModule {
#Reusable
#Provides
fun provideLoginPresenter(loginRequest: LoginRequest): LoginPresenter {
return LoginPresenterImp(loginRequest)
}
#Reusable
#Provides
fun provideLoginResponseListener(loginRequest: LoginRequest): LoginResponseListener {
LoginPresenterImp(loginRequest)
}
#Reusable
#Provides
fun provideLoginRequest(loginUser: LoginUser,
loginPresenter: LoginResponseListener): LoginRequest {
return LoginRequestImp(loginUser, loginPresenter)
}
}
My LoginPresenterImp implements the LoginResponseListener and I want to pass that to the LoginRequestImp class so I can use it as a callback.
class LoginPresenterImp(private val loginRequest: LoginRequest) :
BasePresenterImp<LoginView>(),
LoginPresenter,
LoginResponseListener {
}
And the loginResponseListener gets passed here:
class LoginRequestImp(
private val loginUser: LoginUser,
private val loginResponseListener: LoginResponseListener)
: LoginRequest {
}
Many thanks in advance,

As Ayush described in the comments:
You need LoginResponseListener to create LoginRequest and you need LoginRequest to create LoginResponseListener. So, you are getting the error.
When you are creating LoginRequest in LoginRequestImp(loginUser, loginPresenter), loginPresenter is a parameter to the constructor of type LoginResponseListener. You should try to eliminate this dependency. May be you can set the listener later from the presenter
In your reply between those comments:
LoginRequest has been created in provideLoginRequest
But this is what's happening:
Your LoginFragment tries to inject LoginPresenter.
Before you inject LoginPresenter you need to create a LoginRequest.
Before you create a LoginRequest you need a LoginUser and a LoginRequestListener.
Before you create a LoginRequestListener (which you've implemented as a LoginPresenterImpl), you need a LoginRequest.
You're in the middle of creating a LoginRequest, so Dagger gives up and correctly reports a circular reference.
To reiterate: Even though you've set up the bindings using interfaces correctly, Dagger can't create any of them because to call either constructor it has to create the other. This isn't a problem with Dagger: If class A's constructor takes an instance of B, and class B's constructor takes an instance of A, you couldn't construct either of them manually either while respecting their constructor parameters.
As Ayush suggested, don't have LoginRequest inject a LoginResponseListener. Instead, create a method like setLoginResponseListener, which LoginPresenterImp can call. I recommend this approach as well, in part because #Reusable has weaker semantics than you want: You want to be absolutely sure that the LoginPresenterImp instance that acts as your LoginPresenter is the same instance that acts as LoginResponseListener.
As an alternative, you can inject Provider<LoginPresenter> instead of LoginResponseListener, and change LoginRequestImp to accept a Provider as well. (You could also inject a Provider<LoginResponseListener>, but if you want that LoginResponseListener to be the same one as your LoginPresenter instance, you shouldn't call the LoginPresenterImp constructor explicitly. You'd want to switch to #Binds ideally, or at least have your #Provides method inject LoginPresenter instead.) You're allowed to inject a Provider, because a Provider<T> is automatically bound for every class <T> that Dagger knows how to provide, and it solves your problem because Dagger can pass a Provider<T> without trying to create the T. This will technically appear to work even if you leave your bindings as #Reusable, but in a multithreaded environment #Reusable isn't going to guarantee that you always receive the same instance for LoginRequestListener as your LoginPresenter, or that you'll receive a new LoginPresenter for every LoginFragment. If you want to guarantee that, you can look into custom scopes.

Related

whats the difference between #Provide and #Inject in dagger2?

Whats the difference between #Inject and #Provide ?
although both are used for providing dependencies then whats the difference ?
This is covered very well in documentation, #Inject and #Provides are two different ways of introducing dependencies in the dependency graph. they are suited for different use cases
#Inject
Easy to use, simply add #Inject on constructor or property and you are done
It can be used to inject types as well as type properties
In a subjective way it may seem clearer than #Provides to some people
#Provides
If you don't have access to source code of the type that you want to inject then you can't mark its constructor with #Inject
In some situations you may want to configure an object before you introduce it in dependency graph, this is not an option with #Inject
Sometimes you want to introduce an Interface as a dependency, for this you can create a method annotated with #Provides which returns Inteface type
Following are the examples of above three points for #Provides
If you can't access source code of a type
// You can't mark constructor of String with #Inject but you can use #Provides
#Provides
fun provideString(): String {
return "Hello World"
}
Configure an object before introducing in the dependency graph
#Provides
fun provideCar(): Car {
val car = Car()
// Do some configuration before introducing it in graph, you can't do this with #Inject
car.setMaxSpeed(100)
car.fillFuel()
return car
}
Inject interface types in dependency graph
interface Logger { fun log() }
class DiscLogger : Logger{ override fun log() { } }
class MemoryLogger : Logger { override fun log() { } }
#Provides
fun provideLogger(): Logger {
val logger = DiscLogger() \\ or MemoryLogger()
return logger
}
#Inject:- It is used to inject dependencies in class.
#provides:- Required to annotated the method with #provide where actual instance is created.
This has covered in Dagger-2 documentation clearly.
What #Inject can Do:
Use #Inject to annotate the constructor that Dagger should use to
create instances of a class. When a new instance is requested, Dagger
will obtain the required parameters values and invoke this
constructor.
If your class has #Inject-annotated fields but no #Inject-annotated
constructor, Dagger will inject those fields if requested, but will
not create new instances. Add a no-argument constructor with the
#Inject annotation to indicate that Dagger may create instances as
well.
Similarly dagger can create for methods also.
Classes that lack #Inject annotations cannot be constructed by
Dagger.
What #Inject can't Do: and can be used #Provides annotation
Interfaces can’t be constructed.
Third-party classes can’t be annotated.
Configurable objects must be configured!

Should I use #Singleton, object or both with Dagger2 and Kotlin?

I'm doing an app using Kotlin and Dagger2, trying to follow the MVVM pattern, but I'm in a dilemma, should I use #Singleton, object or both? And why? Let's say I have a RepositoryMovies class and I want to get the same instance every time, according to my research you can do this as follows:
#Singleton (Dagger2 way)
#Singleton
class RepositoryMovies {
TODO()
}
Object (Kotlin way)
object RepositoryMovies {
TODO()
}
Both
#Singleton
object RepositoryMovies {
TODO()
}
And don't get me started with singletons in Kotlin following the "Java-Way". I'd appreciate your help. Thanks so much.
Injecting an object doesn't make much sense, since in kotlin object is used to simulate java's utility classes, such as java's Arrays or Collections classes. One defining characteristic of such classes is that, they are not associated with any specific class in your project, they can be required and used anywhere.
On the other hand in most practical situations a repository will be associated with a specific class. for example you may only want to inject a UserRepository in a UserViewModel, because that is the only place where you need to access user's information.
As for the object and #Singleton, object is by definition a singleton, so marking it with #Singleton is redundant and doesn't accomplish anything until you make it injectable by means of a #Provides function. where you have to specify, how dagger can create instances of this class?
In your first example marking a class #Singleton doesn't do anything, unless it is injectable. as the docs state.
Singletons and Scoped Bindings
Annotate an #Provides method or injectable class with #Singleton. The graph will use a single instance of the value for
all of its clients.

Dagger 2 Inject Fields

I have a class that is dagger injected via the constructor. However I now need to add an argument to this constructor that is not provided via injection and is instead a run time argument.
In my previous job, we rolled our own DI so I am not up to speed with all the "magic" annotations that dagger offers yet. I figured it should be simple to add an argument to a constructor and still have dagger inject the remaining values (as it was very simple to do with the aforementioned "roll your own DI" solution I have implemented before).
However, it looks like this is not possible with dagger (i.e. assisted injection). So I have been reading on how to solve this issue but have become completely stumped.
Here is the class that I am currently (successfully) injecting ServiceA into:
class Foo #Inject constructor(private val serviceA: ServiceA) {
...
}
What I would like to do is add another argument to this constructor that will be provided at run time. In this case a simple flag to determine some behaviour in the class. And, given that Dagger doesn't support assisted injection, I need to remove injection from Foo and instead create a FooFactory to handle creation of Foo objects. This factory will have ServiceA injected into its constructor and will provide a method to construct an instance of Foo taking the boolean. I will then end up with a Foo class that looks like:
class Foo(private val serviceA: ServiceA, myNewArgument: Boolean) {
...
}
And a FooFactory that looks like:
#Singleton
class FooFactory #Inject constructor(private val serviceA: ServiceA) {
fun createFoo(myNewArgument: Boolean) {
return Foo(serviceA, myNewArgument)
}
}
And, although this is a complete mess to just get an extra constructor arg, it does the job.
The problem I am facing, is that my Foo class is actually an AndroidViewModel, and will need to be constructed through the ViewModelProvider.Factory contract, which has a create method which is invoked by the SDK to create the view model. You override this method to create an instance of the view model but the method has no parameters, so there is no way to propagate the flag into the view model through this method.
So the only way for me to get the flag propagated to the view models constructor is by having the factory itself take the flag as an argument to it's constructor, which, because dagger does not support assisted injection, is not possible.
So, instead I am planning to make dagger manually inject my dependencies into the FooFactory at initialization time. This is where I am stuck, I cannot figure out how on earth to get dagger to manually inject dependencies into my class.
My FooFactory now looks like:
class FooFactory(private val myNewArgument: Boolean) : ViewModelProvider.Factory {
init {
// I need to trigger injection here... how though???
}
#Inject
lateinit var serviceA: ServiceA
override fun <T : ViewModel?> create(modelClass: Class<T>): T {
return Foo(
serviceA,
myNewArgument
) as T
}
}
So, somehow in the init block of the factory, I need to ask dagger to inject the fields annotated with #Inject. But I have no idea how to do this, I have tried following the answers at the following questions and tutorials:
https://proandroiddev.com/from-dagger-components-to-manual-dependency-injection-110015abe6e0
Dagger 2 - injecting non Android classes
Dagger 2 injection in non Activity Java class
None of these seem to work for my use case and I'm starting to lose it. Could anyone point me in the right direction? Is this dagger framework massively over engineered/complicated for not much benefit (this is the conclusion I am coming to at this point, all I want to do is achieve DI for testing purposes, I don't want to have to write factories so I can add an extra argument to a constructor)...

Provide all childs of a class with dagger

im creating a highly modular application, i have a lot of clases that need to be injected, all of them are childs (not direct childs) of the same class, none of them have constructor parameters.
I want to avoid having to create a "#Provides" method for each one of them in my module.
Is there a way to tell dagger to automatically provide all the classes that implement a base interface? Or is it possible to do it myself using reflection?
Im using dagger-android with kotlin
Update: Ill post some code to illustrate
In one of the modules i have this interface
interface ExampleClass: BaseExample {
fun doSomething()
}
}
Then in the main app i implement it
class ExampleClassImpl #Inject constructor() : ExampleClass {
override fun doSomething(){
}
}
The class where i need it is a Viewmodel created with dagger so inject works on the constructor.
class ExampleViewModel #Inject constructor(val exmpl :ExampleClass) : BaseViewModel {
}
I want to inject that ExampleClassImpl, to do that i need to create a #module with a method annotated with #Provides or #Bind and return that class.
Without the provider i get an error at compile time:
error: [Dagger/MissingBinding] com.myapp.ExampleClassImpl cannot be provided without an #Provides-annotated method.
You want to inject ExampleClass, but Dagger only knows about ExampleClassImpl. How would Dagger know that you want that specific subclass?
Moreover, you say you have many subclasses that you want to inject. How would Dagger know which one to provide to a constructor expecting the base class?
If you want ExampleViewModel to get an instance of ExampleClassImpl then you can simply change the declaration to:
class ExampleViewModel #Inject constructor(val exmpl :ExampleClassImpl)
Doing so you lose the ability to swap the constructor argument with a different implementation of ExampleClass.
The alternative is to have one #Named #Provides method per subclass. So something like:
// In your module
#Provides
#Named("impl1")
fun provideExampleClassImpl1(impl: ExampleClassImpl): ExampleClass = impl
// When using the named dependency
class ExampleViewModel #Inject constructor(#Named("impl1") val exmpl :ExampleClass)

Dagger why to create custom scope

I'm learning dagger and I fount this article that explains how to use android.dagger everything is clear for me except with custom created scopes. Previously I saw a lot of tutorials where custom scope is created to create dependencies to specific situations (e.g Logged In scope). But that tutorial showed my other approach. Here's my example:
I have a class that should be generated only for MainActivity (and MasterActivity) but not for LoginActivity
class SecretKey(
val token: String,
val userId: Long
)
So here's the module
#Module
class MainActivityModule {
#Provides
fun provideSecretKey(preference: SharedPreferences): SecretKey {
return SecretKey(
"jwtToken",
465465
)
}
}
and ActivitiesBindingModule
#Module
abstract class ActivitiesBindingModule {
#ContributesAndroidInjector(modules = [MainActivityModule::class])
abstract fun mainActivity(): MainActivity
#ContributesAndroidInjector(modules = [LoginActivityModule::class])
abstract fun loginactivity(): LoginActivity
// MasterActivity will see everything from MainActivityModule and LoginActivityModule
#ContributesAndroidInjector(modules = [MainActivityModule::class, LoginActivityModule::class])
abstract fun masterActivity(): MasterActivity
}
So as I understood only in MainActivity and MasterActivity I will be able to inject SecretKey class because of the ContributesAndroidInjector module. So SecretKey scope is within MainActivity and MasterActivity. So why we still are able to create custom scopes with Scope annotation? Is this alternative?
Scope simply tells Dagger to save the instance of the scope-annotated object rather than creating a new one. Dagger will save the instance in the component of the corresponding scope. (You should also know that #ContributesAndroidInjector code-generates a Subcomponent instance for you, so if you annotate the #ContributesAndroidInjector method with a scope annotation, the generated subcomponent will take that scope.)
In your example, SecretKey is not scoped; every time you ask Dagger to inject a SecretKey it will call your constructor and create a brand new instance. This is probably fine, as SecretKey seems not to keep state, and keeping the instance scopeless allows the garbage collector to collect SecretKey when you no longer need it.
However, imagine that you create your own Cache object, and that you want that Cache to live as long as the activity but not longer: Each new Activity instance should get its own Cache. Unlike SecretKey, you could not create your own Cache every time one is requested; you'd need to save your cache instance somewhere. You could choose to do this as a field on the Activity itself, or you could make a Module instance that saves an instance value the first time you call a #Provides method, but Dagger prefers that you mark the binding with a scope annotation that matches the component. This allows you to declare and document that the binding will have the same lifetime as the component, and makes it easy to categorize bindings as "Application-scoped", "Activity-scoped", "Fragment-scoped", "Service-scoped", and so forth.

Categories

Resources