Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Dagger 2: inject an interface in a constructor

I'm trying to learn dagger 2 but I'm confused in injecting of constructor with interface. This is my below code :

MainActivity.java

public class MainActivity extends AppCompatActivity implements MainView {

    // this keyword of request dependency . At compiling process, dagger will look at all of these annotations
    //to create the exact dependency

    @Inject MainPresenter mainPresenter ;
    TextView textView ;


    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        textView = findViewById(R.id.textview) ;
        DaggerPresenterComponent.create().inject(this);
        textView.setOnClickListener(new OnClickListener() {
            @Override
            public void onClick(View view) {
                mainPresenter.doThings(8555) ;
            }
        });

    }

    /**********************************/

    @Override
    public void invokeRandomViewMethod(String msg) {
        textView.setText(msg);
    }
}

MainPresenter.java

public class MainPresenter {

    private MainView mainView ;

    @Inject
    public MainPresenter(MainView mainView) {
        this.mainView = mainView;
    }

    public void doThings(int value){
        Random random = new Random();
        int rand= random.nextInt(value) ;
        if(mainView != null){
            mainView.invokeRandomViewMethod("You random number is "+rand);
        }
    }

public interface MainView {
    void invokeRandomViewMethod(String msg) ;
}
}

This is the Module :

@Module
public class PresenterModule {

    @Provides
        // this is the method that will provide the dependancy
    MainPresenter provideMainPresenter(MainView mainView){
        return new MainPresenter(mainView);
    }
}

And this is the Component

@Component (modules = PresenterModule.class)
public interface PresenterComponent {
    void inject(MainActivity activity) ;
}

When I run the code it shows me this error :

Error:(15, 10) error: com.imennmn.hellodagger2example.MainView cannot be provided without an @Provides-annotated method. com.imennmn.hellodagger2example.MainView is injected at com.imennmn.hellodagger2example.presenterInjection.PresenterModule.provideMainPresenter(mainView) com.imennmn.hellodagger2example.MainPresenter is injected at com.imennmn.hellodagger2example.MainActivity.mainPresenter com.imennmn.hellodagger2example.MainActivity is injected at com.imennmn.hellodagger2example.simpleInjection.DataComponent.inject(activity)

My Question is how I can provide the interface MainView by inject it with dagger and bind the MainPresenter and MainActivity ? Any help would be appreciated !

like image 461
Imene Noomene Avatar asked Dec 27 '17 10:12

Imene Noomene


People also ask

What does @inject constructor mean?

Definition. Constructor Injection is the act of statically defining the list of required Dependencies by specifying them as parameters to the class's constructor. The constructor signature is compiled with the type and it's available for all to see.

What is @inject in dagger?

Dependency Injection, or DI in short, is a design pattern that allows to delegate the creation of objects and their dependencies to another object or framework. It is gaining a lot of interest in Android application development. This post shows how to inject objects using Dagger 2 through a console app.

How do you inject a dependency?

The injector class injects dependencies broadly in three ways: through a constructor, through a property, or through a method. Constructor Injection: In the constructor injection, the injector supplies the service (dependency) through the client class constructor.


3 Answers

By following code:


    MainPresenter provideMainPresenter(MainView mainView) {
        return new MainPresenter(mainView);
    }

You are telling dagger: "hey, whenever I ask you to inject MainPresenter, construct it using MainView". But dagger complaints, because you haven't specified how exactly he should build/acquire MainView.

So, in your PresenterModule do this:


    @Module
    public class PresenterModule {

        MainView mainView;

        public PresenterModule(MainView mainView) {
            this.mainView = mainView;
        }

        @Provides
        MainPresenter provideMainPresenter() {
            return new MainPresenter(mainView);
        }

    }

Then when building the component:


    DaggerPresenterComponent.builder()
                            .presenterModule(new PresenterModule(this))
                            .build();

like image 170
azizbekian Avatar answered Oct 20 '22 23:10

azizbekian


Your provideMainPresenter implicitly depends on a MainView. Dagger has no way to get it. You need to add a method to provide it:

@Module
public class PresenterModule {

    @Provides
    MainView provideMainView(){
        // Provide MainView here somehow so Dagger can use this to create a MainPresenter
    }

    @Provides
    // this is the method that will provide the dependancy
    MainPresenter provideMainPresenter(MainView mainView){
        return new MainPresenter(mainView);
    }
}
like image 38
Veneet Reddy Avatar answered Oct 20 '22 22:10

Veneet Reddy


Add abstract module with @Binds annotation, look at my impl of : AbstractTestSettingsFragmentModule.java

TestFragment.java

public class TestFragment extends Fragment{

    @Inject TestFragmentContract.Presenter mPresenter;

    @Override
    public void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        AndroidSupportInjection.inject(this);

    }

 }

TestFragmentPresenterImpl.java

public class TestFragmentPresenterImpl implements TestFragmentContract.Presenter {

    @Inject
    public TestFragmentPresenterImpl(){
    }
}

AbstractTestSettingsFragmentModule.java

@Module
public abstract class AbstractTestSettingsFragmentModule {

    @Binds
    @NonNull
    public abstract TestSettingsFragmentContract.Presenter testSettingsFragmentPresenterImpl(TestSettingsFragmentImpl presenter);

}

ContributesModule.java

   @Module
    public abstract class ContributesModule {

        @ContributesAndroidInjector(modules = {AbstractTestSettingsFragmentModule.class})
        abstract TestSettingsFragment testSettingsFragment();
    }

AppComponent.java

@Singleton
@Component(
        modules = {
                AndroidSupportInjectionModule.class,
                ContributesModule.class,
                AppModule.class,
              })
public interface AppComponent extends AndroidInjector<DaggerApplication> {

    void inject(TheApplication theApplication);

    @Override
    void inject(DaggerApplication instance);

    @Component.Builder
    interface Builder {

        @BindsInstance
        Builder application(Application application);

        AppComponent build();

    }

}
like image 1
NickUnuchek Avatar answered Oct 20 '22 22:10

NickUnuchek