1

How to inject Presenter in a Fragment using Dagger2

I have written below code

@Module
public abstract class ActivityBuilder {

    @ContributesAndroidInjector(modules = { DetailCastActivityModule.class, FragmentDependencyProvider.class })
    abstract DetailCastActivity bindDetailCastActivity();
}

@Module
public abstract class FragmentDependencyProvider {

    @ContributesAndroidInjector(modules = CastInfoFragmentModule.class)
    abstract CastInfoFragment provideCastInfoFragmentFactory();
}

@Module
public class CastInfoFragmentModule {

    @Provides
    CastInfoMvpPresenter<CastInfoMvpView> provideCastInfoMvpPresenter(CastInfoPresenter<CastInfoMvpView> presenter) {
        return presenter;
    }

}

But i am still getting below error even i have written the provides method

Error:(24, :sunglasses: error: [dagger.android.AndroidInjector.inject(T)] com.app.nmot.ui.castdetail.info.CastInfoPresenter cannot be provided without an @Provides- or @Produces-annotated method.
com.app.nmot.ui.castdetail.info.CastInfoPresenter is injected at
com.app.nmot.ui.castdetail.info.CastInfoFragment.presenter
com.app.nmot.ui.castdetail.info.CastInfoFragment is injected at
dagger.android.AndroidInjector.inject(arg0)

The answer mentioned in the comment does not solve my problem. I have checked all the annotations and i am using new android injector provided by Dagger2

Passiondroid
  • 1,573
  • 1
  • 16
  • 28
  • Possible duplicate of [How do I fix Dagger 2 error '... cannot be provided \[...\]'?](https://stackoverflow.com/questions/44912080/how-do-i-fix-dagger-2-error-cannot-be-provided) – David Medenjak Sep 26 '17 at 10:52
  • this solution does not suit you? https://stackoverflow.com/questions/36838898/presenter-injection-with-dagger-2?answertab=votes#tab-top – Maxime Jallu Sep 26 '17 at 11:11
  • @MaximeJallu - No , I have checked all the annotations and i am using new android injector provided by Dagger2 – Passiondroid Sep 26 '17 at 11:33

2 Answers2

2

1- Create your Scope

import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;

import javax.inject.Scope;

@Scope
@Retention(RetentionPolicy.RUNTIME)
public @interface BaseScope {
}

2- Create your contract

public interface FeatureContract {
   interface View {
        void onReceiveError(Throwable throwable);
        void onReceiveItems(List<Object> items);
        void showAlertDialog();
        ... //others methods
    }

    interface Presenter {
        void onInitView(Object item);
    }
}

3- Create your module (dagger2)

import dagger.Module;
import dagger.Provides;

@Module
public class FeatureContractModule {

    private final FeatureContract.View mView;

    public FeatureContractModule(FeatureContract.View view) {
        mView = view;
    }

    @Provides @BaseScope
    FeatureContract.Presenter providesFeaturePresenter(FeatureContract.View view) {
        return new FeaturePresenter(view);
    }

    @Provides @BaseScope
    FeatureContract.View providesFeatureView() {
        return mView;
    }
}

4- Create your presenter

public class FeaturePresenter implements FeatureContract.Presenter{

   @NonNull
   private final FeatureContract.View mView;

   public FeaturePresenter(@NonNull FeatureContract.View view){
      mView = view;
   }

   @Override
   public void onInitView(Object item){
      mView.showAlertDialog(); //<--for sample
   }
}

5- In your Fragment

import javax.inject.Inject;

public class FeatureFragment extends Fragment implements FeatureContract.View{
@Inject FeatureContract.Presenter mPresenter;

@Override public void onActivityCreated(@Nullable Bundle savedInstanceState) {
        super.onActivityCreated(savedInstanceState);
        ((MyApplication) getActivity().getApplication()).getDataComponent()
                                                                 .plus(new FeatureContractModule(this /*view*/))
                                                                 .inject(this /*fragment*/);

        mPresenter. onInitView(null);
    }
}

5- Your Application

public class MyApplication extends Application {
    //Dagger object
    private DataComponent mDataComponent;

    /**
     * Dagger Injector
     */
    public static DataComponent get(Context context) {
        MyApplication application = (MyApplication) context.getApplicationContext();
        return application.mDataComponent;
    }

    @Override
    public void onCreate() {
    super.onCreate();
    mDataComponent = DaggerDataComponent.builder()
                                        .dataModule(new DataModule(this, Locale.getDefault()))
                                        .build();
    }
    public DataComponent getDataComponent() {
        return mDataComponent;
    }   
}

6 - Create DataComponent

import javax.inject.Singleton;

import dagger.Component;

@Singleton
@Component(modules = {DataModule.class})
public interface DataComponent {
Application application();
FeatureComponent plus(FeatureContractModule module);
...
}

7 - Finally your ComponentModule

import dagger.Module;
import dagger.Provides;

@BaseScope
@Subcomponent(modules = FeatureContractModule.class)
public interface FeatureComponent {
    void inject(FeatureFragment fragment);
}

I believe I have not forgotten anything

Maxime Jallu
  • 2,352
  • 1
  • 10
  • 12
  • see blog : [https://android.jlelse.eu/android-mvp-architecture-with-dependency-injection-dee43fe47af0](MVP_Dagger2 Article1) Or : [https://www.raywenderlich.com/146804/dependency-injection-dagger-2](MVP_Dagger2 Article2) – Maxime Jallu Sep 26 '17 at 13:03
  • Maxim, is it possible to inject not into a field when working with fragments? The problem is that fragmnets prefer the default constructor :/ – Sergio Jan 08 '19 at 11:16
  • @MaximeJallu your answer worked for me correctly. Thanks for that. But suppose now I want to inject class "A" in my presenter i.e. in the FeaturePresenter as mentioned in the above example. How should I do that? Currently, in the FeatureContractModule class I have created a "Provides" annotation which returns the new A()[default constructor] & in the FeaturePresenter class in have marked class A instance with "Inject" annotation. But when ever I am making a call to class A functions, its a NPE. – bhanu kaushik Sep 16 '19 at 12:16
  • @MaximeJallu P.S. I dont want to pass class A in the constructor of FeaturePresenter but want to instantiate it with in presenter due to some circumstances. Thanks – bhanu kaushik Sep 16 '19 at 12:18
0

Make your CastInfoFragmentModule abstract and add this:

@YourScope
@Binds
abstract CastInfoMvpPresenter<CastInfoMvpView> bindPresenter(CastInfoPresenter<CastInfoMvpView> presenter);

Also, you have to add @Inject on the constructor of CastInfoPresenter

Benjamin
  • 7,055
  • 6
  • 40
  • 60