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:
Jak utworzyć instancję
UserRepository
z adnotacją@Inject
za pomocą konstruktora.Jakie są jego zależności:
UserLocalDataSource
iUserRemoteDataSource
.
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:
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 samegoLoginUserData
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.