Reputation: 417
I am trying to inject a presenter for my Fragment
via Constructor injection (so that I do not need to create a @Provides
method for the presenter).
I believe that I am doing it correctly, but I am still receiving the following error:
MainFragmentPresenter cannot be provided without a @Provides- or @Produces-annotation method.
Here is my setup:
ApplicationComponent.java
@Singleton
@Component(
modules = {
NetworkModule.class
}
)
public interface ApplicationComponent {
NetworkService networkService();
}
MainFragmentComponent.java
@PerFragment
@Component(
dependencies = ApplicationComponent.class,
modules = MainFragmentPresenterModule.class
)
public interface MainFragmentComponent {
MainFragmentView view();
void inject(MainFragment mainFragment);
}
MainFragmentPresenterModule.java
@Module
public class MainFragmentPresenterModule {
private final MainFragmentView _view;
public MainFragmentPresenterModule(MainFragmentView view) {
_view = view;
}
@Provides
@PerFragment
MainFragmentView provideMainFragmentView() {
return _view;
}
}
NetworkModule.java
@Module
public class NetworkModule {
@Provides
@Singleton
OkHttpClient provide_OkHttpClient() {
return new OkHttpClient();
}
@Provides
@Singleton
OpenWeatherEndpoints provide_OpenWeatherAPI(OkHttpClient client) {
Retrofit retrofit = new Retrofit.Builder()
.baseUrl("http://api.openweathermap.org/data/2.5/")
.addConverterFactory(GsonConverterFactory.create())
.addCallAdapterFactory(RxJava2CallAdapterFactory.create())
.client(client)
.build();
return retrofit.create(OpenWeatherEndpoints.class);
}
@Provides
@Singleton
YoutubeEndpoints provide_YoutubeEndpoints(OkHttpClient client) {
Retrofit retrofit = new Retrofit.Builder()
.baseUrl("https://www.googleapis.com/youtube/v3/")
.addConverterFactory(GsonConverterFactory.create())
.addCallAdapterFactory(RxJava2CallAdapterFactory.create())
.client(client)
.build();
return retrofit.create(YoutubeEndpoints.class);
}
@Provides
@Singleton
NetworkService provide_NetworkService(OpenWeatherEndpoints openWeatherEndpoints, YoutubeEndpoints youtubeEndpoints) {
return new NetworkService(openWeatherEndpoints, youtubeEndpoints);
}
}
PerFragment.java
@Scope
@Retention(RUNTIME)
@interface PerFragment {
}
MainFragmentPresenterImpl.java
public class MainFragmentPresenterImpl implements MainFragmentPresenter {
private final MainFragmentView _view;
private NetworkService _service;
@Inject MainFragmentPresenterImpl(MainFragmentView view, NetworkService networkService) {
_view = view;
_service = networkService;
}
}
And when I try to inject the presenter in my fragment...
public class MainFragment extends Fragment implements MainFragmentView {
public static final String TAG = "MainFragment";
@Inject
MainFragmentPresenter _presenter;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
DaggerMainFragmentComponent.builder()
.applicationComponent(WeatherForecastApp.get(getContext()).getAppComponent())
.mainFragmentPresenterModule(new MainFragmentPresenterModule(this))
.build()
.inject(this);
}
}
All help is greatly appreciated! thanks.
Upvotes: 1
Views: 725
Reputation: 56
You need to add a binding from MainFragmentPresenterImpl to MainFragmentPresenter. Dagger does not cast between types, so you need to do that explicitly with a @Binds method. @Binds is equivalent to a @Provides method that just returns its argument, but it's implemented more efficiently. Just change your module like this:
@Module
public abstract class MainFragmentPresenterModule {
...
@Binds
abstract MainFragmentPresenter(MainFragmentPresenterImpl impl);
}
Also, you can get rid of the @Provides method, _view
field, and constructor in the module by using a @BindsInstance.
public interface MainFragmentComponent {
@Component.Builder
interface Builder {
@BindsInstance
Builder mainFragmentView(MainFragmentView mainFragmentView);
}
void inject(MainFragment mainFragment);
}
Now, since your your module doesn't have a constructor, Dagger doesn't need to be provided an instance, so you can construct the component like this:
DaggerMainFragmentComponent.builder()
.applicationComponent(WeatherForecastApp.get(getContext()).getAppComponent())
.mainFragmentView(this)
.build()
.inject(this);
Upvotes: 1