Dagger2:如果没有 @Inject 构造函数或 @Provides-annotated 方法,则无法提供

Dagger2 : Cannot be provided without an @Inject constructor or an @Provides-annotated method

我刚刚重构了我的 Dagger 代码以使其具有可扩展性,并将所有核心内容移至一个名为 di 的单独模块。

现在,当我尝试在应用程序模块中注入我的依赖项时,我得到了这个:

[Dagger/MissingBinding] retrofit2.Retrofit cannot be provided without an @Inject constructor or an @Provides-annotated method.
public abstract interface ResetPasswordComponent {
                ^
      retrofit2.Retrofit is injected at
          com.sahra.oms.ibshop.features.resetpassword.di.ResetPasswordNetworkModule.providerResetPasswordAPI(retrofit)
      com.sahra.oms.ibshop.data.remote.service.ResetPasswordService is injected at
          com.sahra.oms.ibshop.data.repisotory.nationalid.UniqueRepositoryImpl(resetPasswordService)
      com.sahra.oms.ibshop.data.repisotory.nationalid.UniqueRepositoryImpl is injected at
          com.sahra.oms.ibshop.features.resetpassword.di.ResetPasswordModule.bindUniqueIdRepository(uniqueRepositoryImpl)
      com.sahra.oms.ibshop.data.repisotory.nationalid.UniqueIdRepository is injected at
          com.sahra.oms.ibshop.features.resetpassword.uniqueid.CheckUniqueIDViewModel(repository)
      com.sahra.oms.ibshop.features.resetpassword.uniqueid.CheckUniqueIDViewModel is injected at
          com.sahra.oms.ibshop.features.resetpassword.di.ResetPasswordModule.bindCheckIdViewModel(checkUniqueIDViewModel)
      java.util.Map<java.lang.Class<? extends androidx.lifecycle.ViewModel>,javax.inject.Provider<androidx.lifecycle.ViewModel>> is injected at
          com.sahra.oms.ibishop.di.util.ViewModelFactory(viewModelsMap)
      com.sahra.oms.ibishop.di.util.ViewModelFactory is injected at
          com.sahra.oms.ibshop.di.ViewModelBuilder.bindViewModelFactory(arg0)
      androidx.lifecycle.ViewModelProvider.Factory is injected at
          com.sahra.oms.ibshop.features.resetpassword.newpassword.NewPasswordFragment.viewModelFactory
      com.sahra.oms.ibshop.features.resetpassword.newpassword.NewPasswordFragment is injected at
          com.sahra.oms.ibshop.features.resetpassword.di.ResetPasswordComponent.inject(com.sahra.oms.ibshop.features.resetpassword.newpassword.NewPasswordFragment)

ResetPasswordService 只是一个 Retrofit 接口。

这是我的代码:

应用组件

@Singleton
@AppScope
@Component
interface AppComponent {


    fun provideContextComponent(): ContextComponent
    fun provideNetworkComponent(): NetworkComponent
    fun provideSharedPrefComponent(): SharedPreferencesComponent

    fun inject(app: Application)

    @Component.Factory
    interface Factory {

        fun create(
            @BindsInstance
            context: ContextComponent,
            @BindsInstance
            network: NetworkComponent,
            @BindsInstance
            sharedPrefs: SharedPreferencesComponent
        ): AppComponent
    }
}

网络组件:

@Scope
@Retention(AnnotationRetention.RUNTIME)
annotation class NetworkScope


@NetworkScope
@Component(
    dependencies = [ContextComponent::class],
    modules = [OkHttpModule::class, AuthBinderModule::class]
)
interface NetworkComponent {

    fun provideOkHttp(): OkHttpClient
    fun provideRetrofit(): Retrofit
    fun provideGson(): GsonConverterFactory

}

OkHttpModule :

@Module
object OkHttpModule {
    private const val BASE_URL = "base_url"

    @Provides
    @JvmStatic
    fun provideLoggingInterceptor(): HttpLoggingInterceptor {
        return HttpLoggingInterceptor(
            HttpLoggingInterceptor.Logger { message -> Log.d("<<<network>>>", message) }).apply {
            level = if (BuildConfig.DEBUG) HttpLoggingInterceptor.Level.BODY else HttpLoggingInterceptor.Level.NONE
        }
    }

    @Provides
    @JvmStatic
    fun provideChuckInterceptor(app: Application): ChuckInterceptor = ChuckInterceptor(app)

    @Provides
    @JvmStatic
    fun provideOkhttpCache(app: Application): Cache =
        Cache(app.cacheDir, 50_000_000)

    @Provides
    @NetworkScope
    @JvmStatic
    fun provideClient(
        loggingInterceptor: HttpLoggingInterceptor,
        chuckInterceptor: ChuckInterceptor,
        authInterceptor: Interceptor,
        cache: Cache
    ): OkHttpClient {
        return OkHttpClient.Builder()
            .cache(cache)
            .addInterceptor(loggingInterceptor)
            .addInterceptor(authInterceptor)
            .addInterceptor(chuckInterceptor)
            .build()
    }

    @Provides
    @NetworkScope
    @JvmStatic
    fun provideGson() = Gson()


    @Provides
    @JvmStatic
    fun provideGsonConverter(gson: Gson) = GsonConverterFactory.create(gson)

    @Provides
    @NetworkScope
    @JvmStatic
    fun provideRetrofit(
        gsonConverterFactory: GsonConverterFactory,
        client: Lazy<OkHttpClient>
    ): Retrofit = Retrofit.Builder()
        .callFactory { request -> client.get().newCall(request) }
        .baseUrl(BASE_URL)
        .addConverterFactory(gsonConverterFactory)
        .build()
}

以下是我尝试注入依赖项的方式:

@FeatureScope
@Component(
    dependencies = [AppComponent::class],
    modules = [
        ResetPasswordNetworkModule::class,
        ResetPasswordModule::class,
        ViewModelBuilder::class
    ]
)
interface ResetPasswordComponent {
    fun inject(newPasswordFragment: NewPasswordFragment)
    fun inject(checkUniqueIDFragment: CheckUniqueIDFragment)

    @Component.Builder
    interface Builder {
        fun coreComponent(appComponent: AppComponent): Builder
        fun build(): ResetPasswordComponent
    }
}
@Module
abstract class ResetPasswordModule {

    @Binds
    abstract fun bindResetPasswordRepository(resetPasswordRepositoryImpl: ResetPasswordRepositoryImpl): ResetPasswordRepository

    @Binds
    abstract fun bindUniqueIdRepository(uniqueRepositoryImpl: UniqueRepositoryImpl): UniqueIdRepository

    @Binds
    @IntoMap
    @ViewModelKey(CheckUniqueIDViewModel::class)
    abstract fun bindCheckIdViewModel(checkUniqueIDViewModel: CheckUniqueIDViewModel): ViewModel


    @Binds
    @IntoMap
    @ViewModelKey(NewPasswordViewModel::class)
    abstract fun bindNewPasswordViewModel(newPasswordViewModel: NewPasswordViewModel): ViewModel
}
@Module
object ResetPasswordNetworkModule {
    @Provides
    @JvmStatic
    @FeatureScope
    fun provideUserAPI(
        retrofit: Retrofit
    ): ResetPasswordService = retrofit.create(ResetPasswordService::class.java)
}

这是我的存储库代码:

class ResetPasswordRepositoryImpl @Inject constructor(
    private val resetPasswordService: ResetPasswordService
) : ResetPasswordRepository {
}

片段:

class NewPasswordFragment{
    @Inject
    lateinit var viewModelFactory: ViewModelProvider.Factory
}
class NewPasswordViewModel @Inject constructor(
    private val repository: ResetPasswordRepository
)

提前致谢。

您的应用程序组件工厂如下所示:

    fun create(
        @BindsInstance
        context: ContextComponent,
        @BindsInstance
        network: NetworkComponent,
        @BindsInstance
        sharedPrefs: SharedPreferencesComponent
    ): AppComponent

这提供了对 NetworkComponent 实例的访问,因此任何需要 NetworkComponent@Provides@Binds@Inject 都可以获得一个。它,但是,可以直接访问NetworkComponent的对象图。

NetworkComponent 已经公开了 Retrofit,因此如果您有权访问该组件,您当然可以获得一个。但是,这个过程不是自动的,在您的设置中需要一个 @Provides 方法。

@Provides
fun provideRetrofit(component: NetworkComponent): Retrofit = component.provideRetrofit()

这比它需要的更复杂。实现此目的的更好方法是使 NetworkComponent 成为 AppComponent 的依赖项(或者仅使用其模块并完全删除网络组件),然后在 AppComponent 中公开 Retrofit

// using multiple scoped dependencies requires Dagger 2.27
@AppScope
@Component(dependencies = [ContextComponent::class, NetworkComponent::class, SharedPreferencesComponent::class])
interface AppComponent {
    fun provideRetrofit(): Retrofit
    // ...
}