尝试使用匕首解决依赖循环

Trying to solve a dependency cycle using dagger

dagger-android 2.16

我的 Dagger 模块出现依赖循环错误。我想我知道问题是什么,但不确定如何解决。

这是错误信息:

Found a dependency cycle:
  public interface LoginFragmentSubcomponent extends AndroidInjector<LoginFragment> {
     presentation.login.request.LoginRequest is injected at
              mobileui.login.di.LoginActivityModule.provideLoginResponseListener(…, loginRequest)
          presentation.login.response.LoginResponseListener is injected at
              mobileui.login.di.LoginActivityModule.provideLoginRequest(…, loginPresenter)
          presentation.login.request.LoginRequest is injected at
              mobileui.login.di.LoginActivityModule.provideLoginPresenter(…, loginRequest)
          mobileui.login.LoginPresenter is injected at
              mobileui.login.LoginFragment.loginPresenter

这是我收到错误的下方模块

@Module
class LoginActivityModule {
    @Reusable
    @Provides
    fun provideLoginPresenter(loginRequest: LoginRequest): LoginPresenter {
        return LoginPresenterImp(loginRequest)
    }

    @Reusable
    @Provides
    fun provideLoginResponseListener(loginRequest: LoginRequest): LoginResponseListener {
        LoginPresenterImp(loginRequest)
    }

    @Reusable
    @Provides
    fun provideLoginRequest(loginUser: LoginUser,
                            loginPresenter: LoginResponseListener): LoginRequest {
        return LoginRequestImp(loginUser, loginPresenter)
    }
}

我的 LoginPresenterImp 实现了 LoginResponseListener,我想将其传递给 LoginRequestImp class 这样我就可以将其用作回调。

class LoginPresenterImp(private val loginRequest: LoginRequest) :
    BasePresenterImp<LoginView>(),
    LoginPresenter,
    LoginResponseListener {
}

并且 loginResponseListener 在这里传递:

class LoginRequestImp(
    private val loginUser: LoginUser,
    private val loginResponseListener: LoginResponseListener)
    : LoginRequest {
}

非常感谢,

如评论中Ayush所述:

You need LoginResponseListener to create LoginRequest and you need LoginRequest to create LoginResponseListener. So, you are getting the error.

When you are creating LoginRequest in LoginRequestImp(loginUser, loginPresenter), loginPresenter is a parameter to the constructor of type LoginResponseListener. You should try to eliminate this dependency. May be you can set the listener later from the presenter

在这些评论之间的回复中:

LoginRequest has been created in provideLoginRequest

但这就是正在发生的事情:

  1. 您的 LoginFragment 试图注入 LoginPresenter。
  2. 在注入 LoginPresenter 之前,您需要创建一个 LoginRequest。
  3. 在创建 LoginRequest 之前,您需要一个 LoginUser 和一个 LoginRequestListener。
  4. 在创建 LoginRequestListener(已作为 LoginPresenterImpl 实现)之前,您需要一个 LoginRequest。
  5. 您正在创建登录请求,因此 Dagger 放弃并正确报告循环引用。

重申一下:即使您使用接口正确设置了绑定,Dagger 也无法创建它们中的任何一个,因为要调用其中一个构造函数,它必须创建另一个构造函数。这不是 Dagger 的问题:如果 class A 的构造函数采用 B 的实例,并且 class B 的构造函数采用 A 的实例,那么在尊重它们的同时,你也不能手动构造它们中的任何一个构造函数参数。


正如 Ayush 所建议的,不要让 LoginRequest 注入 LoginResponseListener。相反,创建一个类似于 setLoginResponseListener 的方法,LoginPresenterImp 可以调用该方法。我也推荐这种方法,部分原因是 :您要绝对确保充当 LoginPresenter 的 LoginPresenterImp 实例与充当 LoginResponseListener 的实例相同。

作为替代方案,您可以注入 Provider<LoginPresenter> 而不是 LoginResponseListener,并更改 LoginRequestImp 以接受 Provider。 (您也可以注入 Provider<LoginResponseListener>,但如果您希望 LoginResponseListener 与您的 LoginPresenter 实例相同,则不应显式调用 LoginPresenterImp 构造函数。您需要切换到 @Binds理想情况下,或者至少让您的 @Provides 方法注入 LoginPresenter。)您可以注入 Provider,因为 a Provider<T> is automatically bound for every class <T> that Dagger knows how to provide, and it solves your problem because Dagger can pass a Provider<T> without trying to create the T. This will technically appear to work even if you leave your bindings as @Reusable, but in a multithreaded environment @Reusable isn't going to guarantee that you always receive the same instance for LoginRequestListener as your LoginPresenter, or that you'll receive a new LoginPresenter for every LoginFragment. If you want to guarantee that, you can look into custom scopes.