web-dev-qa-db-fra.com

ne peut pas être fourni sans un constructeur @Inject ou à partir d'une méthode @ Provides-annotated

J'utilise Android Dagger2 mais je reçois l'erreur ci-dessous.

Ma classe AppModule est:

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

Mon interface AppComponent est:

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

Ma classe DaggerInject

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

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

J'essaie d'injecter dans mon 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;
}

Et ma partie constructeur DownloadFilePresenterImp

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

C'est l'erreur que j'obtiens:

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)

Merci beaucoup pour vos suggestions,

26
ant2009

Ok ... ça devrait marcher

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

et

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) {
                [...]
            }
        }
    }
}

Fondamentalement, tous les objets fournis par le graphique (le composant) doivent être construits avec des objets appartenant ou créés par le graphique lui-même. J'ai donc supprimé le constructeur DownloadFilePresenterImp(DownloadFileView downloadFileView) qui va provoquer une fuite et je l'ai remplacé par un séparateur qui crée un WeakReference à la vue.

EDIT

Si vous souhaitez injecter un objet que vous avez créé au moment de l'exécution, la seule solution consiste à transmettre cet objet à AppComponent. Dans votre cas, cela pourrait être:

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);
    }

}
5
Mimmo Grottoli

L'erreur indique simplement que le poignard n'a pas le moyen de fournir ladite dépendance. Vous devrez l’ajouter à votre composant d’une manière ou d’une autre - et comme il s’agit d’un Fragment - vous devrez utiliser un @Module.

Je suppose que votre AppComponent est créé par votre Application au début. Votre AppComponent a un cycle de vie plus long que celui de vos activités et de vos fragments. Il est donc raisonnable qu’il ne sache pas comment vous proposer une activité ou un fragment dans votre cas.

  1. Votre DownloadFilePresenterImp dépend de votre DownloadFileView.
  2. Vous voulez injecter DownloadFilePresenterImp dans votre DownloadFileView
  3. Pour injecter la vue et le présentateur, vous utilisez votre AppComponent qui ne sait rien de l'activité, et évidemment rien du fragment. Il a une portée et un cycle de vie différents.

Pour éviter toute confusion, je parlerai de fragments, car leur cycle de vie et celui de leurs activités sont ce que vous devez garder à l’esprit. Vous pouvez simplement utiliser DownloadFileView avec le module, mais ces noms longs deviendraient confus.


Pour fournir un fragment ou une activité que vous devez pour utiliser un module. par exemple.

@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;
    }
}

Étant donné que ce module ne doit vivre qu'avec le cycle de vie des fragments, vous devez utiliser des sous-composants ou des composants dépendant de votre AppComponent.

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

Dans votre fragment, vous devez créer correctement votre composant ...

@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;
}

En outre, je fortement recommandé jeter un oeil sur et utiliser injection de constructeur

public class DownloadFilePresenterImp implements DownloadFilePresenterContact {

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

Sinon, vous pouvez déplacer la méthode provideDownloadfilePresenterImp(View) vers FragmentModule pour avoir le même effet si vous aimez le code redondant.

Terminé.

27
David Medenjak

Pour moi, le problème était qu'un autre composant avait une méthode d'injection inutilisée avec la même classe en tant que paramètre d'entrée. Supprimé cette méthode d'injection et il a compilé.

2
Adam Kis

Dans mon cas, j'ai eu une classe de présentateur à Kotlin, tout en utilisant Dagger2. J'ai oublié @Inject constructor en tant que constructeur du présentateur.

2
Sava B.

Je vous recommande vivement de vérifier cette implémentation complète de dragger: https://github.com/Spentas/securechat/blob/master/app/src/main/Java/com/spentas/javad/securechat/app/ AppComponent.Java

Ou suivez les instructions suivantes:

Dans votre AppModule:

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

votre AppComponent:

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

alors vous devez initialiser votre poignard dans votre classe d'application:

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;
    }

Où que vous vouliez utiliser:

((App) App.getContext()).getComponent().inject(this);
1
Javad Jafari