Dagger'la ilgili temel bilgiler

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:

  1. @Inject ek açıklamalı oluşturucu ile UserRepository örneği oluşturma.

  2. Bağımlılıkları nelerdir? UserLocalDataSource ve UserRemoteDataSource.

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:

  1. 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 fazla ViewModel nesnesi vardır.

  2. 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.