Dolche – Grundlagen

Manuelle Abhängigkeitsinjektion oder -dienst in einer Android-App kann die Suche je nach Größe Projekt arbeiten. Sie können die Komplexität Ihres Projekts beim Skalieren begrenzen, indem Sie Dagger zum Verwalten von Abhängigkeiten

Dagger generiert automatisch Code, der den Code nachahmt, den Sie sonst erhalten würden. handschriftlich sind. Da der Code bei der Kompilierung generiert wird, kann er nachverfolgt werden. und leistungsstärker als andere reflexionsbasierte Lösungen wie Guice:

Vorteile von Dagger

Mit Dagger müssen Sie keinen lästigen und fehleranfälligen Boilerplate-Code mehr schreiben:

  • Manuelles Generieren des AppContainer-Codes (Anwendungsdiagramm) manuell im DI-Abschnitt implementiert.

  • Erstellen von Factories für die im Anwendungsdiagramm verfügbaren Klassen. Dieses wie Abhängigkeiten intern befriedigt werden.

  • Entscheiden, ob eine Abhängigkeit wiederverwendet oder über die Methode Verwendung von Bereichen

  • Container für bestimmte Abläufe erstellen, wie Sie es beim Anmeldevorgang in der im vorherigen Abschnitt mit den Unterkomponenten von Dagger. Das verbessert die Leistung deiner App indem Sie Objekte im Arbeitsspeicher freigeben, wenn sie nicht mehr benötigt werden.

Das alles geschieht bei der Erstellung automatisch, solange Sie Abhängigkeiten einer Klasse deklarieren und mithilfe von Anmerkungen. Dagger generiert Code, der dem entspricht, was Sie geschrieben hätten manuell. Intern erstellt Dagger ein Diagramm mit Objekten, auf die es verweisen kann. um eine Möglichkeit zu finden, eine Instanz einer Klasse bereitzustellen. Für jede Klasse in der Grafik Dagger generiert eine factory-type-Klasse, die es verwendet. um Instanzen dieses Typs abzurufen.

Zum Zeitpunkt der Build-Erstellung geht Dagger Ihren Code durch und:

  • Erstellt und validiert Abhängigkeitsdiagramme. Dabei wird sichergestellt, dass:

    • Die Abhängigkeiten jedes Objekts können erfüllt werden, sodass es keine Laufzeit gibt Ausnahmen.
    • Es gibt keine Abhängigkeitszyklen, daher gibt es auch keine Endlosschleifen.
  • Erzeugt die Klassen, die zur Laufzeit zum Erstellen der tatsächlichen Objekte und ihrer Abhängigkeiten verwendet werden.

Ein einfacher Anwendungsfall in Dagger: Factory generieren

Um zu demonstrieren, wie Sie mit Dagger arbeiten können, erstellen wir ein einfaches factory für die Klasse UserRepository fest, die im folgendes Diagramm:

Definieren Sie UserRepository so:

Kotlin

class UserRepository(
    private val localDataSource: UserLocalDataSource,
    private val remoteDataSource: UserRemoteDataSource
) { ... }

Java

public class UserRepository {

    private final UserLocalDataSource userLocalDataSource;
    private final UserRemoteDataSource userRemoteDataSource;

    public UserRepository(UserLocalDataSource userLocalDataSource, UserRemoteDataSource userRemoteDataSource) {
        this.userLocalDataSource = userLocalDataSource;
        this.userRemoteDataSource = userRemoteDataSource;
    }

    ...
}

Fügen Sie dem UserRepository-Konstruktor eine @Inject-Anmerkung hinzu, damit Dagger weiß, So erstellen Sie eine UserRepository:

Kotlin

// @Inject lets Dagger know how to create instances of this object
class UserRepository @Inject constructor(
    private val localDataSource: UserLocalDataSource,
    private val remoteDataSource: UserRemoteDataSource
) { ... }

Java

public class UserRepository {

    private final UserLocalDataSource userLocalDataSource;
    private final UserRemoteDataSource userRemoteDataSource;

    // @Inject lets Dagger know how to create instances of this object
    @Inject
    public UserRepository(UserLocalDataSource userLocalDataSource, UserRemoteDataSource userRemoteDataSource) {
        this.userLocalDataSource = userLocalDataSource;
        this.userRemoteDataSource = userRemoteDataSource;
    }
}

Im obigen Code-Snippet teilen Sie Dagger mit:

  1. So erstellen Sie eine UserRepository-Instanz mit der annotierten @Inject -Konstruktor.

  2. Die Abhängigkeiten sind: UserLocalDataSource und UserRemoteDataSource.

Jetzt weiß Dagger, wie eine Instanz von UserRepository erstellt wird, wie die Abhängigkeiten erstellt werden. Wenn Sie auch die anderen Klassen annotieren, Dagger weiß, wie diese erstellt werden:

Kotlin

// @Inject lets Dagger know how to create instances of these objects
class UserLocalDataSource @Inject constructor() { ... }
class UserRemoteDataSource @Inject constructor() { ... }

Java

public class UserLocalDataSource {
    @Inject
    public UserLocalDataSource() { }
}

public class UserRemoteDataSource {
    @Inject
    public UserRemoteDataSource() { }
}

Dolchkomponenten

Dagger kann ein Diagramm der Abhängigkeiten in Ihrem Projekt erstellen, um herauszufinden, woher diese Abhängigkeiten kommen, wenn sie benötigt werden. Dazu müssen Sie eine Schnittstelle erstellen und mit Anmerkungen versehen @Component Dagger erstellt wie bei der manuellen Erstellung einen Container. Abhängigkeitsinjektion.

Innerhalb der @Component-Schnittstelle können Sie Funktionen definieren, die Instanzen der benötigten Klassen (z.B. UserRepository). @Component teilt Ihnen mit Dagger zum Generieren eines Containers mit allen Abhängigkeiten, die für die Typen angezeigt werden. Dies wird als Dagger-Komponente bezeichnet. es enthält ein Diagramm mit den Objekten, die Dagger weiß, und ihren jeweiligen Abhängigkeiten.

Kotlin

// @Component makes Dagger create a graph of dependencies
@Component
interface ApplicationGraph {
    // The return type  of functions inside the component interface is
    // what can be provided from the container
    fun repository(): UserRepository
}

Java

// @Component makes Dagger create a graph of dependencies
@Component
public interface ApplicationGraph {
    // The return type  of functions inside the component interface is
    // what can be consumed from the graph
    UserRepository userRepository();
}

Beim Erstellen des Projekts generiert Dagger eine Implementierung der ApplicationGraph-Oberfläche für Sie: DaggerApplicationGraph. Mit der erstellt Dagger ein Abhängigkeitsdiagramm, das aus den Beziehungen zwischen den drei Klassen (UserRepository, UserLocalDatasource und UserRemoteDataSource) mit nur einem Einstiegspunkt: Abrufen einer UserRepository-Instanz Sie können es wie folgt verwenden:

Kotlin

// Create an instance of the application graph
val applicationGraph: ApplicationGraph = DaggerApplicationGraph.create()
// Grab an instance of UserRepository from the application graph
val userRepository: UserRepository = applicationGraph.repository()

Java

// Create an instance of the application graph
ApplicationGraph applicationGraph = DaggerApplicationGraph.create();

// Grab an instance of UserRepository from the application graph
UserRepository userRepository = applicationGraph.userRepository();

Dagger erstellt bei jeder Anfrage eine neue Instanz von UserRepository.

Kotlin

val applicationGraph: ApplicationGraph = DaggerApplicationGraph.create()

val userRepository: UserRepository = applicationGraph.repository()
val userRepository2: UserRepository = applicationGraph.repository()

assert(userRepository != userRepository2)

Java

ApplicationGraph applicationGraph = DaggerApplicationGraph.create();

UserRepository userRepository = applicationGraph.userRepository();
UserRepository userRepository2 = applicationGraph.userRepository();

assert(userRepository != userRepository2)

Manchmal benötigen Sie eine eindeutige Instanz einer Abhängigkeit in einem Container. Dies kann verschiedene Gründe haben:

  1. Sie möchten, dass andere Typen, die diesen Typ als Abhängigkeit haben, denselben wie z. B. mehrere ViewModel-Objekte im Anmeldevorgang mit demselben LoginUserData.

  2. Die Erstellung eines Objekts ist teuer und Sie möchten kein neues erstellen -Instanz jedes Mal, wenn sie als Abhängigkeit deklariert wird (z. B. ein JSON-Parser).

In diesem Beispiel benötigen Sie möglicherweise eine eindeutige Instanz von UserRepository. in der Grafik angezeigt. Jedes Mal, wenn Sie nach einem UserRepository fragen, erhalten immer dieselbe Instanz. Dies ist in Ihrem Beispiel nützlich, einer realen Anwendung mit einer komplexeren Anwendungsgrafik, mehrere ViewModel-Objekte abhängig von UserRepository und Sie möchten zum Erstellen neuer Instanzen von UserLocalDataSource und UserRemoteDataSource jedes Mal, wenn UserRepository angegeben werden muss.

Bei der manuellen Abhängigkeitsinjektion übergeben Sie dasselbe Instanz von UserRepository für die Konstruktoren der ViewModel-Klassen; aber Da Sie diesen Code nicht manuell schreiben, Dagger weiß, dass Sie dieselbe Instanz verwenden möchten. Dies kann mithilfe des Umfangs Anmerkungen.

Geltungsbereich mit Dolch

Sie können Bereichsanmerkungen verwenden, um die Lebensdauer eines Objekts auf die Lebensdauer zu begrenzen. seiner Komponente. Dies bedeutet, dass dieselbe Instanz einer Abhängigkeit verwendet wird wenn dieser Typ bereitgestellt werden muss.

Um eine eindeutige Instanz von UserRepository zu haben, wenn Sie nach dem Repository fragen Verwenden Sie in ApplicationGraph dieselbe Bereichsanmerkung für @Component. und UserRepository. Mit der Anmerkung @Singleton können Sie ist bereits im javax.inject-Paket enthalten, das von Dagger verwendet wird:

Kotlin

// Scope annotations on a @Component interface informs Dagger that classes annotated
// with this annotation (i.e. @Singleton) are bound to the life of the graph and so
// the same instance of that type is provided every time the type is requested.
@Singleton
@Component
interface ApplicationGraph {
    fun repository(): UserRepository
}

// Scope this class to a component using @Singleton scope (i.e. ApplicationGraph)
@Singleton
class UserRepository @Inject constructor(
    private val localDataSource: UserLocalDataSource,
    private val remoteDataSource: UserRemoteDataSource
) { ... }

Java

// Scope annotations on a @Component interface informs Dagger that classes annotated
// with this annotation (i.e. @Singleton) are scoped to the graph and the same
// instance of that type is provided every time the type is requested.
@Singleton
@Component
public interface ApplicationGraph {
    UserRepository userRepository();
}

// Scope this class to a component using @Singleton scope (i.e. ApplicationGraph)
@Singleton
public class UserRepository {

    private final UserLocalDataSource userLocalDataSource;
    private final UserRemoteDataSource userRemoteDataSource;

    @Inject
    public UserRepository(UserLocalDataSource userLocalDataSource, UserRemoteDataSource userRemoteDataSource) {
        this.userLocalDataSource = userLocalDataSource;
        this.userRemoteDataSource = userRemoteDataSource;
    }
}

Alternativ können Sie eine benutzerdefinierte Umfangsanmerkung erstellen und verwenden. So erstellen Sie eine Bereichsanmerkung:

Kotlin

// Creates MyCustomScope
@Scope
@MustBeDocumented
@Retention(value = AnnotationRetention.RUNTIME)
annotation class MyCustomScope

Java

// Creates MyCustomScope
@Scope
@Retention(RetentionPolicy.RUNTIME)
public @interface MyCustomScope {}

Anschließend können Sie es wie zuvor verwenden:

Kotlin

@MyCustomScope
@Component
interface ApplicationGraph {
    fun repository(): UserRepository
}

@MyCustomScope
class UserRepository @Inject constructor(
    private val localDataSource: UserLocalDataSource,
    private val service: UserService
) { ... }

Java

@MyCustomScope
@Component
public interface ApplicationGraph {
    UserRepository userRepository();
}

@MyCustomScope
public class UserRepository {

    private final UserLocalDataSource userLocalDataSource;
    private final UserRemoteDataSource userRemoteDataSource;

    @Inject
    public UserRepository(UserLocalDataSource userLocalDataSource, UserRemoteDataSource userRemoteDataSource) {
        this.userLocalDataSource = userLocalDataSource;
        this.userRemoteDataSource = userRemoteDataSource;
    }
}

In beiden Fällen wird dem Objekt derselbe Bereich bereitgestellt, mit dem das Objekt annotiert wird. @Component-Schnittstelle. Daher sollten Sie jedes Mal, wenn Sie applicationGraph.repository(), erhalten Sie dieselbe Instanz von UserRepository.

Kotlin

val applicationGraph: ApplicationGraph = DaggerApplicationGraph.create()

val userRepository: UserRepository = applicationGraph.repository()
val userRepository2: UserRepository = applicationGraph.repository()

assert(userRepository == userRepository2)

Java

ApplicationGraph applicationGraph = DaggerApplicationGraph.create();

UserRepository userRepository = applicationGraph.userRepository();
UserRepository userRepository2 = applicationGraph.userRepository();

assert(userRepository == userRepository2)

Fazit

Es ist wichtig, die Vorteile und die Funktionsweise von Dagger zu kennen. bevor Sie sie in komplizierteren Szenarien einsetzen können.

Auf der nächsten Seite erfahren Sie, wie Sie Dagger einer Android-App hinzufügen.