Попытка решить цикл зависимости с помощью кинжала

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 LoginPresenterImp(private val loginRequest: LoginRequest) :
    BasePresenterImp<LoginView>(),
    LoginPresenter,
    LoginResponseListener {
}

И loginResponseListener передается здесь:

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

Спасибо заранее,

1 ответ

Решение

Как Ayush Maharjan описал в комментариях:

Вам нужен LoginResponseListener для создания LoginRequest и вам нужен LoginRequest для создания LoginResponseListener. Итак, вы получаете ошибку.

Когда вы создаете LoginRequest в LoginRequestImp(loginUser, loginPresenter), loginPresenter является параметром для конструктора типа LoginResponseListener. Вы должны попытаться устранить эту зависимость. Может быть, вы можете установить слушателя позже от докладчика

В вашем ответе между этими комментариями:

LoginRequest был создан в provideLoginRequest

Но это то, что происходит:

  1. Ваш LoginFragment пытается внедрить LoginPresenter.
  2. Прежде чем вводить LoginPresenter, вам нужно создать LoginRequest.
  3. Перед созданием запроса LoginRequest вам нужны LoginUser и LoginRequestListener.
  4. Прежде чем создавать LoginRequestListener (который вы реализовали как LoginPresenterImpl), вам нужно LoginRequest.
  5. Вы находитесь в процессе создания LoginRequest, поэтому Dagger сдается и правильно сообщает о циклической ссылке.

Повторим: даже если вы правильно установили привязки с помощью интерфейсов, Dagger не может создать ни одного из них, потому что для вызова одного из конструкторов необходимо создать другой. С Dagger это не проблема: если конструктор класса A принимает экземпляр B, а конструктор класса B - A, вы не можете создать ни один из них вручную, при соблюдении параметров их конструктора.


Как и предполагал Аюш, у LoginRequest не должно вводить LoginResponseListener. Вместо этого создайте метод, подобный setLoginResponseListener, который LoginPresenterImp может вызвать. Я также рекомендую этот подход, отчасти потому, что @Reusable имеет более слабую семантику, чем вы хотите: вы хотите быть абсолютно уверены, что экземпляр LoginPresenterImp, который действует как ваш LoginPresenter, является тем же экземпляром, который действует как LoginResponseListener.

В качестве альтернативы вы можете ввести Provider<LoginPresenter> вместо LoginResponseListener и измените LoginRequestImp, чтобы принять поставщика. (Вы также можете ввести Provider<LoginResponseListener>, но если вы хотите, чтобы LoginResponseListener был тем же, что и ваш экземпляр LoginPresenter, вам не следует явно вызывать конструктор LoginPresenterImp. Вы хотели бы переключиться на @Binds в идеале, или, по крайней мере, ваш @Provides вместо этого введите метод LoginPresenter.) Вам разрешено вводить поставщика, потому что Provider<T> автоматически привязывается к каждому классу <T> что Dagger знает, как предоставить, и это решает вашу проблему, потому что Dagger может передать Provider<T> не пытаясь создать T, Технически это будет работать, даже если вы оставите свои привязки как @Reusable, но в многопоточной среде @Reusable не собирается гарантировать, что вы всегда получаете тот же экземпляр LoginRequestListener, что и LoginPresenter, или что вы получите новый LoginPresenter для каждого LoginFragment. Если вы хотите гарантировать это, вы можете посмотреть в пользовательских областях.

Другие вопросы по тегам