すべてのDagger関連クラスを配置する特定のパッケージがありますか?
または、それらを注入する関連クラスの隣に置きますか? MainActivityModule
とMainActivityComponent
がある場合は、MainActivity
と同じパッケージに入れます。
また、コンポーネントを内部クラスとして定義している人がかなりいます。 ApplicationComponent
クラス内で定義されるApplication
これは良い習慣だと思いますか?
編集:これはここで真実に近いという事実から始めましょうが、これは antipattern です。MartinFowlerのData Domain Presentation Layering
記事 HERE(リンクをクリック!) 、 /がMapperModule
とPresenterModule
を持たないことを指定します。GalleryModule
とSomeFeatureModule
が必要です。すべてのマッパー、プレゼンターなどが含まれています。
賢明な方法は、コンポーネントの依存関係を使用して、各機能の元のシングルトンコンポーネントをサブスコープにすることです。これは、 "full-stack" layering 、separation by機能。
以下に記載されているのは「アンチパターン」で、アプリケーションの最上位モジュールを「レイヤー」にカットします。これには多くの欠点があります。しないでください。しかし、あなたはそれを読んで、すべきでないことを学ぶことができます。
元のテキスト:
通常、アプリケーション全体が存在する限り、アプリ全体で使用するすべてのシングルトン依存関係を含めるために、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の複数のクラスから継承できないように、複数のスコープから継承できないためです。
スコープ外のプロバイダーは、モジュールがsingleインスタンスを保持せず、すべての注入呼び出しで新しいインスタンスを保持するようにします。スコープ依存関係を取得するには、モジュールプロバイダーメソッドにもスコープを提供する必要があります。
@Module
public class InteractorModule {
@Provides
@Singleton
public LeftNavigationDrawerInteractor leftNavigationDrawerInteractor() {
return new LeftNavigationDrawerInteractorImpl();
}
...
}
アプリケーションで、どこでもシングルトンコンポーネントを使用する場合、サブスコープを作成しない限り、追加のコンポーネントは必要ありません。必要に応じて、モジュールをビューやプレゼンターの完全なデータプロバイダーにすることも検討できます。
@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());
}
}
}
}
すべてのDagger関連クラスを配置する特定のパッケージがありますか?
または、それらを注入する関連クラスの隣に置きますか? MainActivityModuleとMainActivityComponentがある場合は、MainActivityと同じパッケージに入れます。
私はその経験はあまりありませんが、私のアプローチをお見せできます。より多くの経験を積んだ人の中には、そのソリューションを改善したり、視点を提供したりできる人もいるでしょう。
私は通常、Dagger 2クラスを次のように編成します。
_- di
|
+-- ApplicationComponent class
|
+-- modules
|
+-- AndroidModule class
|
+-- WebServiceModule class
|
+-- ...
|
_
di
パッケージには、Dagger 2および依存性注入に関連するクラスが含まれています。ApplicationComponent
という名前です-多くのDagger 2でAndroidアプリケーションを作成していませんまだコンポーネントがあり、1つのコンポーネントだけでソリューションを見てきました。modules
パッケージにはDagger 2モジュールが含まれていますアクティビティごとにモジュールを作成しません。モジュールは特定の機能をグループ化します。例えば。 SharedPreferences、EventBus(そのようなものを使用している場合)、ネットワーク接続などのインターフェースのようなシステムに強く接続されている要素は、AndroidModule
に配置できます。プロジェクトにWebServiceの重要なインターフェイスがある場合、またはそれらが多数ある場合は、WebServiceModule
にグループ化できます。たとえば、アプリケーションがネットワークの分析を担当し、ネットワークに関連する同様のタスクのための多くのインターフェイスを持っている場合、これらのインターフェイスをNetworkModule
にグループ化できます。アプリケーションが単純な場合、モジュールが1つだけになることがあります。複雑な場合、多くのモジュールを使用できます。私の意見では、単一のモジュールに多くのインターフェイスを含めるべきではありません。そのような状況がある場合、それらを別々のモジュールに分割することを検討できます。また、プロジェクト固有のビジネスロジックを別のモジュールに保持することもできます。
また、コンポーネントを内部クラスとして定義している人がかなりいます。 Applicationクラス内で定義されるApplicationComponent。これは良い習慣だと思いますか?
それが良いプラクティスか悪いプラクティスかはわかりません。そうする必要はないと思います。 Application
クラスを拡張するクラス内にpublic static get()
メソッドを作成すると、Application
のインスタンスをシングルトンとして返します。それははるかに簡単な解決策であり、Application
クラスのインスタンスは1つだけにする必要があります。単体テストでコンテキストをモックしたい場合は、コンテキストをパラメーターとして受け入れ、アプリケーションコードで、状況に応じてアプリケーションコンテキストまたはアクティビティコンテキストを渡すことができます。
注意してください、それは私のアプローチであり、より経験豊富な開発者の中には、プロジェクトを別のより良い方法で編成する人もいます。