prasanthMurugan
prasanthMurugan

Reputation: 617

Dagger-android build errror cannot be provided without an @Provides-annotated method

I am learning dagger dependency injection(I know i am late to the party, its better to start now).

But I am facing below error. Please any help or suggestion could be greatly appreciated.

ERROR Log

    error: [Dagger/MissingBinding] [dagger.android.AndroidInjector.inject(T)] @com.mypackage.di.key.ItemDetail java.lang.Integer cannot be provided without an @Provides-annotated method.
public interface ApplicationComponent extends AndroidInjector<RetailShopApplication> {
   ^
  @com.mypackage.di.key.ItemDetail java.lang.Integer is injected at
      com.mypackage.ui.detail.ItemDetailViewModel.<init>(…, itemId)
  com.mypackage.ui.detail.ItemDetailViewModel is injected at
      com.mypackage.di.module.ViewModelModule.provideItemDetailViewModel(itemDetailViewModel)
  java.util.Map<java.lang.Class<? extends androidx.lifecycle.ViewModel>,javax.inject.Provider<androidx.lifecycle.ViewModel>> is injected at
      com.mypackage.viewModel.ViewModelFactory.<init>(creators)
  com.mypackage.viewModel.ViewModelFactory is injected at
      com.mypackage.ui.home.ItemCategoryFragment.itemViewFactory
  com.mypackage.ui.home.ItemCategoryFragment is injected at
      dagger.android.AndroidInjector.inject(T)
component path: com.mypackage.di.component.ApplicationComponent ? com.mypackage.di.builder.ActivityBuilder_ContributeHomeActivity.HomeActivitySubcomponent ? com.mypackage.ui.home.HomeFragmentProvider_ContributeItemCategoryFragment.ItemCategoryFragmentSubcomponent

My Problem

I created a provideItemDetailViewModel() with contributeAndroidInjection annotation in ViewModelModule but the ItemDetailViewModel contructor has an integer value.

So i am providing an integer value at runtime from my activity intent through ItemDetailActivityModule class. Problem is dagger throwing me an error "integer already exist in the scope" but not able to inject in the ViewModel contructor.

ViewModelModule

@Module
public abstract class ViewModelModule {

@Binds
@IntoMap
@ViewModelKey(ItemCategoryViewModel.class)
abstract ViewModel provideItemCategoryViewModel(ItemCategoryViewModel 
itemCategoryViewModel);

@Binds
@IntoMap//<---- Here is the new ViewModel
@ViewModelKey(ItemDetailViewModel.class)
abstract ViewModel provideItemDetailViewModel(ItemDetailViewModel 
itemDetailViewModel);

@Binds
abstract ViewModelProvider.NewInstanceFactory 
getViewModelFactory(ViewModelFactory viewModelFactory);
}

ItemDetailViewModel

@Inject
  public ItemDetailViewModel(Application application, @ItemDetail int itemId) 
  {
        itemRepository = new ItemRepository(application);
        itemLiveData = itemRepository.getItem(itemId);
    }

ItemDetailActivityModule

@Module
public class ItemDetailActivityModule {

  @Provides
  @ItemDetail
  int provideItemId(ItemDetailActivity itemDetailActivity) {
    return itemDetailActivity.getIntent().getIntExtra(AppConstants.ITEM_ID, 
  0);
  }

}

ActivityBuilder

@Module
public abstract class ActivityBuilder {

@ActivityScope
@ContributesAndroidInjector(modules = {HomeFragmentProvider.class, 
HomeActivityModule.class})
abstract HomeActivity contributeHomeActivity();

@ActivityScope
@ContributesAndroidInjector(modules = ItemDetailActivityModule.class)
abstract ItemDetailActivity contributeItemDetailActivity();

}

ApplicationComponent

@Singleton
@Component(modules = {AndroidSupportInjectionModule.class, 
ApplicationModule.class, ActivityBuilder.class})
public interface ApplicationComponent extends 
AndroidInjector<RetailShopApplication> {

  @Component.Builder
  abstract class Builder extends 
  AndroidInjector.Builder<RetailShopApplication> {
  }

}

ApplicationModule

@Module(includes = ViewModelModule.class)
public class ApplicationModule {

   @Provides
   Application provideApplication(RetailShopApplication application) {
    return application;
   }
}

Upvotes: 1

Views: 1074

Answers (1)

prasanthMurugan
prasanthMurugan

Reputation: 617

Solution

Problem is i was creating provider for each ViewModel in ViewModelModule class which is included in AppModule.

Every Activity created using @ContributeAndroidInjection is a sub component.

So App component and Activity sub component are different. Activity component can access provider of App component but its not vice versa that the reason for injection already exist error

I solved the problem my including view model inside the activity module instead of appcomponent module.

ViewModelModule

Before

@Module
public abstract class ViewModelModule {

   @Binds
   @IntoMap//<---- Here is the new ViewModel
   @ViewModelKey(ItemDetailViewModel.class)
   abstract ViewModel provideItemDetailViewModel(ItemDetailViewModel 
   itemDetailViewModel);

   @Binds
   abstract ViewModelProvider.NewInstanceFactory 
   getViewModelFactory(ViewModelFactory viewModelFactory);
}

After - Only ViewModelFactory is necessary in ViewModelModule move all your ViewModel provider to activity module.

@Module
public abstract class ViewModelModule {

   @Binds
   abstract ViewModelProvider.NewInstanceFactory 
   getViewModelFactory(ViewModelFactory viewModelFactory);
}

ItemDetailProvider

Create Abstract class in order to hold the ViewModel providers

@Module
public abstract class ItemDetailProvider {

  @Binds
  @IntoMap
  @ViewModelKey(ItemDetailViewModel.class)
  abstract ViewModel provideItemDetailViewModel(ItemDetailViewModel 
  itemDetailViewModel);

}

ActivityModule

Now add the created provider in the Activity module as shown below

Before

@Module
public abstract class ActivityBuilder {

   @ContributesAndroidInjector(modules = 
   {ItemDetailActivityModule.class})
   abstract ItemDetailActivity contributeItemDetailActivity();

}

After - Now add the ViewModel that corespond to the above activity in my case it is ItemDetailViewModel class

@Module
public abstract class ActivityBuilder {

   @ContributesAndroidInjector(modules = 
   {ItemDetailActivityModule.class,ItemDetailProvider.class})//<--- provider added in this line
   abstract ItemDetailActivity contributeItemDetailActivity();

}

Upvotes: 1

Related Questions