Home » Android » android – cannot be provided without an @Inject constructor or from an @Provides-annotated method

android – cannot be provided without an @Inject constructor or from an @Provides-annotated method

Posted by: admin April 23, 2020 Leave a comment

Questions:

I am using Android Dagger2 but I am getting the error below.

My AppModule class is:

@Module
public class AppModule {
    @Provides
    public DownloadFilePresenterImp provideDownloadfilePresenterImp(DownloadFileView downloadFileView) {
        return new DownloadFilePresenterImp(downloadFileView);
    }
}

My AppComponent interface is:

@Singleton
@Component(modules = AppModule.class)
public interface AppComponent {
    void inject(DownloadFileView target);
}

My DaggerInject class

public class DaggerInjector {
    private static AppComponent mAppComponent = DaggerAppComponent
            .builder()
            .appModule(new AppModule())
            .build();

    public static AppComponent getAppComponent() {
        return mAppComponent;
    }
}

I am trying to inject into my Fragment (DownloadFileView)

@Inject DownloadFilePresenterImp mDownloadFilePresenterImp;

@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
   final View view = inflater.inflate(R.layout.download_file_view, container, false);
   /* Initialize presenter */
   DaggerInjector.getAppComponent().inject(DownloadFileView.this);


   /* Use mDownloadFilePresenterImp here */       
   return view;
}

And my DownloadFilePresenterImp constructor part

public class DownloadFilePresenterImp implements DownloadFilePresenterContact {
    public DownloadFilePresenterImp(DownloadFileView downloadFileView) {
        mDownloadFileContract = downloadFileView;
    }
}

This is the error I am getting:

Error:(17, 10) error: com.sunsystem.downloadfilechatapp.downloader.DownloadFileView cannot be provided without an @Inject constructor or from an @Provides-annotated method. This type supports members injection but cannot be implicitly provided.
com.sunsystem.downloadfilechatapp.downloader.DownloadFileView is injected at
com.sunsystem.downloadfilechatapp.downloader.dagger.AppModule.provideDownloadfilePresenterImp(downloadFileView)
com.sunsystem.downloadfilechatapp.downloader.DownloadFilePresenterImp is injected at
com.sunsystem.downloadfilechatapp.downloader.DownloadFileView.mDownloadFilePresenterImp
com.sunsystem.downloadfilechatapp.downloader.DownloadFileView is injected at
com.sunsystem.downloadfilechatapp.downloader.dagger.AppComponent.inject(target)

Many thanks for any suggestions,

How to&Answers:

Ok…this should work

@Module
public class AppModule {
    @Provides
    public DownloadFilePresenterImp provideDownloadfilePresenterImp() {
        return new DownloadFilePresenterImp();
    }
}

and

public class DownloadFilePresenterImp implements DownloadFilePresenterContact {

    private WeakReference<DownloadFileView> weak;

    public DownloadFilePresenterImp() {

    }

    public void setDownloadFileView(DownloadFileView view) {
        weak = new WeakReference<>(view);
    }

    public void doSomething() {
        if (weak != null) {
            DownloadFileView view = weak.get();
            if (view != null) {
                [...]
            }
        }
    }
}

Basically, all objects provided by the graph (the component) must be built with objects belonging or built by the graph itself. So I removed the DownloadFilePresenterImp(DownloadFileView downloadFileView) constructor that is going to cause a leak and replaced it with a setter that creates a WeakReference to the View.

EDIT

If you want to inject an object that you build at runtime, the only way to go is to pass that object to the AppComponent. In your case it could be:

AppComponent.java

@Singleton
@Component(modules = AppModule.class)
public interface AppComponent {

    void inject(DownloadFileView target);
}

AppModule.java

@Module
public class AppModule {

    private final DownloadFileView downloadFileView;

    public AppModule(DownloadFileView downloadFileView) {
        this.downloadFileView = downloadFileView;
    }

    @Provides
    public DownloadFilePresenterImp provideDownloadfilePresenterImp() {
        return new DownloadFilePresenterImp(downloadFileView);
    }
}

DownloadFilePresenterImp.java

public class DownloadFilePresenterImp {

    private final DownloadFileView downloadFileView;

    public DownloadFilePresenterImp(DownloadFileView downloadFileView) {
        this.downloadFileView = downloadFileView;
    }
}

DownloadFileView.java

public class DownloadFileView extends Fragment {

    private AppComponent mAppComponent;

    @Inject
    DownloadFilePresenterImp impl;


    public DownloadFileView() {
        // Required empty public constructor
        mAppComponent = DaggerAppComponent
                .builder()
                .appModule(new AppModule(this))
                .build();
        mAppComponent.inject(this);
    }


    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container,
                             Bundle savedInstanceState) {
        // Inflate the layout for this fragment
        return inflater.inflate(R.layout.fragment_download_file_view, container, false);
    }

}

Answer:

The error just states that dagger doesn’t have a way to provide said dependency. You will have to add it to your component in some way—and since it is a Fragment—you will have to use a @Module.

I am assuming that your AppComponent is created by your Application on start. Your AppComponent has a lifecycle that is longer than both your activities and fragments lifecycles are. It is therefore reasonable, that it does not know how to provide an activity, or a fragment in your case.

  1. Your DownloadFilePresenterImp depends on your DownloadFileView.
  2. You want to inject DownloadFilePresenterImp into your DownloadFileView
  3. To inject the view and presenter you are using your AppComponent which knows nothing about the activity, and obviously nothing about the fragment. It has a different scope and lifecycle.

To not further cause confusion I will be talking about fragments, since their and the activities lifecycle are what you have to keep in mind. You can just use DownloadFileView with the module, but those long names would get confusing.


To provide a fragment or activity you have to use a module. e.g.

@Module FragmentModule {
    Fragment mFragment;
    FragmentModule(Fragment fragment) { mFragment = fragment; }

    @Provides Fragment provideFragment() { return mFragment; }

    // here you could also provide your view implementation...
    @Provides DownloadFileView provideDownloadFileView() {
        return (DownloadFileView) mFragment;
    }
}

Since this module should only live along with fragments lifecycle, you have to use either subcomponents, or components with a dependency on your AppComponent.

@Component(dependencies=AppComponent.class, modules=FragmentModule.class)
interface FragmentComponent {
    // inject your fragment
    void inject(DownloadFileView fragment);
}

In your fragment you’d have to properly create your component…

@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
   final View view = inflater.inflate(R.layout.download_file_view, container, false);

   // properly create a component that knows about the fragment
   DaggerFragmentComponent.builder()
           .appComponent(DaggerInjector.getAppComponent())
           .fragmentModule(new FragmentModule(this))
           .build()
           .inject(DownloadFileView.this);

   return view;
}

Also, I highly recommend having a look at and using constructor injection

public class DownloadFilePresenterImp implements DownloadFilePresenterContact {

    @Inject
    public DownloadFilePresenterImp(DownloadFileView downloadFileView) {
        mDownloadFileContract = downloadFileView;
    }
}

Alternatively you can move the provideDownloadfilePresenterImp(View) method to the FragmentModule to have the same effect if you like redundant code.

Done.

Answer:

In my case, I had a presenter class in Kotlin, while using Dagger2. I forgot
@Inject constructor
as the constructor of the presenter.

Answer:

For me the problem was that another component had an unused inject method with the same class as input parameter. Removed that inject method and it compiled.

Answer:

I highly recommend you to checkout this complete implementation of dragger :
https://github.com/Spentas/securechat/blob/master/app/src/main/java/com/spentas/javad/securechat/app/AppComponent.java

Or follow following:

In your AppModule:

@Singleton
@Provides
public DownloadFilePresenterImp  provideDownloadfilePresenterImp(DownloadFileView downloadFileView) {
    return new DownloadFilePresenterImp(downloadFileView);
}

your AppComponent:

@Singleton
@Component(modules = {AppModule.class})
public interface AppComponent {
void inject(DownloadFileView target);
}

then you need to initialize your dagger in your Application Class :

public class App extends Application {
private AppComponent component;

@Override
public void onCreate() {
    super.onCreate();
    mContext = this;
    component = DaggerAppComponent.builder().appModule(new AppModule(this)).build();

}
 public AppComponent getComponent(){
        return component;
    }

In wherever u want to use:

((App) App.getContext()).getComponent().inject(this);