Ответ 1
EDIT: Позвольте мне начать с того, что это близко к истине здесь, но это antipattern, как описано Мартином Фаулером Data Domain Presentation Layering
article ЗДЕСЬ (НАЖМИТЕ ЛИНК!), в котором указывается, что не должен иметь MapperModule
и PresenterModule
, вы должны иметь GalleryModule
и SomeFeatureModule
который имеет в нем все картографы, докладчики и т.д.
Умный маршрут для этого - использовать зависимости компонентов для подкачки вашего оригинального синглтон-компонента для каждой вашей функции. Это то, что я описал, это "полный стек" , разделение .
То, что записано ниже, - это "анти-шаблон", где вы вырезаете модули верхнего уровня приложения в "слои". Для этого есть множество недостатков. Не делай этого. Но вы можете прочитать его и узнать, что не делать.
ОРИГИНАЛЬНЫЙ ТЕКСТ:
Обычно вы должны использовать один Component
, такой как ApplicationComponent
, чтобы содержать все однопользовательские зависимости, которые вы используете во всем приложении, пока существует все приложение. Вы должны создать это в своем классе Application и сделать это доступным из других источников.
Структура проекта для меня в настоящее время:
+ injection
|- components
|-- ApplicationComponent.java
|- modules
|- data
|-- DbMapperModule.java
|-- ...
|- domain
|-- InteractorModule.java
|-- ...
|- presentation
|-- ...
|- utils
|-- ...
|- scope
|- subcomponents
|- data
|-- ...
|- domain
|-- DbMapperComponent.java
|-- ...
|- presentation
|-- ...
|- utils
|-- ...
|-- AppContextComponent.java
|-- AppDataComponent.java
|-- AppDomainComponent.java
|-- AppPresentationComponent.java
|-- AppUtilsComponent.java
Например, мой выглядит следующим образом:
public enum Injector {
INSTANCE;
private ApplicationComponent applicationComponent;
private Injector() {
}
public ApplicationComponent getApplicationComponent() {
return applicationComponent;
}
ApplicationComponent initializeApplicationComponent(CustomApplication customApplication) {
AppContextModule appContextModule = new AppContextModule(customApplication);
RealmModule realmModule = new RealmModule(customApplication.getRealmHolder());
applicationComponent = DaggerApplicationComponent.builder()
.appContextModule(appContextModule)
.realmModule(realmModule)
.build();
return applicationComponent;
}
}
И вам понадобится ApplicationComponent
, который может вставлять любые защищенные оболочкой поля любого класса, который вы хотите ввести в поле.
@Singleton
@Component(modules = {
AppContextModule.class,
DbMapperModule.class,
DbTaskModule.class,
RealmModule.class,
RepositoryModule.class,
InteractorModule.class,
ManagerModule.class,
ServiceModule.class,
PresenterModule.class,
JobManagerModule.class,
XmlPersisterModule.class
})
public interface ApplicationComponent
extends AppContextComponent, AppDataComponent, AppDomainComponent, AppUtilsComponent, AppPresentationComponent {
void inject(CustomApplication customApplication);
void inject(DashboardActivity dashboardActivity);
...
}
Для меня AppContextComponent
будет @Subcomponent
, но на самом деле это не значит. Это всего лишь способ создать подскоп, а не способ сократить ваш компонент на более мелкие части. Таким образом, интерфейс, который я наследую, на самом деле является обычным interface
с методами предоставления. То же самое для остальных.
public interface AppContextComponent {
CustomApplication customApplication();
Context applicationContext();
AppConfig appConfig();
PackageManager packageManager();
AlarmManager alarmManager();
}
Компонентные зависимости (которые позволяют подкапывать, как и подкомпоненты) не позволяют использовать несколько компонентов с областью, что также означает, что ваши модули не будут доступны. Это связано с тем, как вы не можете наследовать из нескольких областей, так же, как вы не можете наследовать от нескольких классов в Java.
Поставщики без доступа делают это так, чтобы модуль не сохранял один экземпляр, а новый - при каждом вызове инъекции. Чтобы получить зависимые от области действия области действия, вам также необходимо предоставить область применения методов модуля.
@Module
public class InteractorModule {
@Provides
@Singleton
public LeftNavigationDrawerInteractor leftNavigationDrawerInteractor() {
return new LeftNavigationDrawerInteractorImpl();
}
...
}
В приложении, если вы используете компоненты Singleton везде, вам не понадобится больше компонентов, если вы не создадите подкоды. Если вы хотите, вы даже можете подумать о том, чтобы сделать ваши модули полным поставщиком данных для ваших представлений и презентаторов.
@Component(dependencies = {ApplicationComponent.class}, modules = {DetailActivityModule.class})
@ActivityScope
public interface DetailActivityComponent extends ApplicationComponent {
DataObject data();
void inject(DetailActivity detailActivity);
}
@Module
public class DetailActivityModule {
private String parameter;
public DetailActivityModule(String parameter) {
this.parameter = parameter;
}
@Provides
public DataObject data(RealmHolder realmHolder) {
Realm realm = realmHolder.getRealm();
return realm.where(DataObject.class).equalTo("parameter", parameter).findFirst();
}
}
Подсканирование позволяет вам иметь несколько экземпляров вашего презентатора, которые затем могут сохранять состояние. Это имеет смысл, например, в Mortar/Flow, где каждый экран имеет свой собственный "путь" , и каждый путь имеет свой собственный компонент - для предоставить данные в качестве "плана".
public class FirstPath
extends BasePath {
public static final String TAG = " FirstPath";
public final int parameter;
public FirstPath(int parameter) {
this.parameter = parameter;
}
//...
@Override
public int getLayout() {
return R.layout.path_first;
}
@Override
public FirstViewComponent createComponent() {
FirstPath.FirstViewComponent firstViewComponent = DaggerFirstPath_FirstViewComponent.builder()
.applicationComponent(InjectorService.obtain())
.firstViewModule(new FirstPath.FirstViewModule(parameter))
.build();
return firstViewComponent;
}
@Override
public String getScopeName() {
return TAG + "_" + parameter;
}
@ViewScope //needed
@Component(dependencies = {ApplicationComponent.class}, modules = {FirstViewModule.class})
public interface FirstViewComponent
extends ApplicationComponent {
String data();
FirstViewPresenter firstViewPresenter();
void inject(FirstView firstView);
void inject(FirstViewPresenter firstViewPresenter);
}
@Module
public static class FirstViewModule {
private int parameter;
public FirstViewModule(int parameter) {
this.parameter = parameter;
}
@Provides
public String data(Context context) {
return context.getString(parameter);
}
@Provides
@ViewScope //needed to preserve scope
public FirstViewPresenter firstViewPresenter() {
return new FirstViewPresenter();
}
}
public static class FirstViewPresenter
extends ViewPresenter<FirstView> {
public static final String TAG = FirstViewPresenter.class.getSimpleName();
@Inject
String data;
public FirstViewPresenter() {
Log.d(TAG, "First View Presenter created: " + toString());
}
@Override
protected void onEnterScope(MortarScope scope) {
super.onEnterScope(scope);
FirstViewComponent firstViewComponent = scope.getService(DaggerService.TAG);
firstViewComponent.inject(this);
Log.d(TAG, "Data [" + data + "] and other objects injected to first presenter.");
}
@Override
protected void onSave(Bundle outState) {
super.onSave(outState);
FirstView firstView = getView();
outState.putString("input", firstView.getInput());
}
@Override
protected void onLoad(Bundle savedInstanceState) {
super.onLoad(savedInstanceState);
if(!hasView()) {
return;
}
FirstView firstView = getView();
if(savedInstanceState != null) { //needed check
firstView.setInput(savedInstanceState.getString("input"));
}
}
public void goToNextActivity() {
FirstPath firstPath = Path.get(getView().getContext());
if(firstPath.parameter != R.string.hello_world) {
Flow.get(getView()).set(new FirstPath(R.string.hello_world));
} else {
Flow.get(getView()).set(new SecondPath());
}
}
}
}