I wanna to make a simple project with one main activity and multiple fragments. here I have two fragments at one activity and I want to inject presenter to login fragment but it doesn't work. where is my mistake?
MainApplication.java
public class MainApplication extends DaggerApplication{
private static ApplicationComponent component;
@Override
public void onCreate() {
super.onCreate();
Utils.init(this);
}
public static ApplicationComponent getComponent() {
return component;
}
protected AndroidInjector<? extends DaggerApplication> applicationInjector()
{
component =
DaggerApplicationComponent.builder().application(this).build();
component.inject(this);
return component;
}
}
MainActivity.java
public class MainActivity extends DaggerAppCompatActivity {
private Fragment[] mFragments = new Fragment[2];
private int curIndex;
@Inject
HomeFragment homeFragment;
@Inject
LoginFragment loginFragment;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
if (savedInstanceState != null) {
curIndex = savedInstanceState.getInt("curIndex");
}
mFragments[AppConstant.HOME_FRAGMENT] = homeFragment;
mFragments[AppConstant.LOGIN_FRAGMENT] = loginFragment;
FragmentUtils.add(getSupportFragmentManager(), mFragments,
R.id.container, curIndex);
showCurrentFragment(AppConstant.LOGIN_FRAGMENT);
}
private void showCurrentFragment(int index) {
FragmentUtils.showHide(curIndex = index, mFragments);
}
@Override
public void onSaveInstanceState(Bundle outState, PersistableBundle
outPersistentState) {
super.onSaveInstanceState(outState, outPersistentState);
outState.putInt("curIndex", curIndex);
}
}
ApplicationComponent.java
@Singleton
@Component(modules = {
ContextModule.class,
ApiServiceModule.class,
AndroidSupportInjectionModule.class,
ActivityBuilder.class
})
public interface ApplicationComponent extends
AndroidInjector<DaggerApplication> {
void inject(MainApplication component);
@Component.Builder
interface Builder {
ApplicationComponent build();
@BindsInstance
Builder application(MainApplication application);
}
}
LoginFragment.java
public class LoginFragment extends DaggerFragment{
View mRootView;
@Inject
HomeFragment homeFragment;
@Inject
LoginContract.Presenter mPresenter;
@Inject
public LoginFragment() {
// Required empty public constructor
}
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
}
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
mRootView =inflater.inflate(R.layout.fragment_login, container, false);
ButterKnife.bind(this,mRootView);
// Inflate the layout for this fragment
return mRootView;
}
@OnClick(R.id.button)
public void goToHome(){
FragmentUtils.replace(this,homeFragment,false);
}
@Override
public void onAttach(Context context) {
super.onAttach(context);
}
@Override
public void onDetach() {
super.onDetach();
}
}
ActivityBuilder.java
@Module
public abstract class ActivityBuilder {
@ContributesAndroidInjector(modules = {MainActivityModule.class ,
FragmentContainerModule.class, AndroidSupportInjectionModule.class} )
abstract MainActivity bindMainActivity();
}
FragmentContainerModule.java
@Module
public abstract class FragmentContainerModule {
@ContributesAndroidInjector(modules =
{HomeFragmentModule.class,
AndroidSupportInjectionModule.class})
abstract HomeFragment contributeHomeFragmentInjector();
@FragmentScope
@ContributesAndroidInjector(modules =
{LoginFragmentModule.class,
AndroidSupportInjectionModule.class})
abstract LoginFragment contributeLoginFragmentInjector();
}
** LoginFragmentModule.java
@Module
public abstract class LoginFragmentModule {
@FragmentScope
@Binds
public abstract LoginContract.Presenter presenter(LoginPresenter
loginPresenter);
}
and this is the error :
error: [Dagger/MissingBinding] [dagger.android.AndroidInjector.inject(T)]
ir.heart.heartproject.utils.MvpUtils.LoginContract.Presenter cannot be
provided without an @Provides-annotated method.
A binding with matching key exists in component:
ir.heart.heartproject.utils.di.
modules.FragmentContainerModule_ContributeLoginFr
agmentInjector.LoginFragmentSubcomponent
ir.heart.heartproject.utils.MvpUtils.
LoginContract.Presenter is injected at
ir.heart.heartproject.views.fragments.LoginFragment.mPresenter
ir.heart.heartproject.views.fragments.LoginFragment is injected at
ir.heart.heartproject.views.activities.MainActivity.loginFragment
ir.heart.heartproject.views.activities.MainActivity is injected at
dagger.android.AndroidInjector.inject(T)
component path:
ir.heart.heartproject.utils.di.component.ApplicationComponent ?
ir.heart.heartproject.utils.di.modules.
ActivityBuilder_BindMainActivity.MainActivitySubcomponent
Hilt is built on top of Dagger and it provides a standard way to incorporate Dagger dependency injection into an Android application. Note: If you're already familiar with Dagger, check out these best practices.
For more information about this, check out the Using Dagger in your Android app codelab. Dagger modules are a way to encapsulate how to provide objects in a semantic way. You can include modules in components but you can also include modules inside other modules. This is powerful, but can be easily misused.
A generic inject () method doesn't tell Dagger what needs to be provided. The functions in the interface can have any name, but calling them inject () when they receive the object to inject as a parameter is a convention in Dagger.
For more information about how to inject these classes, check out the code in the official Android Blueprints Dagger implementation, in the dev-dagger branch. One of the considerations with Dagger is that injected fields cannot be private.
You use @inject to annotate the constructor of LoginFragment, when a new instance is requested, Dagger constructs instances of your LoginFragment class and satisfies their dependencies (like the mPresenter field). Because MainActivitySubcomponent doesn’t access LoginFragmentModule, Dagger gives you an error that can not provide LoginContract.Presenter. instead of using constructor injection, if you use another approach for providing LoginFragment for MainActivity, the problem will be solved. You should remove @inject from LoginFragment constructor and create a module to provide it like the following example:
@Module
public class MainModule {
@Provides
public static LoginFragment provideLoginFragment() {
return LoginFragment.newInstance("param1", "param2");
}
}
Dagger tips: don't use Constructor injection for an object that system instantiate it. in android, for injecting dependency in activity and fragment, you should use field injection approach.
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