web-dev-qa-db-ja.com

@Injectコンストラクターなしで、または@Provides注釈付きメソッドからは提供できません

Android Dagger2を使用していますが、以下のエラーが表示されています。

私のAppModuleクラスは次のとおりです。

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

私のAppComponentインターフェースは次のとおりです。

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

私のDaggerInjectクラス

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

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

フラグメント(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;
}

そして、私のDownloadFilePresenterImpコンストラクター部分

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

これは私が得ているエラーです:

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)

提案に感謝します、

26
ant2009

わかりました...これは動作するはずです

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

そして

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

基本的に、グラフ(コンポーネント)が提供するすべてのオブジェクトは、グラフ自体が所有または構築するオブジェクトで構築する必要があります。そこで、リークを引き起こすDownloadFilePresenterImp(DownloadFileView downloadFileView)コンストラクターを削除し、ビューへのWeakReferenceを作成するセッターに置き換えました。

[〜#〜] edit [〜#〜]

実行時にビルドするオブジェクトを注入する場合、実行する唯一の方法は、そのオブジェクトをAppComponentに渡すことです。あなたの場合、それは次のようになります:

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

このエラーは、短剣には前述の依存関係を提供する方法がないことを示しています。何らかの方法でコンポーネントに追加する必要があります。Fragmentなので、_@Module_を使用する必要があります。

AppComponentは、起動時にApplicationによって作成されると想定しています。 AppComponentのライフサイクルは、アクティビティとフラグメントのライフサイクルよりも長いです。したがって、アクティビティまたはフラグメントを提供する方法がわからない場合は合理的です。

  1. DownloadFilePresenterImpDownloadFileViewに依存します。
  2. DownloadFilePresenterImpDownloadFileViewに注入したい
  3. ビューとプレゼンターを挿入するには、アクティビティについては何も知らず、明らかにフラグメントについても何も知らないAppComponentを使用しています。スコープとライフサイクルが異なります。

さらに混乱を引き起こさないために、フラグメントとアクティビティのライフサイクルがあなたが心に留めておくべきものなので、フラグメントについて話します。モジュールではDownloadFileViewを使用できますが、これらの長い名前は混乱を招きます。


モジュールを使用するhaveがあるフラグメントまたはアクティビティをprovideする例えば.

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

このモジュールはフラグメントのライフサイクルとともにのみ有効であるため、haveを使用して、サブコンポーネントまたはAppComponentに依存するコンポーネントを使用します。

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

フラグメントでは、コンポーネントを適切に作成する必要があります...

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

また、私は強く推奨を見て、使用してconstructor injection

_public class DownloadFilePresenterImp implements DownloadFilePresenterContact {

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

別の方法として、provideDownloadfilePresenterImp(View)メソッドをFragmentModuleに移動して、冗長コードが必要な場合に同じ効果を得ることができます。

できた.

27
David Medenjak

私にとっての問題は、別のコンポーネントに、入力パラメーターと同じクラスの未使用の注入メソッドがあることでした。その注入メソッドを削除し、コンパイルしました。

2
Adam Kis

私の場合、Dagger2を使用しながら、Kotlinにプレゼンタークラスがありました。忘れてた @Inject constructorプレゼンターのコンストラクターとして。

2
Sava B.

ドラッガーのこの完全な実装をチェックアウトすることを強くお勧めします: https://github.com/Spentas/securechat/blob/master/app/src/main/Java/com/spentas/javad/securechat/app/ AppComponent.Java

または、以下に従ってください:

AppModuleで:

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

あなたのAppComponent:

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

次に、アプリケーションクラスで短剣を初期化する必要があります。

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

どこでも使用したい場合:

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