【问题标题】:cannot be provided without an @Inject constructor or from an @Provides-annotated method如果没有 @Inject 构造函数或 @Provides-annotated 方法,则无法提供
【发布时间】:2016-05-25 10:39:45
【问题描述】:

我正在使用 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;
    }
}

我正在尝试注入我的 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;
}

还有我的 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)

非常感谢您的任何建议,

【问题讨论】:

    标签: android dagger-2


    【解决方案1】:

    该错误只是表明 dagger 无法提供所述依赖项。你必须以某种方式将它添加到你的组件中——因为它是Fragment——你必须使用@Module

    我假设您的AppComponent 是由您的Application 在开始时创建的。您的 AppComponent 的生命周期比您的活动和片段生命周期都长。因此,在您的情况下,它不知道如何提供活动或片段是合理的。

    1. 您的DownloadFilePresenterImp 取决于您的DownloadFileView
    2. 您想将DownloadFilePresenterImp 注入您的DownloadFileView
    3. 要注入视图和演示者,您使用的是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 以获得相同的效果。

    完成。

    【讨论】:

      【解决方案2】:

      好的...这应该可以工作

      @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) 构造函数,并将其替换为创建一个对视图的弱引用的设置器。

      编辑

      如果你想注入一个在运行时构建的对象,唯一的办法就是将该对象传递给 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);
          }
      }
      

      下载FilePresenterImp.java

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

      下载FileView.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);
          }
      
      }
      

      【讨论】:

      • 我已经尝试过该解决方案。但是,我对如何在原始问题中使用 DI 很感兴趣。我想用我的片段 DownloadFileView 的参数注入 DownloadFilePresenterImp
      • @ant2009 是你现在想要的吗?
      【解决方案3】:

      在我的例子中,我在 Kotlin 中有一个演示者课程,同时使用 Dagger2。我忘了 @Inject constructor 作为presenter的构造函数。

      【讨论】:

      • 这里也一样。这有帮助。
      • 天哪。谢谢 !!即使 ViewModel 为空且没有依赖关系,我们也必须使用@Inject constructor!!
      【解决方案4】:

      对我来说,问题是另一个组件有一个未使用的注入方法,其类与输入参数相同。删除了该注入方法并编译了它。

      【讨论】:

        【解决方案5】:

        我强烈建议您查看这个完整的拖动器实现: 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);
        }
        

        你的应用组件:

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

        【讨论】:

        • 我试过了,但没有为我工作。当我将 DownloadFileView 接口用作 void inject(DownloadFileView target) 时,如果没有 Inject 构造函数或来自 Provider 注释,则无法提供错误。当我传递实现该视图接口的活动时,如 void injection(DownloadFileViewActivity target) 它编译但通过空异常的依赖。
        【解决方案6】:

        匕首错误不是很有用

        就我而言,我在我的主要 :app 模块和其他模块之间使用了不匹配的版本。确保您使用的是正确的版本,并且它们在模块之间是相同的。

        作为永久修复,在我的 android 级别 build.gradle 中,我有以下内容:

        buildscript {
            ext.dagger_version = "2.30.1"
        }
        

        而且我所有的模块build.gradle 文件都引用了相同的版本:

            // Dependency Injection
            implementation "com.google.dagger:dagger:$dagger_version"
            kapt "com.google.dagger:dagger-compiler:$dagger_version"
        

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2019-01-11
          相关资源
          最近更新 更多