1

I made a simple project for learning Dagger. The app is fetching a list of properties (vacation rentals) from Internet and displays them on a RecyclerView list. I injected all dependencies with Dagger 2 except the adapter for the list. The adapter is pretty standard, it takes a list of properties and populates the views:

public class PropertyListAdapter extends RecyclerView.Adapter<PropertyListAdapter.ViewHolder> {

    private List<Property> mPropertyList;

    @Inject
    public PropertyListAdapter(List<Property> propertyList) {
        mPropertyList = propertyList;
    }

    // onCreateViewHolder()
    // onBindViewHolder()
    // getItemCount()
    // class ViewHolder{}

}

The app consists of one activity that holds a fragment which contains the recyclerview list. In the fragment's onActivityCreated() I have:

mPropertyViewModel.getPropertyList().observe(this, properties -> setPropertyAdapter(properties));

and the setPropertyAdapter() is:

private void setPropertyAdapter(List<Property> properties) {
    rvPropertyList.setAdapter(new PropertyListAdapter(properties));
}

So, to inject PropertyListAdapter I have created a module:

@Module
    public class AdapterModule {
    @Provides
    @Singleton
    PropertyListAdapter providePropertyListAdapter(List<Property> propertyList) {
        return new PropertyListAdapter(propertyList);
}

}

but then I realized that I have to inject the List<Property> and I don't know how to achieve this and I'm stuck. How to inject the adapter in this example?

P.S. I'm using MVVM with architecture components.

Logcat: error: [dagger.android.AndroidInjector.inject(T)] java.util.List<com.aandritchi.android.propertyrentals.data.domain.Property> cannot be provided without an @Provides-annotated method. java.util.List<com.aandritchi.android.propertyrentals.data.domain.Property> is injected at com.aandritchi.android.propertyrentals.di.module.data.AdapterModule.providePropertyListAdapter(propertyList) com.aandritchi.android.propertyrentals.ui.search_result.PropertyListAdapter is injected at com.aandritchi.android.propertyrentals.ui.search_result.PropertySearchResultFragment.mPropertyListAdapter com.aandritchi.android.propertyrentals.ui.search_result.PropertySearchResultFragment is injected at com.aandritchi.android.propertyrentals.ui.home.HomeActivity.mPropertySearchResultFragment com.aandritchi.android.propertyrentals.ui.home.HomeActivity is injected at dagger.android.AndroidInjector.inject(arg0)

Andritchi Alexei
  • 1,380
  • 1
  • 16
  • 23
  • please post your logcat here and are you able to inject other classes ? – Rujul Gandhi May 08 '18 at 07:53
  • Yes, I injected all classes except the adapter. – Andritchi Alexei May 08 '18 at 08:03
  • 2
    It might be better to _not_ inject the list, but only modify it through a setter. Otherwise you need to add a `@Provides` method for it to your module, see also [here](https://stackoverflow.com/q/44912080/1837367). And please have a look at **Constructor Injection**, e.g. here https://stackoverflow.com/a/50209578/1837367 – David Medenjak May 08 '18 at 08:10

2 Answers2

2

You should ask yourself: do I really need to inject the Adapter?

Two reasons why you would want to do it:

1) Adapter has dependencies you want to inject. That's not your case, because there aren't any other dependencies than list of Properties.

2) You want to remove responsibility for creation of the Adapter's instance from your Activity/Fragment.

The correct solution for both problems is to inject a factory, that has all injectable dependencies already injected and provides a method for instance creation.

Josef Adamcik
  • 5,620
  • 3
  • 36
  • 42
  • Also you may be interested in this q/a: https://stackoverflow.com/questions/22799407/looking-for-an-example-for-dagger-assisted-injection – Josef Adamcik May 08 '18 at 08:54
1

After David Medenjak's comment I realized that I'm not using constructor injection correctly and the approach was wrong. As David Medenjak suggested I created a setter for the List<Property> in the adapter and than I initialized it in the fragment. So the solution is:

public class PropertyListAdapter extends RecyclerView.Adapter<PropertyListAdapter.ViewHolder> {

    private List<Property> mPropertyList;

    public void setPropertyList(List<Property> propertyList) {
        mPropertyList = propertyList;
    }

    @Inject
    public PropertyListAdapter() {
    }

    // onCreateViewHolder()
    // onBindViewHolder()
    // getItemCount()
    // class ViewHolder{}
}

and in the fragment I needed to set the property list:

private void setPropertyAdapter(List<Property> properties) {
    mPropertyListAdapter.setPropertyList(properties); // added this line
    rvPropertyList.setAdapter(mPropertyListAdapter);
}
Andritchi Alexei
  • 1,380
  • 1
  • 16
  • 23