为了学习,我想在一个简单的项目中实现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)
})
}
}
}
使用限定符时,需要将限定符注释放在两个位置:
@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()
}
尽管@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)
}
}
}