本文介绍了Android Dagger2 + OkHttp + Retrofit 依赖循环报错的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

嘿,我正在使用 Dagger2RetrofitOkHttp,但我正面临依赖循环问题.

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.

推荐答案

您的问题是:

  1. 您的 OKHttpClient 取决于您的身份验证器
  2. 您的身份验证器依赖于改造服务
  3. 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 依赖循环报错的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!

06-07 20:19