Не может быть предоставлен без конструктора @Inject или из метода @Provides-annotated

Я использую 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)

Большое спасибо за любые предложения,

Ответ 1

Хорошо... это должно работать

@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 для представления.

ИЗМЕНИТЬ

Если вы хотите добавить объект, который вы создаете во время выполнения, единственный способ перейти - передать этот объект 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);
    }

}

Ответ 2

Ошибка только указывает, что кинжал не имеет способа обеспечить указанную зависимость. Вам нужно будет каким-то образом добавить его к своему компоненту, и поскольку это Fragment — вам нужно будет использовать @Module.

Я предполагаю, что ваш AppComponent создается вашим Application при запуске. Ваш AppComponent имеет жизненный цикл, который длиннее, чем ваши действия и жизненные циклы фрагментов. Поэтому разумно, что он не знает, как обеспечить деятельность или фрагмент в вашем случае.

  • Ваш DownloadFilePresenterImp зависит от вашего DownloadFileView.
  • Вы хотите ввести DownloadFilePresenterImp в свой DownloadFileView
  • Чтобы ввести представление и презентатор, вы используете свой AppComponent, который ничего не знает об активности, и, очевидно, ничего о фрагменте. Он имеет различную область действия и жизненный цикл.

Чтобы не приводить к путанице, я буду говорить о фрагментах, так как их жизненный цикл и действия - это то, что вы должны иметь в виду. Вы можете просто использовать DownloadFileView с модулем, но эти длинные имена будут запутываться.


Чтобы предоставить фрагмент или действие, вы должны использовать модуль. например.

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

Поскольку этот модуль должен жить вместе с жизненным циклом фрагментов, вам нужно использовать либо подкомпоненты, либо компоненты с зависимостью от вашего 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;
}

Кроме того, я рекомендую посмотреть и использовать конструктор

public class DownloadFilePresenterImp implements DownloadFilePresenterContact {

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

В качестве альтернативы вы можете переместить метод provideDownloadfilePresenterImp(View) в FragmentModule, чтобы получить тот же эффект, если вам нужен резервный код.

Готово.

Ответ 3

Я очень рекомендую вам проверить эту полную реализацию dragger: 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:

@Singletone
@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);