Was bestimmt den Lebenszyklus einer Komponente (Objektdiagramm) in Dolch 2?

134

Ich versuche, meinen Kopf um Bereiche in Dolch 2 zu wickeln, insbesondere um den Lebenszyklus von Diagrammen mit Gültigkeitsbereich. Wie erstellen Sie eine Komponente, die bereinigt wird, wenn Sie den Bereich verlassen?

Bei einer Android-Anwendung haben Sie mit Dagger 1.x im Allgemeinen einen Stammbereich auf Anwendungsebene, den Sie erweitern würden, um einen untergeordneten Bereich auf Aktivitätsebene zu erstellen.

public class MyActivity {

    private ObjectGraph mGraph;

    public void onCreate() {
        mGraph = ((MyApp) getApplicationContext())
            .getObjectGraph()
            .plus(new ActivityModule())
            .inject(this);
    }

    public void onDestroy() {
        mGraph = null;
    }
}

Der untergeordnete Bereich bestand, solange Sie einen Verweis darauf aufbewahrten. In diesem Fall war dies der Lebenszyklus Ihrer Aktivität. Durch das Löschen der Referenz in onDestroy wurde sichergestellt, dass das Diagramm mit dem Gültigkeitsbereich frei für die Müllabfuhr war.

BEARBEITEN

Jesse Wilson hat kürzlich einen Mea Culpa veröffentlicht

Dagger 1.0 hat seine Bereichsnamen stark vermasselt ... Die Annotation @Singleton wird sowohl für Stammdiagramme als auch für benutzerdefinierte Diagramme verwendet. Daher ist es schwierig, den tatsächlichen Umfang einer Sache herauszufinden.

und alles andere, was ich gelesen / gehört habe, deutet darauf hin, dass Dolch 2 die Funktionsweise der Bereiche verbessert, aber ich habe Mühe, den Unterschied zu verstehen. Laut dem folgenden Kommentar von @Kirill Boyarshinov wird der Lebenszyklus einer Komponente oder Abhängigkeit wie üblich immer noch durch konkrete Referenzen bestimmt. Ist der Unterschied zwischen den Bereichen Dagger 1.x und 2.0 nur eine Frage der semantischen Klarheit?

Mein Verständnis

Dolch 1.x.

Abhängigkeiten waren entweder @Singletonoder nicht. Dies galt gleichermaßen für Abhängigkeiten im Stammdiagramm und in den Untergraphen, was zu Unklarheiten darüber führte, an welches Diagramm die Abhängigkeit gebunden war (siehe In Dolch sind Singletons innerhalb des zwischengespeicherten Untergraphen oder werden sie immer neu erstellt, wenn ein neuer Aktivitätsuntergraphen erstellt wird ist gebaut? )

Dolch 2.0

Mit benutzerdefinierten Bereichen können Sie semantisch klare Bereiche erstellen, die jedoch funktional der Anwendung @Singletonin Dagger 1.x entsprechen.

// Application level
@Singleton
@Component( modules = MyAppModule.class )
public interface MyAppComponent {
    void inject(Application app);
}

@Module
public class MyAppModule {

    @Singleton @Named("SingletonScope") @Provides
    StringBuilder provideStringBuilderSingletonScope() {
        return new StringBuilder("App");
    }
}

// Our custom scope
@Scope public @interface PerActivity {}

// Activity level
@PerActivty
@Component(
    dependencies = MyAppComponent.class,
    modules = MyActivityModule.class
)
public interface MyActivityComponent {
    void inject(Activity activity);
}

@Module
public class MyActivityModule {

    @PerActivity @Named("ActivityScope") @Provides
    StringBuilder provideStringBuilderActivityScope() {
        return new StringBuilder("Activity");
    }

    @Name("Unscoped") @Provides
    StringBuilder provideStringBuilderUnscoped() {
        return new StringBuilder("Unscoped");
    }
}

// Finally, a sample Activity which gets injected
public class MyActivity {

    private MyActivityComponent component;

    @Inject @Named("AppScope")
    StringBuilder appScope

    @Inject @Named("ActivityScope")
    StringBuilder activityScope1

    @Inject @Named("ActivityScope")
    StringBuilder activityScope2

    @Inject @Named("Unscoped")
    StringBuilder unscoped1

    @Inject @Named("Unscoped")
    StringBuilder unscoped2

    public void onCreate() {
        component = Dagger_MyActivityComponent.builder()
            .myApplicationComponent(App.getComponent())
            .build()
            .inject(this);

        appScope.append(" > Activity")
        appScope.build() // output matches "App (> Activity)+" 

        activityScope1.append("123")
        activityScope1.build() // output: "Activity123"

        activityScope2.append("456")
        activityScope1.build() // output: "Activity123456"

        unscoped1.append("123")
        unscoped1.build() // output: "Unscoped123"

        unscoped2.append("456")
        unscoped2.build() // output: "Unscoped456"

    }

    public void onDestroy() {
        component = null;
    }

}

Das Mitnehmen ist, dass die Verwendung @PerActivityIhre Absicht bezüglich des Lebenszyklus dieser Komponente kommuniziert , aber letztendlich können Sie die Komponente überall und jederzeit verwenden. Das einzige Versprechen von Dagger besteht darin, dass für eine bestimmte Komponente mit einem Bereich versehene Methoden eine einzelne Instanz zurückgeben. Ich gehe auch davon aus, dass Dagger 2 die Bereichsanmerkung für die Komponente verwendet, um zu überprüfen, ob Module nur Abhängigkeiten bereitstellen, die entweder im selben Bereich oder nicht im Gültigkeitsbereich liegen.

Zusammenfassend

Abhängigkeiten sind immer noch entweder Singleton- oder Nicht-Singleton-Abhängigkeiten, sind jedoch @Singletonjetzt für Singleton-Instanzen auf Anwendungsebene vorgesehen, und benutzerdefinierte Bereiche sind die bevorzugte Methode zum Kommentieren von Singleton-Abhängigkeiten mit einem kürzeren Lebenszyklus.

Der Entwickler ist dafür verantwortlich, den Lebenszyklus von Komponenten / Abhängigkeiten zu verwalten, indem er nicht mehr benötigte Referenzen löscht und dafür sorgt, dass Komponenten nur einmal in dem Bereich erstellt werden, für den sie bestimmt sind. Benutzerdefinierte Bereichsanmerkungen erleichtern jedoch die Identifizierung dieses Bereichs .

Die $ 64k Frage *

Ist mein Verständnis der Bereiche und Lebenszyklen von Dolch 2 korrekt?

* Eigentlich keine $ 64'000 Frage.

Enrico
quelle
5
Du hast nichts verpasst. Die Verwaltung des Live-Zyklus jeder Komponente erfolgt manuell. Aus eigener Erfahrung war das auch in Dolch 1 so. Beim Subgraphing wurde das ObjectGraph-Objekt auf Anwendungsebene unter Verwendung eines plus()Verweises auf ein neues Diagramm in Activity gespeichert und an seinen Live-Zyklus gebunden (dereferenziert in onDestroy). Die Bereiche stellen sicher, dass Ihre Komponentenimplementierungen beim Kompilieren fehlerfrei generiert werden, wobei jede Abhängigkeit erfüllt ist. Also nicht nur zu Dokumentationszwecken. Schauen Sie sich ein Beispiel aus diesem Thread an .
Kirill Boyarshinov
1
Um dies klar zu machen, geben "unscoped" -Anbietermethoden bei jeder Injektion neue Instanzen zurück?
user1923613
2
Warum setzen Sie component = null? in onDestroy ()?
Marian Paździoch

Antworten:

70

Wie für Ihre Frage

Was bestimmt den Lebenszyklus einer Komponente (Objektdiagramm) in Dolch 2?

Die kurze Antwort ist, dass Sie es bestimmen . Ihren Komponenten kann ein Bereich zugewiesen werden, z

@Scope
@Retention(RetentionPolicy.RUNTIME)
public @interface ApplicationScope {
}

@Scope
@Retention(RetentionPolicy.RUNTIME)
public @interface ActivityScope {
}

Diese sind für Sie aus zwei Gründen nützlich:

  • Validierung des Gültigkeitsbereichs: Eine Komponente kann nur Anbieter ohne Gültigkeitsbereich oder Anbieter mit Gültigkeitsbereich mit demselben Gültigkeitsbereich wie Ihre Komponente haben.

.

@Component(modules={ApplicationModule.class})
@ApplicationScope
public interface ApplicationComponent {
    Something something();
    AnotherThing anotherThing();

    void inject(Whatever whatever);
}

@Module
public class ApplicationModule {
    @ApplicationScope //application-scoped provider, only one can exist per component
    @Provides
    public Something something() {
         return new Something();
    }

    @Provides //unscoped, each INJECT call creates a new instance
    public AnotherThing anotherThing() {
        return new AnotherThing();
    }
}
  • Ermöglicht das Unter-Scoping Ihrer Abhängigkeiten im Scoping, sodass Sie eine "Subscoped" -Komponente erstellen können, die die bereitgestellten Instanzen aus der "Superscoped" -Komponente verwendet.

Dies kann mit @SubcomponentAnmerkungen oder Komponentenabhängigkeiten erfolgen. Ich persönlich bevorzuge Abhängigkeiten.

@Component(modules={ApplicationModule.class})
@ApplicationScope
public interface ApplicationComponent {
    Something something();
    AnotherThing anotherThing();

    void inject(Whatever whatever);

    ActivityComponent newActivityComponent(ActivityModule activityModule); //subcomponent factory method
}

@Subcomponent(modules={ActivityModule.class})
@ActivityScope
public interface ActivityComponent {
    ThirdThingy thirdThingy();

    void inject(SomeActivity someActivity);
}

@Module
public class ActivityModule {
    private Activity activity;

    public ActivityModule(Activity activity) {
        this.activity = activity;
    }

    //...
}

ApplicationComponent applicationComponent = DaggerApplicationComponent.create();
ActivityComponent activityComponent = applicationComponent.newActivityComponent(new ActivityModule(SomeActivity.this));

Oder Sie können solche Komponentenabhängigkeiten verwenden

@Component(modules={ApplicationModule.class})
@ApplicationScope
public class ApplicationComponent {
    Something something(); 
    AnotherThing anotherThing();

    void inject(Whatever whatever);
}

@Component(dependencies={ApplicationComponent.class}, modules={ActivityModule.class})
@ActivityScope
public interface ActivityComponent extends ApplicationComponent {
    ThirdThingy thirdThingy();

    void inject(SomeActivity someActivity);
}

@Module
public class ActivityModule {
    private Activity activity;

    public ActivityModule(Activity activity) {
        this.activity = activity;
    }

    //...
}

ApplicationComponent applicationComponent = DaggerApplicationComponent.create();
ActivityComponent activityComponent = DaggerActivityComponent.builder().activityModule(new ActivityModule(SomeActivity.this)).build();

Wichtige Dinge zu wissen:

  • Ein Anbieter mit Gültigkeitsbereich erstellt für jede Komponente eine Instanz für diesen bestimmten Bereich . Das bedeutet, dass eine Komponente ihre eigenen Instanzen verfolgt, andere Komponenten jedoch keinen gemeinsamen Bereichspool oder Magie haben. Um eine Instanz in einem bestimmten Bereich zu haben, benötigen Sie eine Instanz der Komponente. Aus diesem Grund müssen Sie die ApplicationComponentangeben, um auf eigene Abhängigkeiten zuzugreifen.

  • Eine Komponente kann nur eine Komponente mit Gültigkeitsbereich unterteilen. Abhängigkeiten von Komponenten mit mehreren Bereichen sind nicht zulässig.

EpicPandaForce
quelle
Eine Komponente kann nur eine Komponente mit Gültigkeitsbereich unterteilen. Abhängigkeiten von Komponenten mit mehreren Bereichen sind nicht zulässig (auch wenn sie alle unterschiedliche Bereiche haben, obwohl ich denke, dass dies ein Fehler ist). nicht wirklich verstehen, was es bedeutet
Damon Yuan
Aber was ist mit Livecycle? Wird ActivityComponent für den Garbage Collector in Frage kommen, wenn die Aktivität zerstört wird?
Sever
Wenn Sie es nicht woanders aufbewahren, dann ja
EpicPandaForce
1
Wenn wir also eine Komponente und ein injiziertes Objekt live durch die Aktivität benötigen, erstellen wir eine Komponente innerhalb der Aktivität. Wenn wir nur durch ein Fragment überleben wollen, sollte ich eine Komponente innerhalb eines Fragments bauen, oder? Wo Sie die Komponenteninstanz aufbewahren, wird der Umfang festgelegt?
Thracian
Was soll ich tun, wenn ich möchte, dass es durch eine bestimmte Aktivität überlebt?
Thracian