简体   繁体   English

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

[英]Trying to solve a dependency cycle using dagger

dagger-android 2.16

I have a dependency cycle error in my Dagger module. 我的Dagger模块中存在依赖循环错误。 I think I know what the problem is but not sure how to solve it. 我想我知道问题是什么,但不知道如何解决它。

This is the error message: 这是错误消息:

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

This is the module below where I am getting the error 这是我收到错误的模块

@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)
    }
}

My LoginPresenterImp implements the LoginResponseListener and I want to pass that to the LoginRequestImp class so I can use it as a callback. 我的LoginPresenterImp实现了LoginResponseListener,我想将它传递给LoginRequestImp类,以便我可以将它用作回调。

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

And the loginResponseListener gets passed here: loginResponseListener在这里传递:

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

Many thanks in advance, 提前谢谢了,

As Ayush described in the comments: 正如Ayush在评论中所描述的:

You need LoginResponseListener to create LoginRequest and you need LoginRequest to create LoginResponseListener. 您需要LoginResponseListener来创建LoginRequest,您需要LoginRequest来创建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. 在LoginRequestImp(loginUser,loginPresenter)中创建LoginRequest时,loginPresenter是LoginResponseListener类型的构造函数的参数。 You should try to eliminate this dependency. 你应该尝试消除这种依赖。 May be you can set the listener later from the presenter 可能是您可以稍后从演示者设置监听器

In your reply between those comments: 在你的回复中:

LoginRequest has been created in provideLoginRequest LoginRequest已在provideLoginRequest中创建

But this is what's happening: 但这就是发生的事情:

  1. Your LoginFragment tries to inject LoginPresenter. 您的LoginFragment尝试注入LoginPresenter。
  2. Before you inject LoginPresenter you need to create a LoginRequest. 在注入LoginPresenter之前,您需要创建一个LoginRequest。
  3. Before you create a LoginRequest you need a LoginUser and a LoginRequestListener. 在创建LoginRequest之前,您需要LoginUser和LoginRequestListener。
  4. Before you create a LoginRequestListener (which you've implemented as a LoginPresenterImpl), you need a LoginRequest. 在创建LoginRequestListener(您已实现为LoginPresenterImpl)之前,您需要LoginRequest。
  5. You're in the middle of creating a LoginRequest, so Dagger gives up and correctly reports a circular reference. 您正在创建LoginRequest,因此Dagger放弃并正确报告循环引用。

To reiterate: Even though you've set up the bindings using interfaces correctly, Dagger can't create any of them because to call either constructor it has to create the other. 重申一下:即使您已正确使用接口设置绑定,Dagger也无法创建任何绑定,因为要调用任何一个构造函数,它必须创建另一个。 This isn't a problem with Dagger: If class A's constructor takes an instance of B, and class B's constructor takes an instance of A, you couldn't construct either of them manually either while respecting their constructor parameters. 这对Dagger来说不是问题:如果A类的构造函数接受B的实例,并且B类的构造函数接受A的实例,则在尊重其构造函数参数时,您无法手动构造它们中的任何一个。


As Ayush suggested, don't have LoginRequest inject a LoginResponseListener. 正如Ayush建议的那样,没有LoginRequest注入一个LoginResponseListener。 Instead, create a method like setLoginResponseListener , which LoginPresenterImp can call. 相反,创建一个类似setLoginResponseListener的方法,LoginPresenterImp可以调用该方法。 I recommend this approach as well, in part because @Reusable has weaker semantics than you want : You want to be absolutely sure that the LoginPresenterImp instance that acts as your LoginPresenter is the same instance that acts as LoginResponseListener. 我也推荐这种方法,部分原因是因为@Reusable的语义比你想要的要弱 :你想要绝对确定充当你的LoginPresenter的LoginPresenterImp实例是同一个充当LoginResponseListener的实例。

As an alternative, you can inject Provider<LoginPresenter> instead of LoginResponseListener , and change LoginRequestImp to accept a Provider as well. 作为替代方案,您可以注入Provider<LoginPresenter>而不是LoginResponseListener ,并更改LoginRequestImp以接受Provider。 (You could also inject a Provider<LoginResponseListener> , but if you want that LoginResponseListener to be the same one as your LoginPresenter instance, you shouldn't call the LoginPresenterImp constructor explicitly. You'd want to switch to @Binds ideally, or at least have your @Provides method inject LoginPresenter instead.) You're allowed to inject a Provider, because 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 . (您也可以注入Provider<LoginResponseListener> ,但如果您希望LoginResponseListener与LoginPresenter实例相同,则不应显式调用LoginPresenterImp构造函数。您希望理想地切换到@Binds ,或者至少让你的@Provides方法注入LoginPresenter。)你被允许注入一个Provider,因为Provider<T>会自动绑定Dagger知道如何提供的每个类<T>它解决了你的问题,因为Dagger可以传递Provider<T>而不尝试创建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. 即使您将绑定保留为@Reusable ,这在技术上似乎也会@Reusable ,但在多线程环境中, @Reusable不会保证您总是会收到与LoginPresenter相同的LoginRequestListener实例,或者您将收到一个新的每个LoginFragment的LoginPresenter。 If you want to guarantee that, you can look into custom scopes . 如果您想保证这一点,您可以查看自定义范围

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM