I just started using Dagger 2 and I found online thousands guides each one with a different implementation and I'm a bit confused now. So basically this is what I wrote at the moment:
AppModule.java:
@Module
public class AppModule {
Application mApplication;
public AppModule(Application application) {
mApplication = application;
}
@Provides
@Singleton
Application providesApplication() {
return mApplication;
}
}
DataModule.java:
@Module
public class DataModule {
private static final String BASE_URL = "http://beta.fridgewizard.com:9001/api/";
@Provides
@Singleton
NetworkService provideNetworkService() {
return new NetworkService(BASE_URL);
}
@Provides
@Singleton
SharedPreferences provideSharedPreferences(Application app) {
return PreferenceManager.getDefaultSharedPreferences(app);
}
}
PrefsModel.java:
@Module(includes = DataModule.class)
public class PrefsModel {
@Provides
@Singleton
QueryPreferences provideQuery(SharedPreferences prefs) {
return new QueryPreferences(prefs);
}
}
AppComponent.java (I'm exposing QueryPreferences object since I need it in a presenter, hopefully is correct in this way):
@Singleton
@Component(modules = {AppModule.class, DataModule.class, PrefsModel.class})
public interface AppComponent {
void inject(HomeFragment homeFragment);
QueryPreferences preferences();
NetworkService networkService();
}
Then I have the FwApplication.java:
public class FwApplication extends Application {
private static final String TAG = "FwApplication";
private NetworkService mNetworkService;
private AppComponent mDataComponent;
@Override
public void onCreate() {
super.onCreate();
buildComponentAndInject();
}
public static AppComponent component(Context context) {
return ((FwApplication) context.getApplicationContext()).mDataComponent;
}
public void buildComponentAndInject() {
mDataComponent = DaggerComponentInitializer.init(this);
}
public static final class DaggerComponentInitializer {
public static AppComponent init(FwApplication app) {
return DaggerAppComponent.builder()
.appModule(new AppModule(app))
.dataModule(new DataModule())
.build();
}
}
}
Finally I added another module for the presenters:
@Module
public class PresenterModule {
@Provides
Presenter<FwView> provideHomePresenter(NetworkService networkService) {
return new HomePresenterImpl(networkService);
}
@Provides
Presenter<FwView> provideSearchPresenter(NetworkService networkService) {
return new SearchPresenterImpl(networkService);
}
}
And the following component (which returns error because I cannot add a scoped dependencies here):
@Component(dependencies = AppComponent.class, modules = PresenterModule.class)
public interface PresenterComponent {
void inject(HomePresenterImpl presenter);
}
So, I have few questions that are not clear for me reading the documentation online:
EDIT - FIX:
HomeFragment.java:
public class HomeFragment extends Fragment {
private static final String TAG = "FW.HomeFragment";
@Inject
HomePresenterImpl mHomePresenter;
public static HomeFragment newInstance() {
return new HomeFragment();
}
@Override
public void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
FwApplication.component(getActivity()).inject(this);
}
Then I modified the presenter constructor in this way:
@Inject
public HomePresenterImpl(NetworkService networkService) {
mNetworkService = networkService;
mInteractor = new InteractorImpl(mNetworkService);
}
Then NetworkService is injected automatically.
I was wondering if it is correct in this way since I have to call for every fragment I have that needs a presenter constructed in the same way as the one above the following code:
FwApplication.component(getActivity()).inject(this);
You do not have to provide Presenter if @Inject annotation is used in the constructor. @Inject annotation used in the constructor of the class makes that class a part of dependencies graph. So, it also can be injected when needed.
On the other hand, if you add @Inject annotation to fields, but not to constructors, you have to provide that class.
You are mixing thing up. To provide your presenter, you should switch to something like the following:
Use constructor injection if possible. It will make things much easier
public class HomePresenterImpl {
@Inject
public HomePresenterImpl(NetworkService networkService) {
// ...
}
}
To provide the interface use this constructor injection and depend on the implementation:
Presenter<FwView> provideHomePresenter(HomePresenterImpl homePresenter) {
return homePresenter;
}
This way you don't have to call any constructors yourself. And to actually inject the presenter...
public class MyFragment extends Fragment {
@Inject
Presenter<FwView> mHomePresenter;
public void onCreate(Bundle xxx) {
// simplified. Add your modules / Singleton component
PresenterComponent component = DaggerPresenterComponent.create().inject(this);
}
}
This way you will inject the things. Please read this carefully and try to understand it. This will fix your major problems, you still can not provide 2 presenters of the same type from the same module (in the same scope)
// DON'T
@Provides
Presenter<FwView> provideHomePresenter(NetworkService networkService) { /**/ }
@Provides
Presenter<FwView> provideSearchPresenter(NetworkService networkService) { /**/ }
This will not work. You can not provide 2 objects of the same kind. They are indistinguishable. Have a look at @Qualifiers
like @Named
if you are sure this is the way you want to go.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With