Hey there I am using Dagger2
, Retrofit
and OkHttp
and I am facing dependency cycle issue.
When providing OkHttp
:
Your problem is:
Hence the circular dependency.
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.
A very simple APIServiceHolder:
public class APIServiceHolder {
private APIService apiService;
@Nullable
APIService apiService() {
return apiService;
}
void setAPIService(APIService apiService) {
this.apiService = apiService;
}
}
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;
}
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.
Of course you will need to write the @Provides
methods for the same:
@Provides
@ApplicationScope
apiServiceHolder() {
return new APIServiceHolder();
}
And refactor the provider methods:
@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.