Попытка решить цикл зависимости с помощью кинжала - PullRequest
0 голосов
/ 02 января 2019
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 Ответ

0 голосов
/ 07 января 2019

Как Аюш описано в комментариях:

Вам нужен 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.) Вам разрешено внедрять провайдера, потому что a Provider<T> автоматически связывается с каждым классом <T>, который Даггер знает, как предоставить , и это решает вашу проблему, потому что Dagger может передать Provider<T>, не пытаясь создать T. Технически это будет работать, даже если вы оставите свои привязки как @Reusable, но в многопоточной среде @Reusable не гарантирует, что вы всегда получите тот же экземпляр LoginRequestListener, что и LoginPresenter, или что вы получите новый LoginPresenter для каждого LoginFragment. Если вы хотите это гарантировать, вы можете посмотреть пользовательских областей .

...