Попытка решить цикл зависимости с помощью кинжала
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
Но это то, что происходит:
- Ваш LoginFragment пытается внедрить LoginPresenter.
- Прежде чем вводить LoginPresenter, вам нужно создать LoginRequest.
- Перед созданием запроса LoginRequest вам нужны LoginUser и LoginRequestListener.
- Прежде чем создавать LoginRequestListener (который вы реализовали как LoginPresenterImpl), вам нужно LoginRequest.
- Вы находитесь в процессе создания 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. Если вы хотите гарантировать это, вы можете посмотреть в пользовательских областях.