Не может быть предоставлен без конструктора @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);