如何解决错误:使用Dagger2进行改造时会出现[Dagger / MissingBinding]

问题描述 投票:1回答:2

为了学习,我想在一个简单的项目中实现Dagger2的依赖注入。我通读了Google Codelab代码示例,以了解Dagger2的基本概念。然后,我通读了一些中等博客和示例Github回购,该回购已实现了Dagger2用于项目中的依赖注入。然后,我开始了一个演示项目,并尝试通过Retrofit实现Dagger2。实施后,我收到一个意外的构建失败错误,错误为“ [Dagger / MissingBinding] com.aomi.mybase.data.remote.testimonial.TestimonialRestService,如果没有@Provides批注的方法就无法提供。” TestimonialService是与Api相关的服务。因此,我无法使用@Provide或@Binds注释进行注释。我真的不知道该怎么解决]

错误日志为以下截图https://imgur.com/a/0qQXLbN

让我分享一些代码,以便您查看问题的实际出处

Qualifier.kt

@Qualifier
@MustBeDocumented
@Retention(RUNTIME)
annotation class Type(val type: String = "")

InterceptorModule.kt

@Module
class InterceptorModule {

    @Module
    companion object {

        @JvmStatic
        @Singleton
        @Provides
        fun provideLoggingInterceptor(): HttpLoggingInterceptor {
            return HttpLoggingInterceptor().apply {
                level = if (BuildConfig.DEBUG) BODY else NONE
            }
        }

        @JvmStatic
        @Singleton
        @Type("Basic")
        @Provides
        fun provideBasicInterceptor(): Interceptor {
            val basicAuthCredential = Credentials.basic(
                "username",
                "password"
            )

            try {
                return Interceptor {
                    val request = it.request()

                    it.proceed(
                        request.newBuilder()
                            .header("Accept", "application/json")
                            .header("Content-Type", "application/json")
                            .header("Authorization", basicAuthCredential)
                            .build()
                    )
                }
            } catch (exception: Exception) {
                throw Exception(exception.message)
            }
        }

        @JvmStatic
        @Singleton
        @Type("Bearer")
        @Provides
        fun provideAuthInterceptor(appContext: Context): Interceptor {
            val accessToken = AppPreferenceImpl(appContext).accessToken

            try {
                return Interceptor {
                    val request = it.request()

                    it.proceed(
                        request.newBuilder()
                            .header("Accept", "application/json")
                            .header("Content-Type", "application/json")
                            .header("Authorization", "Bearer $accessToken")
                            .build()
                    )
                }
            } catch (exception: Exception) {
                throw Exception(exception.message)
            }
        }

    }
}

NetworkModule.kt

@Module(
    includes = [
        InterceptorModule::class
    ]
)
abstract class NetworkModule {

    @Module
    companion object {

        private const val BASE_URL = BuildConfig.BASE_URL
        private const val TIME_OUT = 60L

        @JvmStatic
        @Singleton
        @Type("Basic")
        @Provides
        fun provideBasicRetrofit(okHttpClient: OkHttpClient): Retrofit {
            return Retrofit.Builder()
                .baseUrl(BASE_URL)
                .addConverterFactory(GsonConverterFactory.create())
                .addCallAdapterFactory(RxJava2CallAdapterFactory.create())
                .client(okHttpClient)
                .callbackExecutor { Logger.d("returning") }
                .build()
        }

        @JvmStatic
        @Singleton
        @Type("Basic")
        @Provides
        fun provideBasicOkHttpClient(loggingInterceptor: HttpLoggingInterceptor, basicInterceptor: Interceptor): OkHttpClient {
            return OkHttpClient.Builder()
                .connectTimeout(TIME_OUT, SECONDS)
                .readTimeout(TIME_OUT, SECONDS)
                .writeTimeout(TIME_OUT, SECONDS)
                .addInterceptor(loggingInterceptor)
                .addInterceptor(basicInterceptor)
                .build()
        }

        @JvmStatic
        @Singleton
        @Type("Bearer")
        @Provides
        fun provideBearerRetrofit(okHttpClient: OkHttpClient): Retrofit {
            return Retrofit.Builder()
                .baseUrl(BASE_URL)
                .addConverterFactory(GsonConverterFactory.create())
                .addCallAdapterFactory(RxJava2CallAdapterFactory.create())
                .client(okHttpClient)
                .callbackExecutor { Logger.d("returning") }
                .build()
        }

        @JvmStatic
        @Singleton
        @Type("Bearer")
        @Provides
        fun provideBearerOkHttpClient(loggingInterceptor: HttpLoggingInterceptor, authInterceptor: Interceptor): OkHttpClient {
            return OkHttpClient.Builder()
                .connectTimeout(TIME_OUT, SECONDS)
                .readTimeout(TIME_OUT, SECONDS)
                .writeTimeout(TIME_OUT, SECONDS)
                .addInterceptor(loggingInterceptor)
                .addInterceptor(authInterceptor)
//                .authenticator(ServiceAuthenticator())
                .build()
        }

    }
}

ServiceModule.kt

@Module
abstract class ServiceModule {

    @Module
    companion object {

        @JvmStatic
        @Singleton
        @Type("Basic")
        @Provides
        fun provideTestimonialService(retrofit: Retrofit): TestimonialRestService {
            return retrofit.create(TestimonialRestService::class.java)
        }

    }
}

RepositoryModule.kt

@Module
abstract class RepositoryModule {

    @Binds
    abstract fun provideTestimonialRepository(repo: TestimonialRepositoryImpl): TestimonialRepository

}

TestimonialRepository.kt

interface TestimonialRepository {
    fun getTestimonials(): Flowable<ArrayList<Testimonial>>
}

TestimonialRepositoryImpl.kt

class TestimonialRepositoryImpl @Inject constructor(
    private val testimonialDataSource: TestimonialDataSource
) : TestimonialRepository {

    override fun getTestimonials(): Flowable<ArrayList<Testimonial>> {
        return testimonialDataSource.getTestimonialResponse().map { it.testimonialList }
    }

}

TestimonialDataSource.kt

class TestimonialDataSource @Inject constructor(
    private val testimonialRestService: TestimonialRestService
) {

    fun getTestimonialResponse(): Flowable<TestimonialResponse> {
        return testimonialRestService.getTestimonialResponse().onResponse()
    }

}

TestimonialRestService.kt

interface TestimonialRestService {

    @GET("static/testimonials")
    fun getTestimonialResponse(): Flowable<Response<TestimonialResponse>>

}

WelcomeViewModel.kt

class WelcomeViewModel @Inject constructor(
    private val repository: TestimonialRepository
) : BaseViewModel() {

    var testimonials = MutableLiveData<ArrayList<Testimonial>>()

    fun getTestimonials() {
        if(testimonials.value == null) {
            compositeDisposable += repository.getTestimonials()
                .performOnBackgroundOutputOnMain()
                .doOnSubscribe { loader.value = true }
                .doAfterTerminate { loader.value = false }
                .subscribe({
                    Logger.d(it)
                    testimonials.value = it
                }, {
                    handleException(it)
                })
        }
    }

}
dependency-injection retrofit dagger-2
2个回答
2
投票

使用限定符时,需要将限定符注释放在两个位置:

  • 绑定对象的位置:@Binds@Provides方法,构建器中的@BindsInstance方法或工厂中的@BindsInstance参数。
  • 使用对象的地方:@Binds@Provides方法的参数@Inject构造函数或方法的参数,或@Inject字段/属性。

仅当使用站点上的限定符与@Binds / @Provides方法上的限定符匹配时,才能提供依赖项。为此,“无限定词”是一种限定词。

由于要使用@Type("Basic")限定的Retrofit来提供不合格的TestimonialRestService,因此这意味着参数应限定,方法本身也应不限定:

    @JvmStatic
    @Singleton
    @Provides
    fun provideTestimonialService(@Type("Basic") retrofit: Retrofit): TestimonialRestService {
        return retrofit.create(TestimonialRestService::class.java)
    }

[解决此问题时,您会注意到provideXxxRetrofit方法也由于相同的原因而出错:它们都在寻找不合格的OkHttpClient,但是图形中唯一的OkHttpClient绑定具有限定符。这些错误可以通过相同的方式修复:

    @JvmStatic
    @Singleton
    @Type("Basic")
    @Provides
    fun provideBasicRetrofit(@Type("Basic") okHttpClient: OkHttpClient): Retrofit {
        return Retrofit.Builder()
            .baseUrl(BASE_URL)
            .addConverterFactory(GsonConverterFactory.create())
            .addCallAdapterFactory(RxJava2CallAdapterFactory.create())
            .client(okHttpClient)
            .callbackExecutor { Logger.d("returning") }
            .build()
    }

0
投票

尽管@Nitrodon的答案是正确的,但我根据他的回答将其变得更现实,因此任何陷入此问题的人都可以很容易地理解它。这是用于更好理解的代码

Constant.kt

object Network {
    const val BASIC = "Basic"
    const val BEARER = "Bearer"
} 

Qualifier.kt

@Qualifier
@MustBeDocumented
@Retention(RUNTIME)
annotation class InterceptorType(val type: String = "")

@Qualifier
@MustBeDocumented
@Retention(RUNTIME)
annotation class OkHttpClientType(val type: String = "")

@Qualifier
@MustBeDocumented
@Retention(RUNTIME)
annotation class RetrofitType(val type: String = "")

InterceptorModule.kt

@Module
class InterceptorModule {

    companion object {

        @Singleton
        @Provides
        fun provideLoggingInterceptor(): HttpLoggingInterceptor {
            return HttpLoggingInterceptor().apply {
                level = if (BuildConfig.DEBUG) BODY else NONE
            }
        }

        @Singleton
        @InterceptorType(BASIC)
        @Provides
        fun provideBasicInterceptor(): Interceptor {
            val basicAuthCredential = Credentials.basic(
                "ct_android",
                "\$2y\$12\$ej.DK5rJIZjF9FokTWErDeDylA7N.4apw0FZ2FllcK53KEYZqDryO"
            )

            try {
                return Interceptor {
                    val request = it.request()

                    it.proceed(
                        request.newBuilder()
                            .header("Accept", "application/json")
                            .header("Content-Type", "application/json")
                            .header("Authorization", basicAuthCredential)
                            .build()
                    )
                }
            } catch (exception: Exception) {
                throw Exception(exception.message)
            }
        }

        @Singleton
        @InterceptorType(BEARER)
        @Provides
        fun provideAuthInterceptor(appContext: Context): Interceptor {
            val accessToken = AppPreferenceImpl(appContext).accessToken

            try {
                return Interceptor {
                    val request = it.request()

                    it.proceed(
                        request.newBuilder()
                            .header("Accept", "application/json")
                            .header("Content-Type", "application/json")
                            .header("Authorization", "Bearer $accessToken")
                            .build()
                    )
                }
            } catch (exception: Exception) {
                throw Exception(exception.message)
            }
        }

    }
}

NetworkModule.kt

@Module(
    includes = [
        InterceptorModule::class
    ]
)
abstract class NetworkModule {

    companion object {
        private const val BASE_URL = BuildConfig.BASE_URL
        private const val TIME_OUT = 60L

        @Singleton
        @RetrofitType(BASIC)
        @Provides
        fun provideBasicRetrofit(@OkHttpClientType(BASIC) okHttpClient: OkHttpClient): Retrofit {
            return Retrofit.Builder()
                .baseUrl(BASE_URL)
                .addConverterFactory(GsonConverterFactory.create())
                .addCallAdapterFactory(RxJava2CallAdapterFactory.create())
                .client(okHttpClient)
                .callbackExecutor { Logger.d("returning") }
                .build()
        }

        @Singleton
        @OkHttpClientType(BASIC)
        @Provides
        fun provideBasicOkHttpClient(loggingInterceptor: HttpLoggingInterceptor, @InterceptorType(BASIC) basicInterceptor: Interceptor): OkHttpClient {
            return OkHttpClient.Builder()
                .connectTimeout(TIME_OUT, SECONDS)
                .readTimeout(TIME_OUT, SECONDS)
                .writeTimeout(TIME_OUT, SECONDS)
                .addInterceptor(loggingInterceptor)
                .addInterceptor(basicInterceptor)
                .build()
        }

        @Singleton
        @RetrofitType(BEARER)
        @Provides
        fun provideBearerRetrofit(@OkHttpClientType(BEARER) okHttpClient: OkHttpClient): Retrofit {
            return Retrofit.Builder()
                .baseUrl(BASE_URL)
                .addConverterFactory(GsonConverterFactory.create())
                .addCallAdapterFactory(RxJava2CallAdapterFactory.create())
                .client(okHttpClient)
                .callbackExecutor { Logger.d("returning") }
                .build()
        }

        @Singleton
        @OkHttpClientType(BEARER)
        @Provides
        fun provideBearerOkHttpClient(loggingInterceptor: HttpLoggingInterceptor, @InterceptorType(BEARER) authInterceptor: Interceptor): OkHttpClient {
            return OkHttpClient.Builder()
                .connectTimeout(TIME_OUT, SECONDS)
                .readTimeout(TIME_OUT, SECONDS)
                .writeTimeout(TIME_OUT, SECONDS)
                .addInterceptor(loggingInterceptor)
                .addInterceptor(authInterceptor)
//                .authenticator(ServiceAuthenticator())
                .build()
        }

    }
}

ServiceModule.kt

@Module
abstract class ServiceModule {

    companion object {

        @Singleton
        @Provides
        fun provideTestimonialService(@RetrofitType(BASIC) retrofit: Retrofit): TestimonialRestService {
            return retrofit.create(TestimonialRestService::class.java)
        }

    }
}
© www.soinside.com 2019 - 2024. All rights reserved.