问题描述
嘿,我正在使用 Dagger2
、Retrofit
和 OkHttp
,但我正面临依赖循环问题.
Hey there I am using Dagger2
, Retrofit
and OkHttp
and I am facing dependency cycle issue.
当提供OkHttp
时:
@Provides
@ApplicationScope
OkHttpClient provideOkHttpClient(TokenAuthenticator auth,Dispatcher dispatcher){
return new OkHttpClient.Builder()
.connectTimeout(Constants.CONNECT_TIMEOUT, TimeUnit.SECONDS)
.readTimeout(Constants.READ_TIMEOUT,TimeUnit.SECONDS)
.writeTimeout(Constants.WRITE_TIMEOUT,TimeUnit.SECONDS)
.authenticator(auth)
.dispatcher(dispatcher)
.build();
}
当提供Retrofit
时:
@Provides
@ApplicationScope
Retrofit provideRetrofit(Resources resources,Gson gson, OkHttpClient okHttpClient){
return new Retrofit.Builder()
.baseUrl(resources.getString(R.string.base_api_url))
.addConverterFactory(GsonConverterFactory.create(gson))
.addCallAdapterFactory(RxJava2CallAdapterFactory.create())
.client(okHttpClient)
.build();
}
提供APIService
时:
@Provides
@ApplicationScope
APIService provideAPI(Retrofit retrofit) {
return retrofit.create(APIService.class);
}
我的APIService
接口:
public interface APIService {
@FormUrlEncoded
@POST("token")
Observable<Response<UserTokenResponse>> refreshUserToken();
--- other methods like login, register ---
}
我的 TokenAuthenticator
类:
@Inject
public TokenAuthenticator(APIService mApi,@NonNull ImmediateSchedulerProvider mSchedulerProvider) {
this.mApi= mApi;
this.mSchedulerProvider=mSchedulerProvider;
mDisposables=new CompositeDisposable();
}
@Override
public Request authenticate(Route route, Response response) throws IOException {
request = null;
mApi.refreshUserToken(...)
.subscribeOn(mSchedulerProvider.io())
.observeOn(mSchedulerProvider.ui())
.doOnSubscribe(d -> mDisposables.add(d))
.subscribe(tokenResponse -> {
if(tokenResponse.isSuccessful()) {
saveUserToken(tokenResponse.body());
request = response.request().newBuilder()
.header("Authorization", getUserAccessToken())
.build();
} else {
logoutUser();
}
},error -> {
},() -> {});
mDisposables.clear();
stop();
return request;
}
我的日志:
Error:(55, 16) error: Found a dependency cycle:
com.yasinkacmaz.myapp.service.APIService is injected at com.yasinkacmaz.myapp.darkvane.modules.NetworkModule.provideTokenAuthenticator(…, mApi, …)
com.yasinkacmaz.myapp.service.token.TokenAuthenticator is injected at
com.yasinkacmaz.myapp.darkvane.modules.NetworkModule.provideOkHttpClient(…, tokenAuthenticator, …)
okhttp3.OkHttpClient is injected at
com.yasinkacmaz.myapp.darkvane.modules.NetworkModule.provideRetrofit(…, okHttpClient)
retrofit2.Retrofit is injected at
com.yasinkacmaz.myapp.darkvane.modules.NetworkModule.provideAPI(retrofit)
com.yasinkacmaz.myapp.service.APIService is provided at
com.yasinkacmaz.myapp.darkvane.components.ApplicationComponent.exposeAPI()
所以我的问题:我的 TokenAuthenticator
类依赖于 APIService
但我需要在创建 APIService
时提供 TokenAuthenticator
>.这会导致依赖循环错误.我该如何解决这个问题,有人面临这个问题吗?提前致谢.
So my question: My TokenAuthenticator
class is depends on APIService
but I need to provide TokenAuthenticator
when creating APIService
. This causes dependency cycle error. How do I beat this , is there anyone facing this issue ?Thanks in advance.
推荐答案
您的问题是:
- 您的 OKHttpClient 取决于您的身份验证器
- 您的身份验证器依赖于改造服务
- Retrofit 依赖于 OKHttpClient(如第 1 点)
因此是循环依赖.
这里的一种可能解决方案是让您的 TokenAuthenticator
依赖于 APIServiceHolder
而不是 APIService
.然后您的 TokenAuthenticator
可以在配置 OKHttpClient
时作为依赖项提供,无论 APIService
(对象图更下方)是否已实例化.
One possible solution here is for your TokenAuthenticator
to depend on an APIServiceHolder
rather than a APIService
. Then your TokenAuthenticator
can be provided as a dependency when configuring OKHttpClient
regardless of whether the APIService
(further down the object graph) has been instantiated or not.
一个非常简单的 APIServiceHolder:
A very simple APIServiceHolder:
public class APIServiceHolder {
private APIService apiService;
@Nullable
APIService apiService() {
return apiService;
}
void setAPIService(APIService apiService) {
this.apiService = apiService;
}
}
然后重构你的 TokenAuthenticator:
Then refactor your TokenAuthenticator:
@Inject
public TokenAuthenticator(@NonNull APIServiceHolder apiServiceHolder, @NonNull ImmediateSchedulerProvider schedulerProvider) {
this.apiServiceHolder = apiServiceHolder;
this.schedulerProvider = schedulerProvider;
this.disposables = new CompositeDisposable();
}
@Override
public Request authenticate(Route route, Response response) throws IOException {
if (apiServiceHolder.get() == null) {
//we cannot answer the challenge as no token service is available
return null //as per contract of Retrofit Authenticator interface for when unable to contest a challenge
}
request = null;
TokenResponse tokenResponse = apiServiceHolder.get().blockingGet()
if (tokenResponse.isSuccessful()) {
saveUserToken(tokenResponse.body());
request = response.request().newBuilder()
.header("Authorization", getUserAccessToken())
.build();
} else {
logoutUser();
}
return request;
}
请注意,检索令牌的代码应同步.这是Authenticator
合同的一部分.Authenticator
中的代码将关闭主线程.
Note that the code to retrieve the token should be synchronous. This is part of the contract of Authenticator
. The code inside the Authenticator
will run off the main thread.
当然,您需要为此编写 @Provides
方法:
Of course you will need to write the @Provides
methods for the same:
@Provides
@ApplicationScope
apiServiceHolder() {
return new APIServiceHolder();
}
并重构提供者方法:
@Provides
@ApplicationScope
APIService provideAPI(Retrofit retrofit, APIServiceHolder apiServiceHolder) {
APIService apiService = retrofit.create(APIService.class);
apiServiceHolder.setAPIService(apiService);
return apiService;
}
请注意,可变的全局状态通常不是一个好主意.但是,如果您的包组织得很好,您可以适当地使用访问修饰符来避免持有者的意外使用.
Note that mutable global state is not usually a good idea. However, if you have your packages organised well you may be able to use access modifiers appropriately to avoid unintended usages of the holder.
这篇关于Android Dagger2 + OkHttp + Retrofit 依赖循环报错的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!