Podstawowe informacje o sztyletach

Ręczne wstrzykiwanie zależności lub usługa lokalizatory w aplikacji na Androida mogą sprawiać problemy w zależności od rozmiaru w projektach AI. Możesz ograniczyć złożoność projektu w miarę jego skalowania w górę, używając Dagger do zarządzania zależnościami.

Dagger automatycznie generuje kod, który imituje inny kod zostały napisane odręcznie. Ponieważ kod jest generowany podczas kompilacji, można go śledzić i są skuteczniejsze niż inne rozwiązania oparte na odbiciu, takie jak: Guice

Zalety korzystania ze sztyletu

Dagger uwalnia Cię od pisania żmudnego i podatnego na błędy powtarzalnego kodu przez:

  • ręcznie generować kod aplikacji AppContainer (wykres aplikacji), w sekcji ręcznego DI.

  • Tworzenie fabryk dla klas dostępnych na wykresie aplikacji. Ten jest wewnętrzny sposób zaspokojenia zależności.

  • Decyzja o tym, czy ponownie użyć zależności czy utworzyć nową instancję za pomocą za pomocą zakresów.

  • Tworzenie kontenerów dla konkretnych przepływów, tak jak podczas procesu logowania z poprzedniej sekcji, używając podkomponentów Sztyletu. Dzięki temu aplikacja przez zwolnienie obiektów w pamięci, gdy nie są już potrzebne.

Dagger robi to automatycznie w momencie kompilacji, zadeklarować zależności klasy i określić sposób ich spełnienia za pomocą adnotacji. Dagger generuje kod podobny do tego, który należałoby napisać ręcznie. Wewnętrznie Dagger tworzy wykres przedstawiający obiekty, do których może się odwoływać. aby znaleźć sposób na podanie instancji klasy. Dla każdej klasy na wykresie Dagger generuje klasę factory-type, której używa wewnętrznie, aby uzyskać instancje tego typu.

Podczas kompilacji Dagger analizuje kod i:

  • Tworzy i weryfikuje wykresy zależności, dzięki czemu:

    • Można spełnić zależności każdego obiektu, nie ma więc środowiska wykonawczego. wyjątki.
    • Nie istnieją cykle zależności, nie ma więc pętli nieskończonych.
  • Generuje klasy, które są używane w czasie działania do utworzenia rzeczywistych obiektów i ich zależności.

Proste zastosowanie Daggera: generowanie fabryki

Aby zademonstrować, jak pracować z Daggerem, stwórzmy prosty factory dla klasy UserRepository widocznej w ten diagram:

Podaj definicję słowa UserRepository w ten sposób:

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;
    }

    ...
}

Dodaj adnotację @Inject do konstruktora UserRepository, by Dagger o tym wiedział jak utworzyć 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;
    }
}

W tym fragmencie kodu mówisz Daggerowi:

  1. Jak utworzyć instancję UserRepository z adnotacją @Inject za pomocą konstruktora.

  2. Jakie są jego zależności: UserLocalDataSource i UserRemoteDataSource.

Teraz Dagger wie, jak utworzyć instancję UserRepository, ale nie potrafi jak tworzyć zależności. Jeśli dodasz adnotacje do innych zajęć, Dagger wie, jak je stworzyć:

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() { }
}

Komponenty krzyżyka

Dagger może utworzyć graf zależności w projekcie, w którym można określić, gdzie te zależności powinny być w razie potrzeby dostępne. Aby to zrobić, trzeba utworzyć interfejs i dodać do niego adnotacje @Component Dagger tworzy kontener w taki sam sposób jak w przypadku ręcznej konfiguracji wstrzykiwanie zależności.

W interfejsie @Component możesz zdefiniować funkcje, które zwracają instancji potrzebnych klas (np. UserRepository). @Component opowiada Sztylet do wygenerowania kontenera ze wszystkimi zależnościami wymaganymi do spełnienia które udostępnia. Jest to tzw. komponent sztyletu. zawiera grafu złożonego z obiektów, które Dagger wie, jak to zrobić i ich zależności.

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();
}

Podczas tworzenia projektu Dagger generuje implementację interfejsu Interfejs ApplicationGraph dla Ciebie: DaggerApplicationGraph. Dzięki procesora adnotacji, Dagger tworzy graf zależności składający się zależności między 3 klasami (UserRepository, UserLocalDatasource i UserRemoteDataSource) z tylko jednym punktem wejścia: pobieram instancję UserRepository. Możesz go użyć w ten sposób:

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 tworzy nową instancję UserRepository za każdym razem, gdy zostanie wysłane żądanie.

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)

Czasami w kontenerze musi znajdować się unikalna instancja zależności. Może to być przydatne z kilku powodów:

  1. Chcesz, aby inne typy, które korzystają z tego typu jako zależność, współużytkowały ten sam typ np. wiele obiektów ViewModel w procesie logowania korzystających z tego samego LoginUserData

  2. Utworzenie obiektu jest drogie i nie chcesz tworzyć nowego za każdym razem, gdy jest ona zadeklarowana jako zależność (np. parser JSON).

W tym przykładzie możesz potrzebować unikalnej instancji UserRepository na wykresie, dzięki czemu za każdym razem, gdy poprosisz o UserRepository, zawsze pobierać tę samą instancję. Przydaje się to w Twoim przykładzie, ponieważ w prawdziwym życiu z bardziej złożonym wykresem aplikacji, wiele obiektów ViewModel w zależności od argumentu UserRepository i nie chcesz do tworzenia nowych instancji UserLocalDataSource i UserRemoteDataSource za każdym razem, gdy trzeba podać UserRepository.

Przy ręcznym wstrzykiwaniu zależności wykonuje się to, przekazując to samo wystąpienie UserRepository konstruktorom klas ViewModel; ale w Daggerze, ponieważ nie tworzy się go ręcznie, Krzyżyk wie, że chcesz użyć tej samej instancji. Można to zrobić za pomocą zakresu .

Określanie zakresu za pomocą sztyletu

Za pomocą adnotacji dotyczących zakresu możesz ograniczyć czas życia obiektu do jego komponentu. Oznacza to, że używane jest to samo wystąpienie zależności za każdym razem, gdy trzeba podać ten typ.

Aby mieć unikalną instancję UserRepository, gdy prosisz o repozytorium w ApplicationGraph użyj adnotacji zakresu dla pola @Component i UserRepository. Możesz korzystać z adnotacji @Singleton, która zawiera już pakiet javax.inject, którego Dagger używa:

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;
    }
}

Możesz też utworzyć adnotację dotyczącą zakresu niestandardowego i jej używać. Adnotację zakresu możesz utworzyć w następujący sposób:

Kotlin

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

Java

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

Następnie możesz go używać jak wcześniej:

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;
    }
}

W obu przypadkach obiekt otrzymuje ten sam zakres, który jest używany do adnotacji Interfejs @Component. Przy każdym połączeniu applicationGraph.repository(), masz to samo wystąpienie 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)

Podsumowanie

Należy znać zalety Daggera i podstawy jego działania przed użyciem w bardziej skomplikowanych sytuacjach.

Na następnej stronie dowiesz się, jak dodać Dagger do aplikacji na Androida.