Bir Android uygulamasında manuel bağımlılık yerleştirme veya hizmet bulucular, projenizin boyutuna bağlı olarak sorun yaratabilir. Bağımlılıkları yönetmek için Dagger'ı kullanarak büyüdükçe projenizin karmaşıklığını sınırlayabilirsiniz.
Dagger, normalde elle yazacağınız kodu taklit eden kodu otomatik olarak oluşturur. Kod, derleme sırasında oluşturulduğundan Guice gibi diğer yansıma tabanlı çözümlerden izlenebilir ve daha iyi performans gösterir.
Dagger kullanmanın avantajları
Dagger, şunları yaparak sizi yorucu ve hataya açık standart kod yazmaktan kurtarır:
Manuel DI bölümünde manuel olarak uyguladığınız
AppContainer
kodunu (uygulama grafiği) oluşturma.Uygulama grafiğinde bulunan sınıflar için fabrikalar oluşturma. Bağımlılıklar şirket içinde bu şekilde karşılanır.
Kapsamları kullanarak bir bağımlılığın yeniden kullanılmasıyla mı yoksa yeni bir örnek mi oluşturulacağına karar verme.
Dagger alt bileşenlerini kullanarak önceki bölümde yer alan giriş akışında yaptığınız gibi belirli akışlar için container oluşturma. Bu, artık ihtiyaç duyulmayan nesneleri bellekte serbest bırakarak uygulamanızın performansını artırır.
Bir sınıfın bağımlılıklarını beyan ettiğiniz ve ek açıklamalar kullanarak bunları nasıl karşılayacağınızı belirttiğiniz sürece Dagger, tüm bunları derleme zamanında otomatik olarak yapar. Dagger, manuel olarak yazacağınıza benzer bir kod oluşturur. Dagger dahili olarak, bir sınıf örneği sağlamanın yolunu bulmak için başvurabileceği bir nesneler grafiği oluşturur. Grafikteki her sınıf için Dagger, bu türden örnekleri almak amacıyla dahili olarak kullandığı bir fabrika türü sınıfı oluşturur.
Derleme sırasında Dagger, kodunuzda adım adım ilerler ve:
Bağımlılık grafiklerini oluşturup doğrular ve aşağıdakileri sağlar:
- Her nesnenin bağımlılıkları karşılanabileceği için çalışma zamanı istisnası yoktur.
- Bağımlılık döngüsü yoktur, bu nedenle sonsuz döngü yoktur.
Gerçek nesneleri ve bağımlılıklarını oluşturmak için çalışma zamanında kullanılan sınıfları oluşturur.
Dagger'ın basit kullanım alanı: Fabrika oluşturma
Dagger ile nasıl çalışabileceğinizi göstermek amacıyla aşağıdaki şemada gösterilen UserRepository
sınıfı için basit bir fabrika oluşturalım:
UserRepository
öğesini aşağıdaki gibi tanımlayın:
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; } ... }
Dagger'ın UserRepository
oluşturmayı öğrenmesi için UserRepository
oluşturucuya @Inject
ek açıklaması ekleyin:
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; } }
Yukarıdaki kod snippet'inde Dagger'a şunu bildiriyorsunuz:
@Inject
ek açıklamalı oluşturucu ileUserRepository
örneği oluşturma.Bağımlılıkları nelerdir?
UserLocalDataSource
veUserRemoteDataSource
.
Artık Dagger, UserRepository
örneği oluşturmayı biliyor ancak bağımlılıklarını nasıl oluşturacağınızı bilmiyor. Diğer sınıflara da ek açıklama eklerseniz,
Dagger onları nasıl oluşturacağını bilir:
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() { } }
Hançer bileşenleri
Dagger, projenizdeki bağımlılıkların bir grafiğini oluşturabilir. Bu grafik, ihtiyaç duyulduğunda bu bağımlılıkları nereye götüreceğini bulmak için kullanılabilir.
Dagger'ın bunu yapabilmesi için bir arayüz oluşturmanız ve @Component
ile ek açıklama eklemeniz gerekir. Dagger, manuel bağımlılık ekleme işleminde olduğu gibi bir container oluşturur.
@Component
arayüzünde, ihtiyacınız olan sınıfların örneklerini (ör. UserRepository
) döndüren işlevler tanımlayabilirsiniz. @Component
,Dagger'a sunduğu türleri karşılamak için gereken tüm bağımlılıkları içeren bir kapsayıcı oluşturmasını bildirir. Bu bileşen, Dagger bileşeni olarak adlandırılır. Dagger'ın nasıl sağlayacağını bildiği nesnelerden ve bunların bağımlılıklarından oluşan bir grafik içerir.
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(); }
Projeyi oluştururken Dagger, sizin için ApplicationGraph
arayüzünün bir uygulamasını oluşturur: DaggerApplicationGraph
. Dagger, ek açıklama işlemcisi ile tek bir giriş noktası olan, üç sınıf (UserRepository
, UserLocalDatasource
ve UserRemoteDataSource
) arasındaki ilişkilerden oluşan bir bağımlılık grafiği oluşturur: UserRepository
örneği alma. Bunu aşağıdaki şekilde kullanabilirsiniz:
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, her istendiğinde yeni bir UserRepository
örneği oluşturur.
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)
Bazen bir kapsayıcıda bağımlılığın benzersiz bir örneği olması gerekir. Bu işlemi birkaç nedenden dolayı isteyebilirsiniz:
Bu türe sahip diğer türlerin aynı örneği paylaşmasını istersiniz. Örneğin, giriş akışında aynı
LoginUserData
öğesini kullanan birden fazlaViewModel
nesnesi vardır.Bir nesnenin oluşturulması pahalıdır ve bağımlılık olarak tanımlandığında yeni bir örnek (ör. JSON ayrıştırıcı) oluşturmak istemezsiniz.
Her UserRepository
istediğinizde her zaman aynı örneği elde etmeniz için grafikte benzersiz bir UserRepository
örneğinin bulunmasını isteyebilirsiniz. Bu, örneğinizde faydalıdır. Çünkü daha karmaşık bir uygulama grafiğine sahip gerçek hayattaki bir uygulamada, UserRepository
'a bağlı olarak birden fazla ViewModel
nesneniz olabilir ve UserRepository
her sağlanması gerektiğinde yeni UserLocalDataSource
ve UserRemoteDataSource
örnekleri oluşturmak istemezsiniz.
Manuel bağımlılık yerleştirmede bunu, aynı UserRepository
örneğini ViewModel sınıflarının kurucularına geçirerek yaparsınız. Ancak Dagger'da bu kodu manuel olarak yazmadığınız için Dagger'a aynı örneği kullanmak istediğinizi bildirmeniz gerekir. Bunu, kapsam ek açıklamaları ile yapabilirsiniz.
Hançerle Kapsam Oluşturma
Bir nesnenin ömrünü, bileşeninin ömrüyle sınırlamak için kapsam ek açıklamalarını kullanabilirsiniz. Bu, söz konusu türün her sağlanması gerektiğinde aynı bağımlı örneğinin kullanıldığı anlamına gelir.
ApplicationGraph
içinde kod deposunu istediğinizde benzersiz bir UserRepository
örneğine sahip olmak için @Component
arayüzü ve UserRepository
için aynı kapsam ek açıklamasını kullanın. Dagger'ın kullandığı javax.inject
paketinde mevcut olan @Singleton
ek açıklamasını kullanabilirsiniz:
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; } }
Alternatif olarak, özel bir kapsam ek açıklaması oluşturabilir ve kullanabilirsiniz. Bir kapsam ek açıklamasını aşağıdaki gibi oluşturabilirsiniz:
Kotlin
// Creates MyCustomScope @Scope @MustBeDocumented @Retention(value = AnnotationRetention.RUNTIME) annotation class MyCustomScope
Java
// Creates MyCustomScope @Scope @Retention(RetentionPolicy.RUNTIME) public @interface MyCustomScope {}
Ardından, etiketi önceki gibi kullanabilirsiniz:
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; } }
Her iki durumda da nesneye @Component
arayüzüne açıklama eklemek için kullanılan kapsamla sağlanır. Bu nedenle, her applicationGraph.repository()
çağrısında aynı UserRepository
örneğini alırsınız.
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)
Sonuç
Daha karmaşık senaryolarda kullanmadan önce Dagger'ın avantajlarına ve nasıl çalıştığına dair temel bilgilere sahip olmak önemlidir.
Sonraki sayfada, Dagger'ı bir Android uygulamasına nasıl ekleyeceğinizi öğreneceksiniz.