So i want to build multi modular android project and have a problem with injecting the interface and the implementation.
I have two interface for navigation between fragment
first interface
interface Fragment1Navigation {
fun navigateToFragment1()
fun navigateToFragment2()
}
second interface
interface Fragment2Navigation {
fun navigateToFragment3()
fun navigateToFragment4()
}
then i have the class that implement those two interfaces
class Navigator: BaseNavigator(), Fragment1Navigation, Fragment2Navigation {
override fun navigateToFragment1() {
// some implementation
}
override fun navigateToFragment2() {
// some implementation
}
override fun navigateToFragment3() {
// some implementation
}
override fun navigateToFragment4() {
// some implementation
}
}
i want to inject this Navigator class in my mainActivity to bind my navcontroller first and also i want to inject the interface in other fragment and i made the module class like this
#Module()
class NavigationModule {
#Provides
#Singleton
fun provideNavigator(): Navigator = Navigator()
#Provides
fun provideFragment1Navigation(): Fragment1Navigation = Navigator()
#Provides
fun provideFragment2Navigation(): Fragment2Navigation = Navigator()
}
I want those provideFragmentNavigation and provideNavigatioin to provide the same Navigator() so interface in fragment lead to same navigator like in main activity, but turns out it provide different instance so the interface in fragment lead to different navigator from mainactivity
Two ways u can achieve this:
using #Binds instead of #Provides
passing the Navigation() object directly to the constructor of NavigationModule
using binds
class Navigator #Inject constructor(): BaseNavigator(), Fragment1Navigation, Fragment2Navigation {
// body
}
Module
#Module()
abstract class NavigationModule {
#Binds
abstract fun provideNavigator(navigator: Navigator): BaseNavigator
#Binds
abstract fun provideFragment1Navigation(navigator: Navigator): Fragment1Navigation
#Binds
abstract fun provideFragment2Navigation(navigator: Navigator): Fragment2Navigation
}
Related
#Module
abstract class PersonUsecaseModule{
#Provides
internal fun provideUseCase(useCase: GetPersonUseCaseImpl): PersonUseCase = useCase
#Provides
internal fun provideMutableLiveData() = MutableLiveData<PersonUseCase.Result>()
#Provides
internal fun providePersonWidgetImplScreen(widget: PersonWidgetImpl): PersonWidget = widget
}
this is my module class and i am injecting it in MainActivity i am getting error
error: com.anil.gorestapp.person.injection.PersonUsecaseModule is abstract and has instance #Provides methods. Consider making the methods static or including a non-abstract subclass of the module instead.
public abstract interface ApplicationComponent {
I don't know why i am getting this Error Please help me what i am doing mistake
lateinit var personWidget: PersonWidget
AppLication component :
#Singleton
#Component(
modules = [
ApplicationModule::class,
ActivityModule::class,
NetworkModule::class
]
)
interface ApplicationComponent {
#Component.Builder
interface Builder {
#BindsInstance
fun application(application: Application): Builder
fun build(): ApplicationComponent
}
fun inject(application: MainApplication)
}
ActivityModule
abstract class ActivityModule {
#ContributesAndroidInjector
abstract fun contributeMainActivity(): MainActivity
#Binds
abstract fun bindSharedPreferences(appPreferenceImpl: AppPreferenceImpl): AppPreference
}
person module
abstract class ActivityModule {
#ContributesAndroidInjector
abstract fun contributeMainActivity(): MainActivity
#Binds
abstract fun bindSharedPreferences(appPreferenceImpl: AppPreferenceImpl): AppPreference
}
So the issue is, You have declared your module as "abstract" and along with this, you are also using #Provides annotation on methods which are returning implementation of interface.
Dagger doesn't allow that. You can fix this issue in two way:
First Way: Remove abstract from your module like this:
#Module
class ActivityModule {
#Provides
fun providePersonWidget(personWidget: PersonWidgetImpl) : PersonWidget = personWidget
}
Second way: Use #Bind annotation on method instead of provide like this
#Module
abstract class ActivityModule {
#Binds
abstract fun providePersonWidget(personWidget: PersonWidgetImpl) : PersonWidget
}
Note: In second way you can declare your method and class as abstract but cannot return anything.
If you are still not very clear with my answer, you can refer this branch which I have created for you.
https://github.com/parmeshtoyou/StackOverflow/compare/sof_23_oct_21_dagger_issue?expand=1
I have two classes that I'm able to have Dagger find and inject for me to use successfully:
TrackEvent
class TrackEvent #Inject constructor(
private val getTrackingProperties: SomeClass
) : UseCase<Boolean, TrackingEvent> {
override suspend operator fun invoke(params: TrackingEvent): Boolean {
return true
}
SomeClass (note: used as a dependency in TrackEvent)
class SomeClass #Inject constructor() {
override suspend operator fun invoke(): UserTrackingPropertiesResult {
return UserTrackingPropertiesResult()
}
}
TrackEvent has an entry in an #Module annotated interface because it's an implementation of the UseCase interface:
#Component(modules = [MyModule::class])
interface ShiftsComponent {
fun inject(homeFragment: HomeFragment)
}
#Module
interface MyModule {
#Binds
fun bindsTrackEventUseCase(useCase: TrackEvent): UseCase<Boolean, TrackingEvent>
}
Use Case interfaces
interface UseCase<out T, in P> {
suspend operator fun invoke(params: P): T
}
interface NoParamUseCase<out T> {
suspend operator fun invoke(): T
}
What I'd like to do is to inject an interface into TrackEvent instead of the concrete SomeClass. So I make SomeClass implement a NoParamUseCase
class SomeClass #Inject constructor(): NoParamUseCase<UserTrackingPropertiesResult> {
override suspend operator fun invoke(): UserTrackingPropertiesResult {
return UserTrackingPropertiesResult()
}
}
update TrackEvent to inject the interface:
class TrackEvent #Inject constructor(
private val getTrackingProperties: NoParamUseCase<UserTrackingPropertiesResult>) : UseCase<Boolean, TrackingEvent> {
override suspend operator fun invoke(params: TrackingEvent): Boolean {
return true
}
}
…and update MyModule to inform Dagger of which implementation I'd like to use:
#Module
interface MyModule {
#Binds
fun bindsTrackEventUseCase(useCase: TrackEvent): UseCase<Boolean, TrackingEvent>
// New
#Binds
fun bindsSomeClass(useCase: SomeClass): NoParamUseCase<UserTrackingPropertiesResult>
}
Dagger now claims that there is a missing binding and that I need to declare an #Provides annotated method:
error: [Dagger/MissingBinding] com.myapp.core.domain.usecase.NoParamUseCase<? extends com.myapp.core.tracking.UserTrackingPropertiesResult> cannot be provided without an #Provides-annotated method.
public abstract interface MyComponent {
^
com.myapp.core.domain.usecase.NoParamUseCase<? extends com.myapp.core.tracking.UserTrackingPropertiesResult> is injected at
com.myapp.tasks.tracking.domain.usecase.TrackEvent(getTrackingProperties, …)
…
As far as I can tell, this isn't true:
While, I've opted for #Binds in this instance, replacing this with #Provides and manually providing dependencies here yields the same error.
I'm using the exact same approach for the TrackEvent class and this works.
The only thing I've changed is that I'd like to provide an interface instead. I'd fully understand this error had I not provided the #Binds declaration.
This is different to this question as there's no ambiguity as to which implementation I'm asking Dagger to use in the way that there would be if I had two or more implementations of the same interface.
Why would I get this error now?
According to dagger error message, it expects covariant type NoParamUseCase<? extends UserTrackingPropertiesResult>, but DI module provides invariant NoParamUseCase<UserTrackingPropertiesResult>. To generate appropriate signature for provide method you can change it like this:
#Binds fun bindsSomeClass(useCase: SomeClass): NoParamUseCase<#JvmWildcard UserTrackingPropertiesResult>
After that your code should be compiled successfully.
I'm trying to add Dagger 2 in one project. I have created 3 components
AppComponent (the primary one)
RetrofitComponent (Dependent component works fine)
ControllerComponent (Subcomponent, not injecting properly)
this is my AppComponent
#ApplicationScope
#Component(modules = [ApplicationModule::class, PrefModule::class])
interface AppComponent {
fun inject(instance: AppInstance)
fun getAppPref(): AppPreference
fun newControllerComponent(controllerModule: ControllerModule): ControllerComponent
}
this is my ControllerComponent
#UIScope
#Subcomponent(modules = [ControllerModule::class])
interface ControllerComponent {
fun injectActivity(activity: BaseActivity)
fun injectDialog(dialog: BaseDialog)
}
ControllerModule
#Module
class ControllerModule(activity: FragmentActivity) {
var mActivity: FragmentActivity
init {
mActivity = activity
}
#Provides
fun getContext(): Context {
return mActivity
}
#Provides
fun getActivity(): Activity {
return mActivity
}
#Provides
fun getFragmentManager(): FragmentManager {
return mActivity.supportFragmentManager
}
#Provides
fun getDialogManager(fragmentManager: FragmentManager): DialogsManager
{
return DialogsManager(fragmentManager)
}
#Provides
fun getDialogsFactory(): DialogsFactory {
return DialogsFactory()
}
}
Injecting the activity is working fine, but when I try to inject the Dialog its never injecting
controller.injectDialog(singleDialog)
My entire code is here Github link. Need some help. What am I not doing or doing wrong that I can't inject from subcomponent.
To use field injection on a given class, you need to specify the concrete type rather than its superclass.
#UIScope
#Subcomponent(modules = [ControllerModule::class])
interface ControllerComponent {
//fun injectActivity(activity: BaseActivity)
//fun injectDialog(dialog: BaseDialog)
fun inject(activity: SomeSpecificActivity)
fun inject(activity: SomeOtherActivity)
fun inject(dialog: SomeSpecificDialog)
fun inject(dialog: SomeOtherDialog)
}
The Injection is working, but you are injecting nothing inside SingleClickDialog class, remember than the method inject(class : Class) is for let it to know dagger that class will be injected, but you need to put the elements you need inject inside that class with the annotation #Inject, and those elements you need to add its provider in ControllerModule as well
I'm creating an application in Kotlin using the MVP pattern.
I would need to inject a Repository into my Presenter for this purpose. Except that for this, my Repository requires a Retrofit interface as a parameter of its constructuor.
I'm a beginner in the use of Dagger2, and the answers found on the internet are far too complicated for such a basic case like mine.
Here's the repository i want to be injected :
class RepositoryInventory(private val api: Service): IRepositoryInventory {
override fun getInventoryItemByNum(itemnum: String): Observable<Response<Item>> {
return api.getInventoryItemByNum(itemnum)
.toObservable()
}
override fun getAllInventoryItems(): Single<Response<Item>> {
return api.getAllInventoryItems()
}
}
My Component
#Singleton
#Component(modules = arrayOf(ActivityModule::class))
interface ActivityComponent {
fun inject(loginActivity: LoginActivity)
fun inject(itemDetailActivity: ItemDetailActivity)
}
My module :
#Module
class ActivityModule(private var activity: Activity) {
#Provides
fun provideActivity(): Activity {
return activity
}
#Provides
fun provideLoginPresenter(): LoginPresenter {
return LoginPresenter()
}
#Provides
fun provideItemDetailPresenter(): ItemDetailPresenter {
return ItemDetailPresenter()
}
}
In my activity, my module is injected with this method :
private fun injectDependency() {
val activityComponent = DaggerActivityComponent.builder()
.activityModule(ActivityModule(this))
.build()
activityComponent.inject(this)
}
I have 2 components and 2 modules: one designed to inject into a fragment and the other into an activity.
Except in my case, I want to inject into a Presenter that is not a Fragment or an Activity but a class
Ok, my guess is you want to inject RepositoryInventory into LoginPresenter. If so, you can make use of #ContributesAndroidInjector and Binds
First, create a LoginActivityModule
#Module
abstract class LoginActivityModule {
#Binds
abstract fun loginPresenter(loginPresenter: LoginPresenter): LoginPresenter
}
Then, create a module called ActivityBindingModule
#Module
abstract class ActivityBindingModule {
#ContributesAndroidInjector(modules = [LoginActivityModule::class])
abstract fun loginActivity(): LoginActivity
#ContributesAndroidInjector()
abstract fun itemDetailActivity(): ItemDetailActivity
}
And change your ActivityComponent like this
#Singleton
#Component(modules = arrayOf(ActivityModule::class, ActivityBindingModule::class))
interface ActivityComponent {
}
And in your LoginPresenter:
class LoginPresenter #Inject constructor(private val repositoryInventory: IRepositoryInventory) {
...
}
Remember to remove this in ActivityModule:
#Provides
fun provideLoginPresenter(): LoginPresenter {
return LoginPresenter()
}
I would like to implement a part of an application that takes some steps that would be handled by one presenter. I have declared one scope:
#Scope
annotation class FormScope
next, I wanted to declare a module that would provide necessary dependencies:
#Module
object FormModule {
#JvmStatic
#Provides
fun providesFragmentManager(activity: FragmentActivity): FragmentManager = activity.supportFragmentManager
#JvmStatic
#Provides
fun providesNavigation(fragmentManager: FragmentManager): SobergridCoachingNavigationUnauthorizedFirstStep = SobergridCoachingNavigationUnauthorizedFirstStep(fragmentManager)
#JvmStatic
#Provides
fun providesCoordinator(navigation: NavigationUnauthorized): CoordinatoUnauthorized = CoordinatoUnauthorized(navigation)
#JvmStatic
#Provides
#Reusable
fun providesPresenter(coordinator: CoordinatoUnauthorized): OnboardingFragmentContract.Presenter = FormPresenter(coordinator)
}
and finally, I bind the modules into fragments that I want inject dependencies into:
#Module(includes = [AndroidSupportInjectionModule::class])
abstract class FragmentBindingModule {
#FormScope
#ContributesAndroidInjector(modules = [FormFirstModule::class, FormModule::class])
abstract fun contributesFormFirst(): FormFirstFragment
#ContributesAndroidInjector(modules = [FormSecondModule::class, FormModule::class])
abstract fun contributesFormSecond(): FormSecondFragment
#ContributesAndroidInjector(modules = [FormThirdModule::class, FormModule::class])
abstract fun contributesFormThird(): FormThirdFragment
}
The problem that I encounter is that every single time a new fragment is showed the Dagger creates a new instance of the Presenter. I want to use a single presenter for all of those Fragments. What I do wrong? What should I improve to be able to achieve my goal?
UPDATE
I have also tried annotating my provide method with #Singleton
#JvmStatic
#Provides
#Signleton
fun providesPresenter(coordinator: CoordinatoUnauthorized): OnboardingFragmentContract.Presenter = FormPresenter(coordinator)
but this leads to the compilation error. The last thing that I tried was to put annotations (both #Reusable and #Singleton) before the declaration of the Presenter class. This approach gives me no compilation errors but still, there is more than one instance of the Presenter class.
Move your Presenter provides to FragmentActivity Module with Scope to get the same Presenter for all fragments in Activity
#Module
public class FragmentActivityModule {
//common provides for all fragments
#Provides
#FormScope
public YourPresenter providesYourPresenter() {
return new YourPresenter();
}
....
And your builder have to look like this
#Module
public abstract class ActivityBuilder {
#FormScope
#ContributesAndroidInjector(modules = {FragmentActivityModule.class, Form1FragmentProvider.class
, Form2FragmentProvider.class})
abstract FragmentActivity bindFragmentActivity();
In Form1FragmentModule are provides only for Form1Fragment.
Create FragmentProviders for all fragments Form1FragmentProvider, Form2FragmentProvider...
#Module
public abstract class Form1FragmentProvider {
#ContributesAndroidInjector(modules = Form1FragmentModule.class)
abstract Form1Fragment provideForm1FragmentFactory();
}
Do not forget implement HasSupportFragmentInjector in your FragmentActivity
public class FragmentActivity extends AppCompatActivity implements HasSupportFragmentInjector {
#Inject
DispatchingAndroidInjector<Fragment> fragmentDispatchingAndroidInjector;
....
#Override
public AndroidInjector<Fragment> supportFragmentInjector() {
return fragmentDispatchingAndroidInjector;
}
Do not forget attach AndroidSupportInjection in your Fragments
#Override
public void onAttach(Context context) {
AndroidSupportInjection.inject(this);
super.onAttach(context);
}