google / dagger

A fast dependency injector for Android and Java.
https://dagger.dev
Apache License 2.0
17.43k stars 2.01k forks source link

@ContributesAndroidInjector: Cannot be provided without an @Provides-annotated method. #1249

Closed jega-ms closed 6 years ago

jega-ms commented 6 years ago

"@ContributesAndroidInjector" could not inject the fragment view interface on presenter constructor (MVP) but the same approach works fine with activity flow.

Error.

 error: [Dagger/MissingBinding] [dagger.android.AndroidInjector.inject(T)] com.jega.android.example.dagger2.login.LoginView cannot be provided without an @Provides-annotated method.
com.jega.android.example.dagger2.login.LoginView is injected at
com.jega.android.example.dagger2.login.LoginPresenter.<init>(…, callback)
com.jega.android.example.dagger2.login.LoginPresenter is injected at
com.jega.android.example.dagger2.login.LoginFragment.presenter
com.jega.android.example.dagger2.login.LoginFragment is injected at
com.jega.android.example.dagger2.MainActivity.login
com.jega.android.example.dagger2.MainActivity is injected at
dagger.android.AndroidInjector.inject(T)
component path: com.jega.android.example.dagger2.di.MyAppComponent → com.jega.android.example.dagger2.di.MyActivityModule_AccountActivityInjector.MainActivitySubcomponent

Source Code https://github.com/jega-ms/android-dagger2-mvp-rx/blob/master/app/src/main/java/com/jega/android/example/dagger2/MainActivity.java


@Module
public abstract class MainActivityModule {

    @ContributesAndroidInjector(modules = LoginFragmentModule.class)
    abstract LoginFragment LoginFragmentInjector();

    @Binds
    abstract MainActivityView bindMainActivityView(MainActivity mainActivity);

    @Binds
    abstract Activity activity(MainActivity mainActivity);

}

public class LoginFragment extends BaseFragment implements LoginView {
    @Inject
    LoginPresenter presenter;

    @Inject  // If we comment this code and create a instance from activity it works fine.
    public LoginFragment() {
    }
}

@Module
public abstract class LoginFragmentModule {

    @Binds
    abstract LoginView activityLoginFragment(LoginFragment login);
}

public class LoginPresenter {

    private AccountManager accountManager;
    private LoginView callback;

    @Inject
    public LoginPresenter(AccountManager accountManager, LoginView callback) {
        this.accountManager = accountManager;
        this.callback = callback;

    }

    public void login() {
        accountManager.login();
        callback.onSuccess();

    }
}
ronshapiro commented 6 years ago

This appears to be a question - please use StackOverflow with the dagger-2 tag instead, as that is a better forum for questions.

sqsong66 commented 6 years ago

@jega-ms Are you solve it? I face the same problem.

jega-ms commented 6 years ago

yes , you can refer this example https://github.com/jega-ms/android-dagger2-example

On Fri, Oct 19, 2018 at 9:24 AM SQSong notifications@github.com wrote:

@jega-ms https://github.com/jega-ms Are you solve it? I face the same problem.

— You are receiving this because you were mentioned. Reply to this email directly, view it on GitHub https://github.com/google/dagger/issues/1249#issuecomment-431237029, or mute the thread https://github.com/notifications/unsubscribe-auth/APJShoENfM-OUjR6-AHnv6ARRt6pOBp7ks5umU0IgaJpZM4V4eUb .

-- Jegadeesan M

jega-ms commented 6 years ago

@songmao123 , To make it work , you have to comment out the "@Inject for Fragments constructor"

sqsong66 commented 6 years ago

@jega-ms Thanks for your reply, bro.

Yes, my fragment's constructor has @Inject annotation, but it didn't work. here are my code: The application component:

@Singleton
@Component(modules = [AppModule::class, CommonModule::class, HttpModule::class,
    AndroidSupportInjectionModule::class, ActivityBindingModule::class])
interface ApplicationComponent : AndroidInjector<BaseApplication> {
    @dagger.Component.Builder
    interface Builder {
        @BindsInstance
        fun application(application: Application): Builder
        fun build(): ApplicationComponent
    }
}

Then ActivityBindingModule:

@Module
abstract class ActivityBindingModule {
    @ActivityScope
    @ContributesAndroidInjector(modules = [MainModule::class])
    abstract fun contributeHomeActivity(): HomeActivity
}

The MainModule for the HomeActivity, HomeActivity has four Fragment:

@Module
abstract class MainModule {
    @FragmentScope
    @ContributesAndroidInjector(modules = [HomeModule::class])
    abstract fun contributeHomeFragment(): HomeFragment
    ...
}

The HomeModule for the first HomeFragment:

@Module
class HomeModule {
    @FragmentScope
    @Provides
    fun provideHomeView(homeFragment: HomeFragment): HomeContract.HomeView {
        return homeFragment
    }
    @FragmentScope
    @Provides
    fun provideHomeModel(apiService: ApiService): HomeModel {
        return HomeModel(apiService)
    }
}

HomeFragment implement HomeContract.HomeView, and the constructor has @Inject annotation:

class HomeFragment @Inject constructor() : BaseFragment<HomePresenter>(), HomeContract.HomeView,
        SwipeRefreshLayout.OnRefreshListener, RecyclerScrollListener.OnLoadMoreListener {
    ...
}

BaseFragment inject the Presenter:

abstract class BaseFragment<P : IPresenter<*>> : DaggerFragment(), IView {
    @Inject
    lateinit var mPresenter: P
}

HomePresenter code:

class HomePresenter @Inject constructor(private val homeModel: HomeModel,
                                        private val homeView: HomeContract.HomeView,
                                        private val disposable: CompositeDisposable) :
        BasePresenter<HomeContract.HomeView, HomeContract.Model>(homeModel, disposable) {
    ...
}

But when i build the project, it still tips: HomeContract.HomeView cannot be provided without an @Provides-annotated method.

Like you said, the Activity inject View is ok. but in the fragment, it was compile error, i don't know why?

jega-ms commented 6 years ago

i didn't get chance to get deeper into this issue. In my case, i just removed @Inject constructor annotation on all the fragments, later it's started working fine.

On Sat, Oct 20, 2018 at 6:19 PM SQSong notifications@github.com wrote:

@jega-ms https://github.com/jega-ms Thanks for your reply, bro.

Yes, my fragment's constructor has @Inject annotation, but it didn't work. here are my code: The application component:

@Singleton@Component(modules = [AppModule::class, CommonModule::class, HttpModule::class, AndroidSupportInjectionModule::class, ActivityBindingModule::class])interface ApplicationComponent : AndroidInjector { @dagger.Component.Builder interface Builder { @BindsInstance fun application(application: Application): Builder fun build(): ApplicationComponent } }

Then ActivityBindingModule:

@Moduleabstract class ActivityBindingModule { @ActivityScope @ContributesAndroidInjector(modules = [MainModule::class]) abstract fun contributeHomeActivity(): HomeActivity }

The MainModule for the HomeActivity, HomeActivity has four Fragment:

@Moduleabstract class MainModule { @FragmentScope @ContributesAndroidInjector(modules = [HomeModule::class]) abstract fun contributeHomeFragment(): HomeFragment ... }

The HomeModule for the first HomeFragment:

@Moduleclass HomeModule { @FragmentScope @Provides fun provideHomeView(homeFragment: HomeFragment): HomeContract.HomeView { return homeFragment } @FragmentScope @Provides fun provideHomeModel(apiService: ApiService): HomeModel { return HomeModel(apiService) } }

HomeFragment implement HomeContract.HomeView, and the constructor has @Inject annotation:

class HomeFragment @Inject constructor() : BaseFragment(), HomeContract.HomeView, SwipeRefreshLayout.OnRefreshListener, RecyclerScrollListener.OnLoadMoreListener { ... }

BaseFragment inject the Presenter:

abstract class BaseFragment<P : IPresenter<*>> : DaggerFragment(), IView { @Inject lateinit var mPresenter: P }

HomePresenter code:

class HomePresenter @Inject constructor(private val homeModel: HomeModel, private val homeView: HomeContract.HomeView, private val disposable: CompositeDisposable) : BasePresenter<HomeContract.HomeView, HomeContract.Model>(homeModel, disposable) { ... }

But when i build the project, it still tips: HomeContract.HomeView cannot be provided without an @Provides-annotated method.

Like you said, the Activity inject View is ok. but in the fragment, it was compile error, i don't know why?

— You are receiving this because you were mentioned. Reply to this email directly, view it on GitHub https://github.com/google/dagger/issues/1249#issuecomment-431577539, or mute the thread https://github.com/notifications/unsubscribe-auth/APJShougu1AFwanwYGNKJJX-TsCsoPOsks5umxvxgaJpZM4V4eUb .

-- Jegadeesan M