Android Dagger 2 注入拦截器
Android Dagger 2 Injecting in Interceptor
我刚刚在拦截器中注入存储库以在需要或过期时获取访问令牌时遇到问题。我只是不明白我哪里做错了。我只是没有找到任何如何处理拦截器和存储库的示例。想想那个双“Retrofit.Builder”也许这是个问题。你怎么看?让我们来谈谈代码:
@Module
class AppModule {
@Singleton
@Provides
fun provideRefreshTokenService(client: OkHttpClient): RefreshTokenApi {
return Retrofit.Builder()
.baseUrl("https://id.twitch.tv/oauth2/")
.client(client)
.addConverterFactory(GsonConverterFactory.create())
.build()
.create(RefreshTokenApi::class.java)
}
@Singleton
@Provides
fun provideHttpClient(headerInterceptor: HeaderInterceptor): OkHttpClient {
return OkHttpClient.Builder()
.addNetworkInterceptor(headerInterceptor)
.build()
}
@Singleton
@Provides
fun provideRetrofit(client: OkHttpClient): TwichApi {
return Retrofit.Builder()
.baseUrl("https://api.igdb.com/v4/")
.client(client)
.addConverterFactory(GsonConverterFactory.create())
.build()
.create(TwichApi::class.java)
}
}
class TwichHeaderRepository @Inject constructor(
private val refreshTokenApi: RefreshTokenApi)
{
suspend fun fetchRefreshToken(): Response<RefreshToken> {
return withContext(Dispatchers.IO) {
refreshTokenApi.getRefreshToken()
}
}
}
private const val TAG = "AddRepositoryAction"
private const val HEADER_CLIENT_ID = "Client-ID"
private const val HEADER_AUTHORIZATION = "Authorization"
private const val HEADER_ACCEPT = "Accept"
private const val DEFAULT_ACCESS_TOKEN = "mjycvndz4sasons2mg990kqme6vu6d"
private const val UNAUTHORIZED_STATUS_CODE = 401
@Singleton
class HeaderInterceptor @Inject constructor(
private val context: Context,
private val twichHeaderRepository: TwichHeaderRepository
) : Interceptor {
override fun intercept(chain: Interceptor.Chain): Response {
val response = chain.proceed(chain.request().newBuilder().apply {
header(HEADER_CLIENT_ID, "i3nzc6w3n0pod39zgsq8h445o2yp6p")
header(HEADER_AUTHORIZATION, "Bearer $DEFAULT_ACCESS_TOKEN")
header(HEADER_ACCEPT, "application/json")
}.build())
val refreshToken = runBlocking { generateAccessToken(twichHeaderRepository) }
if (response.code == UNAUTHORIZED_STATUS_CODE) {
response.close()
val accessToken = ""
return chain.proceed(chain.request().newBuilder().apply {
header(HEADER_CLIENT_ID, "i3nzc6w3n0pod39zgsq8h445o2yp6p")
header(HEADER_AUTHORIZATION, "Bearer $accessToken")
header(HEADER_ACCEPT, "application/json")
}.build())
}
return response
}
}
private suspend fun generateAccessToken(twichHeaderRepository: TwichHeaderRepository): String
{
val responseRefreshToken = twichHeaderRepository.fetchRefreshToken()
return responseRefreshToken.body().toString()
}
interface RefreshTokenApi {
@POST(
...
)
suspend fun getRefreshToken(
): Response<RefreshToken>
}
@Singleton
@Component(modules = [AppModule::class])
interface AppComponent {
@Component.Factory
interface Factory {
fun create(@BindsInstance context: Context): AppComponent
}
fun inject(activity: MainActivity)
fun inject(fragment: AddFragment)
fun inject(interceptor: HeaderInterceptor)
}
class TwichRepository @Inject constructor(
private val twichApi: TwichApi
) {
suspend fun searchGames(title: String): Response<Game> {
return withContext(Dispatchers.IO) { twichApi.getGamesBySearch(title) }
}
}
错误信息是
Found a dependency cycle okhttp3.OkHttpClient is injected at
com.example.glc.AppModule.provideRefreshTokenService(client)
com.example.glc.add.RefreshTokenApi is injected at
com.example.glc.add.TwichHeaderRepository(refreshTokenApi)
com.example.glc.add.TwichHeaderRepository is injected at
com.example.glc.add.HeaderInterceptor(�, twichHeaderRepository)
com.example.glc.add.HeaderInterceptor is injected at
com.example.glc.AppModule.provideHttpClient(headerInterceptor)
okhttp3.OkHttpClient is injected at
com.example.glc.AppModule.provideRetrofit(client)
com.example.glc.add.TwichApi is injected at
com.example.glc.add.TwichRepository(twichApi)
com.example.glc.add.TwichRepository is injected at
com.example.glc.add.AddViewModel(twichRepository)
com.example.glc.add.AddViewModel is injected at
com.example.glc.add.AddFragment.addViewModel
com.example.glc.add.AddFragment is injected at
com.example.glc.di.AppComponent.inject(com.example.glc.add.AddFragment)
您在 HeaderInterceptor
的构造函数中对 TwichHeaderRepository
依赖使用 Lazy
来打破依赖循环。
class HeaderInterceptor @Inject constructor(
private val lazyTwichHeaderRepository: Lazy<TwichHeaderRepository>
) {
// Access the dependency lazily with lazyTwichHeaderRepository.get()
}
我只需要添加 @Named
来解决我的问题并理解它,就像马克在评论中所说的那样。
@Module
class 应用模块 {
@Singleton
@Provides
@Named("oauth2")
fun provideAccessTokenHttpClient() = OkHttpClient.Builder().build()
@Singleton
@Provides
fun provideRefreshTokenService(@Named("oauth2") client: OkHttpClient): RefreshTokenApi {
return Retrofit.Builder()
.baseUrl("https://id.twitch.tv/oauth2/")
.client(client)
.addConverterFactory(GsonConverterFactory.create())
.build()
.create(RefreshTokenApi::class.java)
}
@Singleton
@Provides
@Named("igdb")
fun provideSearchDataHttpClient(headerInterceptor: HeaderInterceptor): OkHttpClient {
return OkHttpClient.Builder()
.addInterceptor(headerInterceptor)
.build()
}
@Singleton
@Provides
fun provideRetrofit(@Named("igdb") client: OkHttpClient): TwichApi {
return Retrofit.Builder()
.baseUrl("https://api.igdb.com/v4/")
.client(client)
.addConverterFactory(GsonConverterFactory.create())
.build()
.create(TwichApi::class.java)
}
}
我刚刚在拦截器中注入存储库以在需要或过期时获取访问令牌时遇到问题。我只是不明白我哪里做错了。我只是没有找到任何如何处理拦截器和存储库的示例。想想那个双“Retrofit.Builder”也许这是个问题。你怎么看?让我们来谈谈代码:
@Module
class AppModule {
@Singleton
@Provides
fun provideRefreshTokenService(client: OkHttpClient): RefreshTokenApi {
return Retrofit.Builder()
.baseUrl("https://id.twitch.tv/oauth2/")
.client(client)
.addConverterFactory(GsonConverterFactory.create())
.build()
.create(RefreshTokenApi::class.java)
}
@Singleton
@Provides
fun provideHttpClient(headerInterceptor: HeaderInterceptor): OkHttpClient {
return OkHttpClient.Builder()
.addNetworkInterceptor(headerInterceptor)
.build()
}
@Singleton
@Provides
fun provideRetrofit(client: OkHttpClient): TwichApi {
return Retrofit.Builder()
.baseUrl("https://api.igdb.com/v4/")
.client(client)
.addConverterFactory(GsonConverterFactory.create())
.build()
.create(TwichApi::class.java)
}
}
class TwichHeaderRepository @Inject constructor(
private val refreshTokenApi: RefreshTokenApi)
{
suspend fun fetchRefreshToken(): Response<RefreshToken> {
return withContext(Dispatchers.IO) {
refreshTokenApi.getRefreshToken()
}
}
}
private const val TAG = "AddRepositoryAction"
private const val HEADER_CLIENT_ID = "Client-ID"
private const val HEADER_AUTHORIZATION = "Authorization"
private const val HEADER_ACCEPT = "Accept"
private const val DEFAULT_ACCESS_TOKEN = "mjycvndz4sasons2mg990kqme6vu6d"
private const val UNAUTHORIZED_STATUS_CODE = 401
@Singleton
class HeaderInterceptor @Inject constructor(
private val context: Context,
private val twichHeaderRepository: TwichHeaderRepository
) : Interceptor {
override fun intercept(chain: Interceptor.Chain): Response {
val response = chain.proceed(chain.request().newBuilder().apply {
header(HEADER_CLIENT_ID, "i3nzc6w3n0pod39zgsq8h445o2yp6p")
header(HEADER_AUTHORIZATION, "Bearer $DEFAULT_ACCESS_TOKEN")
header(HEADER_ACCEPT, "application/json")
}.build())
val refreshToken = runBlocking { generateAccessToken(twichHeaderRepository) }
if (response.code == UNAUTHORIZED_STATUS_CODE) {
response.close()
val accessToken = ""
return chain.proceed(chain.request().newBuilder().apply {
header(HEADER_CLIENT_ID, "i3nzc6w3n0pod39zgsq8h445o2yp6p")
header(HEADER_AUTHORIZATION, "Bearer $accessToken")
header(HEADER_ACCEPT, "application/json")
}.build())
}
return response
}
}
private suspend fun generateAccessToken(twichHeaderRepository: TwichHeaderRepository): String
{
val responseRefreshToken = twichHeaderRepository.fetchRefreshToken()
return responseRefreshToken.body().toString()
}
interface RefreshTokenApi {
@POST(
...
)
suspend fun getRefreshToken(
): Response<RefreshToken>
}
@Singleton
@Component(modules = [AppModule::class])
interface AppComponent {
@Component.Factory
interface Factory {
fun create(@BindsInstance context: Context): AppComponent
}
fun inject(activity: MainActivity)
fun inject(fragment: AddFragment)
fun inject(interceptor: HeaderInterceptor)
}
class TwichRepository @Inject constructor(
private val twichApi: TwichApi
) {
suspend fun searchGames(title: String): Response<Game> {
return withContext(Dispatchers.IO) { twichApi.getGamesBySearch(title) }
}
}
错误信息是
Found a dependency cycle okhttp3.OkHttpClient is injected at
com.example.glc.AppModule.provideRefreshTokenService(client)
com.example.glc.add.RefreshTokenApi is injected at
com.example.glc.add.TwichHeaderRepository(refreshTokenApi)
com.example.glc.add.TwichHeaderRepository is injected at
com.example.glc.add.HeaderInterceptor(�, twichHeaderRepository)
com.example.glc.add.HeaderInterceptor is injected at
com.example.glc.AppModule.provideHttpClient(headerInterceptor)
okhttp3.OkHttpClient is injected at
com.example.glc.AppModule.provideRetrofit(client)
com.example.glc.add.TwichApi is injected at
com.example.glc.add.TwichRepository(twichApi)
com.example.glc.add.TwichRepository is injected at
com.example.glc.add.AddViewModel(twichRepository)
com.example.glc.add.AddViewModel is injected at
com.example.glc.add.AddFragment.addViewModel
com.example.glc.add.AddFragment is injected at
com.example.glc.di.AppComponent.inject(com.example.glc.add.AddFragment)
您在 HeaderInterceptor
的构造函数中对 TwichHeaderRepository
依赖使用 Lazy
来打破依赖循环。
class HeaderInterceptor @Inject constructor(
private val lazyTwichHeaderRepository: Lazy<TwichHeaderRepository>
) {
// Access the dependency lazily with lazyTwichHeaderRepository.get()
}
我只需要添加 @Named
来解决我的问题并理解它,就像马克在评论中所说的那样。
@Module
class 应用模块 {
@Singleton
@Provides
@Named("oauth2")
fun provideAccessTokenHttpClient() = OkHttpClient.Builder().build()
@Singleton
@Provides
fun provideRefreshTokenService(@Named("oauth2") client: OkHttpClient): RefreshTokenApi {
return Retrofit.Builder()
.baseUrl("https://id.twitch.tv/oauth2/")
.client(client)
.addConverterFactory(GsonConverterFactory.create())
.build()
.create(RefreshTokenApi::class.java)
}
@Singleton
@Provides
@Named("igdb")
fun provideSearchDataHttpClient(headerInterceptor: HeaderInterceptor): OkHttpClient {
return OkHttpClient.Builder()
.addInterceptor(headerInterceptor)
.build()
}
@Singleton
@Provides
fun provideRetrofit(@Named("igdb") client: OkHttpClient): TwichApi {
return Retrofit.Builder()
.baseUrl("https://api.igdb.com/v4/")
.client(client)
.addConverterFactory(GsonConverterFactory.create())
.build()
.create(TwichApi::class.java)
}
}