Ich habe eine Weile mit dagger2 gearbeitet. Und ich war verwirrt, ob ich für jede Aktivität / jedes Fragment eine eigene Komponente / ein eigenes Modul erstellen sollte. Bitte helfen Sie mir, dies zu klären:
Zum Beispiel haben wir eine App und die App hat ungefähr 50 Bildschirme. Wir werden den Code nach dem MVP-Muster und Dagger2 für DI implementieren. Angenommen, wir haben 50 Aktivitäten und 50 Moderatoren.
Meiner Meinung nach sollten wir den Code normalerweise so organisieren:
Erstellen Sie eine AppComponent und ein AppModule, die alle Objekte bereitstellen, die verwendet werden, während die App geöffnet ist.
@Module public class AppModule { private final MyApplicationClass application; public AppModule(MyApplicationClass application) { this.application = application; } @Provides @Singleton Context provideApplicationContext() { return this.application; } //... and many other providers } @Singleton @Component( modules = { AppModule.class } ) public interface AppComponent { Context getAppContext(); Activity1Component plus(Activity1Module module); Activity2Component plus(Activity2Module module); //... plus 48 methods for 48 other activities. Suppose that we don't have any other Scope (like UserScope after user login, ....) }
Erstellen Sie ActivityScope:
@Scope @Documented @Retention(value=RUNTIME) public @interface ActivityScope { }
Erstellen Sie für jede Aktivität eine Komponente und ein Modul. Normalerweise füge ich sie als statische Klassen in die Aktivitätsklasse ein:
@Module public class Activity1Module { public LoginModule() { } @Provides @ActivityScope Activity1Presenter provideActivity1Presenter(Context context, /*...some other params*/){ return new Activity1PresenterImpl(context, /*...some other params*/); } } @ActivityScope @Subcomponent( modules = { Activity1Module.class } ) public interface Activity1Component { void inject(Activity1 activity); // inject Presenter to the Activity } // .... Same with 49 remaining modules and components.
Dies sind nur sehr einfache Beispiele, um zu zeigen, wie ich dies implementieren würde.
Aber ein Freund von mir hat mir gerade eine andere Implementierung gegeben:
Erstellen Sie PresenterModule, das alle Präsentatoren bereitstellt:
@Module public class AppPresenterModule { @Provides Activity1Presenter provideActivity1Presentor(Context context, /*...some other params*/){ return new Activity1PresenterImpl(context, /*...some other params*/); } @Provides Activity2Presenter provideActivity2Presentor(Context context, /*...some other params*/){ return new Activity2PresenterImpl(context, /*...some other params*/); } //... same with 48 other presenters. }
Erstellen Sie AppModule und AppComponent:
@Module public class AppModule { private final MyApplicationClass application; public AppModule(MyApplicationClass application) { this.application = application; } @Provides @Singleton Context provideApplicationContext() { return this.application; } //... and many other provides } @Singleton @Component( modules = { AppModule.class, AppPresenterModule.class } ) public interface AppComponent { Context getAppContext(); public void inject(Activity1 activity); public void inject(Activity2 activity); //... and 48 other methods for 48 other activities. Suppose that we don't have any other Scope (like UserScope after user login, ....) }
Seine Erklärung lautet: Er muss nicht für jede Aktivität Komponenten und Module erstellen. Ich denke, die Idee meines Freundes ist überhaupt nicht gut, aber bitte korrigieren Sie mich, wenn ich falsch liege. Hier sind die Gründe:
Viele Speicherlecks :
- Die App erstellt 50 Präsentatoren, auch wenn der Benutzer nur 2 Aktivitäten geöffnet hat.
- Nachdem der Benutzer eine Aktivität geschlossen hat, bleibt sein Präsentator weiterhin bestehen
Was passiert, wenn ich zwei Instanzen einer Aktivität erstellen möchte? (Wie kann er zwei Moderatoren erstellen?)
Die Initialisierung der App dauert sehr lange (da viele Präsentatoren, Objekte usw. erstellt werden müssen).
Entschuldigung für einen langen Beitrag, aber bitte helfen Sie mir, dies für mich und meinen Freund zu klären. Ich kann ihn nicht überzeugen. Ihre Kommentare werden sehr geschätzt.
/ ------------------------------------------------- ---------------------- /
Nach einer Demo bearbeiten.
Zunächst danke für die Antwort von @pandawarrior. Ich hätte eine Demo erstellen sollen, bevor ich diese Frage gestellt habe. Ich hoffe, meine Schlussfolgerung hier könnte jemand anderem helfen.
- Was mein Freund getan hat, verursacht keine Speicherlecks, es sei denn, er legt einen Bereich für die Provides-Methoden fest. (Zum Beispiel @Singleton oder @UserScope, ...)
- Wir können viele Präsentatoren erstellen, wenn die Provides-Methode keinen Bereich hat. (Also, mein zweiter Punkt ist auch falsch)
- Dolch erstellt die Moderatoren nur dann, wenn sie benötigt werden. (Die Initialisierung der App wird also nicht lange dauern. Ich war durch Lazy Injection verwirrt.)
Alle Gründe, die ich oben gesagt habe, sind größtenteils falsch. Das heißt aber nicht, dass wir meiner Freundidee aus zwei Gründen folgen sollten:
Es ist nicht gut für die Architektur der Quelle, wenn er alle Präsentatoren in Modul / Komponente einbindet. (Es verstößt gegen das Prinzip der Schnittstellentrennung , möglicherweise auch gegen das Prinzip der Einzelverantwortung .)
Wenn wir eine Scope-Komponente erstellen, wissen wir, wann sie erstellt und wann sie zerstört wird. Dies ist ein großer Vorteil, um Speicherlecks zu vermeiden. Daher sollten wir für jede Aktivität eine Komponente mit einem @ActivityScope erstellen. Stellen wir uns bei der Implementierung meiner Freunde vor, dass wir vergessen haben, einen Bereich in die Provider-Methode aufzunehmen => Speicherlecks werden auftreten.
Meiner Meinung nach könnten wir mit einer kleinen App (nur ein paar Bildschirme ohne viele Abhängigkeiten oder mit ähnlichen Abhängigkeiten) die Idee meiner Freunde anwenden, aber das wird natürlich nicht empfohlen.
Lesen Sie lieber weiter: Was bestimmt den Lebenszyklus einer Komponente (Objektdiagramm) in Dolch 2? Dagger2 Aktivitätsumfang, wie viele Module / Komponenten benötige ich?
Und noch ein Hinweis: Wenn Sie sehen möchten, wann das Objekt zerstört wird, können Sie die Methoden zusammen aufrufen, und der GC wird sofort ausgeführt:
System.runFinalization();
System.gc();
Wenn Sie nur eine dieser Methoden verwenden, wird GC später ausgeführt, und Sie erhalten möglicherweise falsche Ergebnisse.
ControllerModule
referenzierte ein neuesPresenter
und dann wird der Präsentator in dasActivity
oder injiziertFragment
. Eine solide Meinung dafür oder dagegen?ControllerComponent
sollten Sie sie injizieren. Ob Sie sie im Inneren verdrahtenControllerModule
oder ein zusätzliches Modul einführen, liegt bei Ihnen. In echten Apps empfehle ich die Verwendung eines Ansatzes mit mehreren Modulen pro Komponente, anstatt alles in einem einzigen Modul zusammenzufassen. Hier ist ein Beispiel fürApplicationComponent
, aber die Controller werden die gleichen sein: github.com/techyourchance/idocare-android/tree/master/app/src/…ApplicationComponent
allen Abhängigkeiten deklarieren ,ControllerComponent
die verwendet werden können. Auch die Methodenanzahl des generierten Codes ist höher. Ich habe noch keinen guten Grund gefunden, abhängige Komponenten zu verwenden.dagger.android
Paket, weil ich finde, dass es schlecht motiviert ist. Daher ist dieses Beispiel immer noch sehr aktuell und der beste Weg, DI in Android IMHO zu machen.Einige der besten Beispiele für die Organisation Ihrer Komponenten, Module und Pakete finden Sie im Github-Repo von Google Android Architecture Blueprints hier .
Wenn Sie den Quellcode dort untersuchen, sehen Sie, dass es eine einzelne Komponente mit App-Bereich gibt (mit einem Lebenszyklus der Dauer der gesamten App) und dann Komponenten mit Aktivitätsbereich für die Aktivität und das Fragment, die einer bestimmten Funktionalität in a entsprechen Projekt. Zum Beispiel gibt es die folgenden Pakete:
In jedem Paket befindet sich ein Modul, eine Komponente, ein Präsentator usw. Im Inneren
taskdetail
befinden sich beispielsweise die folgenden Klassen:Der Vorteil dieser Organisation (anstatt alle Aktivitäten in einer Komponente oder einem Modul zu gruppieren) besteht darin, dass Sie die Java-Eingabehilfen-Modifikatoren nutzen und den effektiven Java-Punkt 13 erfüllen können. Mit anderen Worten, die funktional gruppierten Klassen befinden sich in derselben verpacken und Sie können die Vorteile der
protected
undpackage-private
Zugänglichkeit Modifikatoren zu ungewollten Nutzungen Ihrer Klassen zu verhindern.quelle
Die erste Option erstellt eine Komponente mit Unterbereich für jede Aktivität, wobei die Aktivität Komponenten mit Unterbereich erstellen kann, die nur die Abhängigkeit (Präsentator) für diese bestimmte Aktivität bereitstellen.
Die zweite Option erstellt eine einzelne
@Singleton
Komponente, die die Präsentatoren als nicht abgedeckte Abhängigkeiten bereitstellen kann. Wenn Sie also auf sie zugreifen, erstellen Sie jedes Mal eine neue Instanz des Präsentators. (Nein, es wird keine neue Instanz erstellt, bis Sie eine anfordern.)Technisch gesehen ist keiner der Ansätze schlechter als der andere. Der erste Ansatz trennt Präsentatoren nicht nach Merkmalen, sondern nach Ebenen.
Ich habe beide benutzt, beide funktionieren und beide machen Sinn.
Der einzige Nachteil der ersten Lösung (wenn Sie
@Component(dependencies={...}
anstelle von verwenden@Subcomponent
) besteht darin, dass Sie sicherstellen müssen, dass nicht die Aktivität intern ein eigenes Modul erstellt, da Sie dann die Implementierung von Modulmethoden nicht durch Mocks ersetzen können. Wenn Sie die Konstruktorinjektion anstelle der Feldinjektion verwenden, können Sie die Klasse einfach direkt mit dem Konstruktor erstellen und sie direkt verspotten.quelle
Verwenden Sie
Provider<"your component's name">
anstelle der einfachen Komponentenimplementierung, um Speicherlecks zu vermeiden und Tonnen nutzloser Komponenten zu erstellen. Daher werden Ihre Komponenten beim Aufruf der Methode get () von Lazy erstellt, da Sie keine Instanz der Komponente, sondern nur den Provider angeben. Daher wird Ihr Moderator angewendet, wenn .get () des Anbieters aufgerufen wurde. Lesen Sie hier über den Anbieter und wenden Sie diesen an. ( Offizielle Dolchdokumentation )Eine andere gute Möglichkeit ist die Verwendung von Multibinding. Dementsprechend sollten Sie Ihre Präsentatoren in eine Karte einbinden und sie bei Bedarf über Anbieter erstellen. ( Hier finden Sie Dokumente zum Thema Multibinding )
quelle
Ihr Freund hat Recht, Sie müssen nicht wirklich für jede Aktivität Komponenten und Module erstellen. Dagger soll Ihnen dabei helfen, unordentlichen Code zu reduzieren und Ihre Android-Aktivitäten sauberer zu gestalten, indem Klasseninstanziierungen an die Module delegiert werden, anstatt sie in der onCreate-Methode von Activities zu instanziieren.
Normalerweise machen wir das so
Sie tun dies stattdessen
Zu viele Dinge zu schreiben, besiegt also den Zweck des Dolches, nein? Ich instanziiere meine Präsentatoren eher in Aktivitäten, wenn ich für jede Aktivität Module und Komponenten erstellen muss.
Was Ihre Fragen betrifft zu:
1- Speicherverlust:
Nein, es sei denn, Sie
@Singleton
fügen den von Ihnen bereitgestellten Moderatoren eine Anmerkung hinzu. Dolch erstellt das Objekt nur, wenn Sie eine@Inject
in der Zielklasse ausführen. Die anderen Präsentatoren in Ihrem Szenario werden nicht erstellt. Sie können versuchen, mithilfe von Protokoll festzustellen, ob sie erstellt wurden oder nicht.}}
2- Sie injizieren zweimal und protokollieren ihren Hash-Code
3. Nein, die Objekte werden nur erstellt, wenn Sie
@Inject
an den Aktivitäten teilnehmen, anstatt an der App init.quelle