Android uygulamalarında Dagger kullanma

Dagger ile ilgili temel bilgiler sayfasında, Dagger'ın bağımlılığı otomatikleştirmenize nasıl yardımcı olabileceği açıklanmıştı eklemeniz gerekebilir. Dagger ile, uğraşmanıza gerek yok, hataya açık ortak metin kodudur.

En iyi uygulamaların özeti

  • Dagger'a tür eklemek için @Inject ile oluşturucu yerleştirmeyi kullanın grafiği de oluşturabilirsiniz. Farklı olduğunda:
    • Dagger'a bir arayüzde hangi uygulamanın olması gerektiğini bildirmek için @Binds kullanın.
    • Dagger'a projenizin uygun olduğu sınıfları nasıl sunacağını bildirmek için @Provides sahip değildir.
  • Modülleri bir bileşende yalnızca bir kez tanımlamalısınız.
  • Kapsam ek açıklamalarını, ek açıklaması da yer alır. Örnek olarak şunlar verilebilir: @ApplicationScope, @LoggedUserScope, ve @ActivityScope.

Bağımlılık ekleme

Projenizde Dagger kullanmak için bu bağımlılıkları build.gradle dosyanız. Dagger'ın en yeni sürümünü inceleyebilirsiniz.

Kotlin

plugins {
  id 'kotlin-kapt'
}

dependencies {
    implementation 'com.google.dagger:dagger:2.x'
    kapt 'com.google.dagger:dagger-compiler:2.x'
}

Java

dependencies {
    implementation 'com.google.dagger:dagger:2.x'
    annotationProcessor 'com.google.dagger:dagger-compiler:2.x'
}

Android'de Dagger

Şekil 1'deki bağımlılık grafiğini gösteren örnek bir Android uygulamasını düşünün.

LoginActivity, UserRepository'ye bağlı olan LoginViewModel'e bağlıdır.
  UserLocalDataSource ve UserRemoteDataSource'a bağlıdır. Bu da
  retrospektife bağlıdır.

Şekil 1. Örneğin bağımlılık grafiği kod

Android'de genellikle uygulamanızda bulunan bir Dagger grafiği sınıfına girersiniz çünkü grafiğin bir örneğinin hafızada kalmasını istiyorsanız görebilirsiniz. Grafik, bu şekilde uygulama yaşam döngüsüne eklenir. Bazılarında durumlarda, uygulama bağlamını ayrıca grafiğe dönüştürülebilir. Bunun için, grafiğin Google Görüntülü Reklam Ağı'nda Application sınıfı. Bunun bir avantajı, yaklaşım, grafiğin diğer Android çerçeve sınıflarında da kullanılabilmesidir. Ayrıca, özel bir URL parametresi kullanmanıza olanak tanıyarak test sürecini Testlerde Application sınıf var.

Grafiği oluşturan arayüz @Component ile belirtildiği için, onu ApplicationComponent veya ApplicationGraph olarak adlandırabilirsiniz. Genellikle söz konusu bileşenin özel Application sınıfınızda bir örneğini çağırın aşağıdaki kodda gösterildiği gibi uygulama grafiğine her ihtiyacınız olduğunda snippet:

Kotlin

// Definition of the Application graph
@Component
interface ApplicationComponent { ... }

// appComponent lives in the Application class to share its lifecycle
class MyApplication: Application() {
    // Reference to the application graph that is used across the whole app
    val appComponent = DaggerApplicationComponent.create()
}

Java

// Definition of the Application graph
@Component
public interface ApplicationComponent {
}

// appComponent lives in the Application class to share its lifecycle
public class MyApplication extends Application {

    // Reference to the application graph that is used across the whole app
    ApplicationComponent appComponent = DaggerApplicationComponent.create();
}

Çünkü etkinlikler ve parçaları gibi belirli Android çerçeve sınıfları için örneklendirildiğinde, Dagger bunları sizin için oluşturamaz. Etkinlikler için tüm başlatma kodlarının onCreate() yöntemine uygulanması gerekir. Bu, @Inject ek açıklamasını sınıfını (oluşturucu ekleme) çağırın. Bunun yerine alan yerleştirmeyi kullanmanız gerekir.

onCreate() içinde bir etkinliğin gerektirdiği bağımlılıkları oluşturmak yerine yöntemini kullanıyorsanız Dagger'ın bu bağımlılıkları sizin için doldurmasını istiyorsunuz. Alan için ekleme işlemi gerçekleştirdiğinizde, @Inject ek açıklamasını, eklediğiniz alanlara uygularsınız. Dagger grafiğinden almak isteyebilirsiniz.

Kotlin

class LoginActivity: Activity() {
    // You want Dagger to provide an instance of LoginViewModel from the graph
    @Inject lateinit var loginViewModel: LoginViewModel
}

Java

public class LoginActivity extends Activity {

    // You want Dagger to provide an instance of LoginViewModel from the graph
    @Inject
    LoginViewModel loginViewModel;
}

Basitlik sağlaması açısından LoginViewModel, bir Android Mimari Bileşenleri değildir ViewModel; ViewModel olarak çalışan normal bir sınıftır. Bu sınıfların nasıl yerleştirileceği hakkında daha fazla bilgi için koda göz atın resmi Android Blueprints Dagger uygulamasındaki dev-dagger dalını tıklayın.

Dagger'da dikkat edilmesi gereken noktalardan biri, yerleştirilen alanların gizli olamayacağıdır. Önceki kodda olduğu gibi en azından paket içinde gizli görünürlüklerinin olması gerekir.

Etkinlikler ekleniyor

Dagger'ın LoginActivity için grafiğe erişmesi gerektiğini bilmesi gerekiyor. gereken ViewModel değerini sağlayın. Dagger ile ilgili temel bilgiler sayfasında, @Component arayüzüyle grafikten nesne elde edin döndürerek, grafiğe dönüştürülebilir. Bu durumda, Dagger'a bir nesneyi (LoginActivity bu durumda) çalışmaya devam eder. Bunun için de yerleştirme isteğinde bulunan nesneyi parametre olarak alan bir işlev.

Kotlin

@Component
interface ApplicationComponent {
    // This tells Dagger that LoginActivity requests injection so the graph needs to
    // satisfy all the dependencies of the fields that LoginActivity is requesting.
    fun inject(activity: LoginActivity)
}

Java

@Component
public interface ApplicationComponent {
    // This tells Dagger that LoginActivity requests injection so the graph needs to
    // satisfy all the dependencies of the fields that LoginActivity is injecting.
    void inject(LoginActivity loginActivity);
}

Bu fonksiyon Dagger'a LoginActivity ürününün grafiğe erişmek istediğini ve yerleştirme isteğinde bulunur. Dagger’ın, projenin başarıya ulaşması için LoginActivity gerektirir (kendi bağımlılıkları olan LoginViewModel). Ekleme isteğinde bulunan birden fazla sınıfınız varsa özel olarak bunların tümünü bileşende tam olarak tanımlayan bir sayı girin. Örneğin yeni bir web sitesi için LoginActivity ve RegistrationActivity ekleme isteğinde bulunuyorsa iki Her iki durumu da kapsayan genel bir yöntem yerine inject() yöntemleri. Genel inject() yöntemi, Dagger'a ne sağlanması gerektiğini söylemez. İşlevler herhangi bir adı olabilir, ancak bu ada inject() parametre olarak eklenecek nesneyi Dagger'daki bir kural olarak kabul ediyoruz.

Etkinliğe nesne eklemek için şurada tanımlanan appComponent parametresini kullanırsınız: Application sınıfınızı kullanıp inject() yöntemini çağırarak bir örneği ileterek etkinliğinden birini seçin.

Etkinlikleri kullanırken Dagger'ı Sorunları önlemek için super.onCreate() çağrısından önce etkinliğin onCreate() yöntemi parça geri yükleme ile yapılır. super.onCreate() ayındaki geri yükleme aşamasında, Bir etkinlik, etkinlik bağlamalarına erişmek isteyebilecek parçaları ekler.

Parçaları kullanırken Dagger'ı parçanın onAttach() bölümüne ekleyin yöntemidir. Bu durumda, super.onAttach() çağrılmadan önce veya sonra yapılabilir.

Kotlin

class LoginActivity: Activity() {
    // You want Dagger to provide an instance of LoginViewModel from the graph
    @Inject lateinit var loginViewModel: LoginViewModel

    override fun onCreate(savedInstanceState: Bundle?) {
        // Make Dagger instantiate @Inject fields in LoginActivity
        (applicationContext as MyApplication).appComponent.inject(this)
        // Now loginViewModel is available

        super.onCreate(savedInstanceState)
    }
}

// @Inject tells Dagger how to create instances of LoginViewModel
class LoginViewModel @Inject constructor(
    private val userRepository: UserRepository
) { ... }

Java

public class LoginActivity extends Activity {

    // You want Dagger to provide an instance of LoginViewModel from the graph
    @Inject
    LoginViewModel loginViewModel;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        // Make Dagger instantiate @Inject fields in LoginActivity
        ((MyApplication) getApplicationContext()).appComponent.inject(this);
        // Now loginViewModel is available

        super.onCreate(savedInstanceState);
    }
}

public class LoginViewModel {

    private final UserRepository userRepository;

    // @Inject tells Dagger how to create instances of LoginViewModel
    @Inject
    public LoginViewModel(UserRepository userRepository) {
        this.userRepository = userRepository;
    }
}

Dagger’a, derlemek için diğer bağımlılıkları nasıl sağlayacağını söyleyelim grafik:

Kotlin

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

class UserLocalDataSource @Inject constructor() { ... }
class UserRemoteDataSource @Inject constructor(
    private val loginService: LoginRetrofitService
) { ... }

Java

public class UserRepository {

    private final UserLocalDataSource userLocalDataSource;
    private final UserRemoteDataSource userRemoteDataSource;

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

public class UserLocalDataSource {

    @Inject
    public UserLocalDataSource() {}
}

public class UserRemoteDataSource {

    private final LoginRetrofitService loginRetrofitService;

    @Inject
    public UserRemoteDataSource(LoginRetrofitService loginRetrofitService) {
        this.loginRetrofitService = loginRetrofitService;
    }
}

Dagger modülleri

Bu örnekte, Retrofit ağ iletişimi kitaplığını kullanıyorsunuzdur. UserRemoteDataSource, LoginRetrofitService bağımlılığı içeriyor. Ancak, LoginRetrofitService örneği oluşturma yöntemi, yapmaktan kaçınmalısınız. Bu bir sınıf örneği değildir; bu hatanın sonucu yapılandırmak için Retrofit.Builder() çağrısını yapmak ve farklı parametreler belirtmek giriş hizmeti.

Dagger'a @Inject ek açıklaması dışında başka bir yöntem de bir sınıfın örneğini sunar: Dagger modüllerinin içindeki bilgiler. Bir Hançer modülü, @Module ek açıklamasına sahip bir sınıftır. Bu bölümde projenizin bağımlılıklarını @Provides ek açıklamasıyla gösterir.

Kotlin

// @Module informs Dagger that this class is a Dagger Module
@Module
class NetworkModule {

    // @Provides tell Dagger how to create instances of the type that this function
    // returns (i.e. LoginRetrofitService).
    // Function parameters are the dependencies of this type.
    @Provides
    fun provideLoginRetrofitService(): LoginRetrofitService {
        // Whenever Dagger needs to provide an instance of type LoginRetrofitService,
        // this code (the one inside the @Provides method) is run.
        return Retrofit.Builder()
                .baseUrl("https://example.com")
                .build()
                .create(LoginService::class.java)
    }
}

Java

// @Module informs Dagger that this class is a Dagger Module
@Module
public class NetworkModule {

    // @Provides tell Dagger how to create instances of the type that this function
    // returns (i.e. LoginRetrofitService).
    // Function parameters are the dependencies of this type.
    @Provides
    public LoginRetrofitService provideLoginRetrofitService() {
        // Whenever Dagger needs to provide an instance of type LoginRetrofitService,
        // this code (the one inside the @Provides method) is run.
        return new Retrofit.Builder()
                .baseUrl("https://example.com")
                .build()
                .create(LoginService.class);
    }
}

Modüller, başarılı bir sistem sağlamak için nesneler'i tıklayın. Gördüğünüz gibi, mantığı gruplandırmak için NetworkModule sınıfını çağırdınız. ağ iletişimi ile ilgili nesneler sağlar. Uygulama genişletilirse buraya nasıl OkHttpClient sağlayabileceğinizi veya Gson veya Moshi'yi yapılandırın.

Bir @Provides yönteminin bağımlılıkları, bu yöntemin parametreleridir. Örneğin, önceki yöntemde, LoginRetrofitService bağımlılık olmadan sağlanabilir çünkü yöntemde parametre yoktur. OkHttpClient parametresi için Dagger'ınOkHttpClient grafiklerini kullanarak LoginRetrofitService bağımlılıklarını karşılamaya çalışın. Örnek:

Kotlin

@Module
class NetworkModule {
    // Hypothetical dependency on LoginRetrofitService
    @Provides
    fun provideLoginRetrofitService(
        okHttpClient: OkHttpClient
    ): LoginRetrofitService { ... }
}

Java

@Module
public class NetworkModule {

    @Provides
    public LoginRetrofitService provideLoginRetrofitService(OkHttpClient okHttpClient) {
        ...
    }
}

Dagger grafiğinin bu modül hakkında bilgi sahibi olması için, modülü @Component arayüzü aşağıdaki gibidir:

Kotlin

// The "modules" attribute in the @Component annotation tells Dagger what Modules
// to include when building the graph
@Component(modules = [NetworkModule::class])
interface ApplicationComponent {
    ...
}

Java

// The "modules" attribute in the @Component annotation tells Dagger what Modules
// to include when building the graph
@Component(modules = NetworkModule.class)
public interface ApplicationComponent {
    ...
}

Dagger grafiğine tür eklemek için önerilen yöntem, oluşturucu kullanmaktır ekleme (ör. sınıfın oluşturucusundaki @Inject ek açıklaması ile). Bazen bu mümkün olmaz ve Dagger modüllerini kullanmanız gerekir. Bir örnek Dagger'ın bir hesaplamanın sonucunu kullanarak bu işlemi nasıl bir nesnenin örneğini oluşturabilirsiniz. Bir örnek sunması gerektiğinde Dagger, kodu @Provides yönteminin içinde çalıştırır.

Örnekteki Dagger grafiğinin şu anda görünümü aşağıdaki gibidir:

LoginActivity bağımlılık grafiğinin şeması

Şekil 2. Grafiğin gösterimi Dagger tarafından enjekte edilen LoginActivity

Grafiğin giriş noktası LoginActivity. Çünkü LoginActivity, LoginViewModel, Dagger örnek sağlama konusunda bilgi sahibi bir grafik derler LoginViewModel ve yinelemeli olarak. Dagger nasıl yapılacağını biliyor bunu, sınıfların sayfalarındaki @Inject ek açıklaması nedeniyle yapabilir kurucusu.

Dagger tarafından oluşturulan ApplicationComponent içinde bir fabrika türü var. yöntemini kullanır. Burada Örneğin, Dagger delegelerini NetworkModule LoginRetrofitService öğesinin bir örneğini almak için ApplicationComponent.

Hançer kapsamları

Dagger ile ilgili temel bilgiler sayfasında, bir bileşenin benzersiz örneğidir. Bu, Google Ads'in bileşenin yaşam döngüsü boyunca bir türün kapsamını belirleme.

UserRepository uygulamasını uygulamanın diğer özelliklerinde kullanmak ve her ihtiyaç duyduğunuzda yeni bir nesne oluşturmak istemeyebilir, bu nesneleri tüm uygulamanın benzersiz bir örneği olarak görür. Aynısı LoginRetrofitService: Oluşturması pahalı olabilir ve benzersiz bir örneği olması gerekir. Şunun bir örneği oluşturuluyor: UserRemoteDataSource o kadar pahalı değildir. Bu nedenle, bileşenin yaşam döngüsüne gerek yoktur.

@Singleton javax.inject paketi ApplicationComponent uygulamasına not eklemek için bunu kullanabilirsiniz ve uygulamanın tamamında yeniden kullanmak istediğiniz nesneleri gösterir.

Kotlin

@Singleton
@Component(modules = [NetworkModule::class])
interface ApplicationComponent {
    fun inject(activity: LoginActivity)
}

@Singleton
class UserRepository @Inject constructor(
    private val localDataSource: UserLocalDataSource,
    private val remoteDataSource: UserRemoteDataSource
) { ... }

@Module
class NetworkModule {
    // Way to scope types inside a Dagger Module
    @Singleton
    @Provides
    fun provideLoginRetrofitService(): LoginRetrofitService { ... }
}

Java

@Singleton
@Component(modules = NetworkModule.class)
public interface ApplicationComponent {
    void inject(LoginActivity loginActivity);
}

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

@Module
public class NetworkModule {

    @Singleton
    @Provides
    public LoginRetrofitService provideLoginRetrofitService() { ... }
}

Nesnelere kapsam uygularken bellek sızıntısına yol açmamaya dikkat edin. Farklı oluşturulan nesne bellekte, kapsama alınan bileşen bellekte olduğu sürece çok önemlidir. Çünkü ApplicationComponent, uygulama başlatıldığında ( Application sınıfı) kullanan eski sürümler, uygulama imha edildiğinde kaldırılır. Dolayısıyla, benzersiz bir UserRepository örneği, uygulama kaldırılır.

Dagger alt bileşenleri

Giriş akışınız (tek bir LoginActivity tarafından yönetilen) birden fazla öğeden oluşuyorsa parçalarının hepsinde aynı LoginViewModel örneğini yeniden kullanmanız gerekir. parçalar. @Singleton, örneği yeniden kullanmak için LoginViewModel öğesine ek açıklama ekleyemez şu nedenlerle:

  1. Akış sonrasında LoginViewModel örneği bellekte kalmaya devam eder tamamlandı.

  2. Her giriş akışı için farklı bir LoginViewModel örneği istiyorsunuz. Örneğin, kullanıcı çıkış yaparsa farklı bir LoginViewModel, kullanıcının giriş yaptığı örnekle aynı değildir. ilk kez.

LoginViewModel kapsamını LoginActivity yaşam döngüsüne uygulamak için Giriş akışı için yeni bir bileşen (yeni bir alt grafik) ve yeni bir kapsam.

Şimdi giriş akışına özel bir grafik oluşturalım.

Kotlin

@Component
interface LoginComponent {}

Java

@Component
public interface LoginComponent {
}

Şu anda LoginActivity, LoginComponent kaynağından enjeksiyon almalı çünkü bu özel bir yapılandırmaya sahip. Böylece, dosya ekleme sorumluluğunu ApplicationComponent sınıfından LoginActivity.

Kotlin

@Component
interface LoginComponent {
    fun inject(activity: LoginActivity)
}

Java

@Component
public interface LoginComponent {
    void inject(LoginActivity loginActivity);
}

LoginComponent, ApplicationComponent üzerinden nesnelere erişebilmelidir çünkü LoginViewModel, UserRepository metriğine bağlı. Dagger'a şunu söylemenin yolu: bir başka bileşen kullanmak istiyorsanız Dagger alt bileşenleri. Yeni bileşen, bileşeninde değişiklik yapmadığından emin olun.

Alt bileşenler, bir bileşenin nesne grafiğini devralan ve genişleten bileşenlerdir. üst bileşen. Dolayısıyla, üst bileşende sağlanan tüm nesneler sağlanan değerlerdir. Bu şekilde, alt bileşendeki bir nesne üst bileşen tarafından sağlanan bir nesneye bağlı olabilir.

Alt bileşen örnekleri oluşturmak için üst bileşen örneği gerekir bir bileşenidir. Dolayısıyla, üst bileşen tarafından alt bileşenler üst bileşen kapsamında olmaya devam eder.

Bu örnekte, LoginComponent öğesini şu öğenin alt bileşeni olarak tanımlamanız gerekir: ApplicationComponent. Bunun için LoginComponent öğesine şununla not ekleyin: @Subcomponent:

Kotlin

// @Subcomponent annotation informs Dagger this interface is a Dagger Subcomponent
@Subcomponent
interface LoginComponent {

    // This tells Dagger that LoginActivity requests injection from LoginComponent
    // so that this subcomponent graph needs to satisfy all the dependencies of the
    // fields that LoginActivity is injecting
    fun inject(loginActivity: LoginActivity)
}

Java

// @Subcomponent annotation informs Dagger this interface is a Dagger Subcomponent
@Subcomponent
public interface LoginComponent {

    // This tells Dagger that LoginActivity requests injection from LoginComponent
    // so that this subcomponent graph needs to satisfy all the dependencies of the
    // fields that LoginActivity is injecting
    void inject(LoginActivity loginActivity);
}

Ayrıca LoginComponent içinde bir alt bileşen fabrikası tanımlamanız gerekir. ApplicationComponent, LoginComponent örneği oluşturmayı biliyor.

Kotlin

@Subcomponent
interface LoginComponent {

    // Factory that is used to create instances of this subcomponent
    @Subcomponent.Factory
    interface Factory {
        fun create(): LoginComponent
    }

    fun inject(loginActivity: LoginActivity)
}

Java

@Subcomponent
public interface LoginComponent {

    // Factory that is used to create instances of this subcomponent
    @Subcomponent.Factory
    interface Factory {
        LoginComponent create();
    }

    void inject(LoginActivity loginActivity);
}

Dagger'a LoginComponent öğesinin, şu öğenin bir alt bileşeni olduğunu bildirmek için: ApplicationComponent, bunu şu şekilde belirtmeniz gerekir:

  1. Yeni bir Dagger modülü (ör. SubcomponentsModule) oluşturarak alt bileşenin sınıfını ek açıklamanın subcomponents özelliğine bağlayabilirsiniz.

    Kotlin

    // The "subcomponents" attribute in the @Module annotation tells Dagger what
    // Subcomponents are children of the Component this module is included in.
    @Module(subcomponents = LoginComponent::class)
    class SubcomponentsModule {}
    

    Java

    // The "subcomponents" attribute in the @Module annotation tells Dagger what
    // Subcomponents are children of the Component this module is included in.
    @Module(subcomponents = LoginComponent.class)
    public class SubcomponentsModule {
    }
    
  2. Yeni modülün (ör. SubcomponentsModule) ApplicationComponent ürününe eklenmesi:

    Kotlin

    // Including SubcomponentsModule, tell ApplicationComponent that
    // LoginComponent is its subcomponent.
    @Singleton
    @Component(modules = [NetworkModule::class, SubcomponentsModule::class])
    interface ApplicationComponent {
    }
    

    Java

    // Including SubcomponentsModule, tell ApplicationComponent that
    // LoginComponent is its subcomponent.
    @Singleton
    @Component(modules = {NetworkModule.class, SubcomponentsModule.class})
    public interface ApplicationComponent {
    }
    

    ApplicationComponent uygulamasının artık LoginActivity öğesini yerleştirmesine gerek olmadığını unutmayın Çünkü bu sorumluluk artık LoginComponent hesabına aittir, dolayısıyla şunları kaldırabilirsiniz: ApplicationComponent tarafından sağlanan inject() yöntemi.

    ApplicationComponent tüketicilerinin, LoginComponent. Üst bileşenin Tüketiciler, üst bileşen:

  3. LoginComponent örnekleri oluşturan fabrikayı şurada kullanıma sunun: arayüz:

    Kotlin

    @Singleton
    @Component(modules = [NetworkModule::class, SubcomponentsModule::class])
    interface ApplicationComponent {
    // This function exposes the LoginComponent Factory out of the graph so consumers
    // can use it to obtain new instances of LoginComponent
    fun loginComponent(): LoginComponent.Factory
    }
    

    Java

    @Singleton
    @Component(modules = { NetworkModule.class, SubcomponentsModule.class} )
    public interface ApplicationComponent {
    // This function exposes the LoginComponent Factory out of the graph so consumers
    // can use it to obtain new instances of LoginComponent
    LoginComponent.Factory loginComponent();
    }
    

Alt bileşenlere kapsam atama

Projeyi derlerseniz hem ApplicationComponent hem de örnekler oluşturabilirsiniz ve LoginComponent. ApplicationComponent, daha uzun süre ve yine aynı şekilde grafiğin aynısını kullanmak uygulama bellektedir.

LoginComponent yaşam döngüsü nedir? Proje yöneticisi olmaktan LoginComponent, aynı Girişle ilgili parçalar arasında LoginViewModel. Ama aynı zamanda farklı yeni bir giriş akışı olduğunda LoginViewModel örneklerini görebilirsiniz. LoginActivity. LoginComponent için doğru ömür boyudur: Her yeni etkinlik için yeni bir LoginComponent örneği ve bu örneği kullanabilen parçalar LoginComponent.

LoginComponent, LoginActivity yaşam döngüsüne bağlı olduğu için şunları yapmanız gerekir: etkinlikteki bileşene, Application sınıfındaki applicationComponent öğesine başvuru. Bu şekilde ekip emin olun.

Kotlin

class LoginActivity: Activity() {
    // Reference to the Login graph
    lateinit var loginComponent: LoginComponent
    ...
}

Java

public class LoginActivity extends Activity {

    // Reference to the Login graph
    LoginComponent loginComponent;

    ...
}

loginComponent değişkeninde @Inject ek açıklaması bulunmadığına dikkat edin çünkü söz konusu değişkenin Dagger tarafından sağlanmasını beklemezsiniz.

LoginComponent referansı almak için ApplicationComponent kullanabilirsiniz ve ardından LoginActivity öğesini şu şekilde ekleyin:

Kotlin

class LoginActivity: Activity() {
    // Reference to the Login graph
    lateinit var loginComponent: LoginComponent

    // Fields that need to be injected by the login graph
    @Inject lateinit var loginViewModel: LoginViewModel

    override fun onCreate(savedInstanceState: Bundle?) {
        // Creation of the login graph using the application graph
        loginComponent = (applicationContext as MyDaggerApplication)
                            .appComponent.loginComponent().create()

        // Make Dagger instantiate @Inject fields in LoginActivity
        loginComponent.inject(this)

        // Now loginViewModel is available

        super.onCreate(savedInstanceState)
    }
}

Java

public class LoginActivity extends Activity {

    // Reference to the Login graph
    LoginComponent loginComponent;

    // Fields that need to be injected by the login graph
    @Inject
    LoginViewModel loginViewModel;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        // Creation of the login graph using the application graph
        loginComponent = ((MyApplication) getApplicationContext())
                                .appComponent.loginComponent().create();

        // Make Dagger instantiate @Inject fields in LoginActivity
        loginComponent.inject(this);

        // Now loginViewModel is available

        super.onCreate(savedInstanceState);
    }
}

LoginComponent, etkinliğin onCreate() yönteminde oluşturulur ve şunu alır: etkinlik imha edildiğinde dolaylı olarak imha edilir.

LoginComponent her zaman aynı LoginViewModel örneğini sağlamalıdır geri bildirimde bulunuyor. Bunu sağlamak için özel bir ek açıklama ve hem LoginComponent hem de LoginViewModel için not eklemelidir. Not Daha önce kullanıldığı için @Singleton ek açıklamasını kullanamazsınız tarafından yapılandırıldığından ve nesneyi tek bir uygulama tekilleştiren bir nesne oluşturur. (uygulamanın tamamı için benzersiz bir örnek). Farklı bir ek açıklama oluşturmanız gerekiyor. kapsam.

Bu durumda, bu kapsamı @LoginScope olarak adlandırabilirdiniz ancak bu iyi bir fikir değil alıştırma yapmak. Kapsam ek açıklamasının adı, kullanım amacına açık olmamalıdır yerine getirdiği önemli bir konudur. Bunun yerine kullanım süresine bağlı olarak adlandırılmalıdır. ek açıklamalar, RegistrationComponent gibi kardeş bileşenler tarafından yeniden kullanılabilir ve SettingsComponent. Bu nedenle, bu adı @ActivityScope olarak ayarlamalısınız. / @LoginScope.

Kotlin

// Definition of a custom scope called ActivityScope
@Scope
@Retention(value = AnnotationRetention.RUNTIME)
annotation class ActivityScope

// Classes annotated with @ActivityScope are scoped to the graph and the same
// instance of that type is provided every time the type is requested.
@ActivityScope
@Subcomponent
interface LoginComponent { ... }

// A unique instance of LoginViewModel is provided in Components
// annotated with @ActivityScope
@ActivityScope
class LoginViewModel @Inject constructor(
    private val userRepository: UserRepository
) { ... }

Java

// Definition of a custom scope called ActivityScope
@Scope
@Retention(RetentionPolicy.RUNTIME)
public @interface ActivityScope {}

// Classes annotated with @ActivityScope are scoped to the graph and the same
// instance of that type is provided every time the type is requested.
@ActivityScope
@Subcomponent
public interface LoginComponent { ... }

// A unique instance of LoginViewModel is provided in Components
// annotated with @ActivityScope
@ActivityScope
public class LoginViewModel {

    private final UserRepository userRepository;

    @Inject
    public LoginViewModel(UserRepository userRepository) {
        this.userRepository = userRepository;
    }
}

LoginViewModel gerektiren iki parçanız varsa bunların her ikisi de aynı örnekle sağlanır. Örneğin, Enjekte edilmesi için gereken LoginUsernameFragment ve LoginPasswordFragment LoginComponent tarafından:

Kotlin

@ActivityScope
@Subcomponent
interface LoginComponent {

    @Subcomponent.Factory
    interface Factory {
        fun create(): LoginComponent
    }

    // All LoginActivity, LoginUsernameFragment and LoginPasswordFragment
    // request injection from LoginComponent. The graph needs to satisfy
    // all the dependencies of the fields those classes are injecting
    fun inject(loginActivity: LoginActivity)
    fun inject(usernameFragment: LoginUsernameFragment)
    fun inject(passwordFragment: LoginPasswordFragment)
}

Java

@ActivityScope
@Subcomponent
public interface LoginComponent {

    @Subcomponent.Factory
    interface Factory {
        LoginComponent create();
    }

    // All LoginActivity, LoginUsernameFragment and LoginPasswordFragment
    // request injection from LoginComponent. The graph needs to satisfy
    // all the dependencies of the fields those classes are injecting
    void inject(LoginActivity loginActivity);
    void inject(LoginUsernameFragment loginUsernameFragment);
    void inject(LoginPasswordFragment loginPasswordFragment);
}

Bileşenler, LoginActivity nesne algılandı. LoginUserNameFragment için örnek kod şu kod snippet'ini kullanabilirsiniz:

Kotlin

class LoginUsernameFragment: Fragment() {

    // Fields that need to be injected by the login graph
    @Inject lateinit var loginViewModel: LoginViewModel

    override fun onAttach(context: Context) {
        super.onAttach(context)

        // Obtaining the login graph from LoginActivity and instantiate
        // the @Inject fields with objects from the graph
        (activity as LoginActivity).loginComponent.inject(this)

        // Now you can access loginViewModel here and onCreateView too
        // (shared instance with the Activity and the other Fragment)
    }
}

Java

public class LoginUsernameFragment extends Fragment {

    // Fields that need to be injected by the login graph
    @Inject
    LoginViewModel loginViewModel;

    @Override
    public void onAttach(Context context) {
        super.onAttach(context);

        // Obtaining the login graph from LoginActivity and instantiate
        // the @Inject fields with objects from the graph
        ((LoginActivity) getActivity()).loginComponent.inject(this);

        // Now you can access loginViewModel here and onCreateView too
        // (shared instance with the Activity and the other Fragment)
    }
}

Aynısı LoginPasswordFragment için de geçerli:

Kotlin

class LoginPasswordFragment: Fragment() {

    // Fields that need to be injected by the login graph
    @Inject lateinit var loginViewModel: LoginViewModel

    override fun onAttach(context: Context) {
        super.onAttach(context)

        (activity as LoginActivity).loginComponent.inject(this)

        // Now you can access loginViewModel here and onCreateView too
        // (shared instance with the Activity and the other Fragment)
    }
}

Java

public class LoginPasswordFragment extends Fragment {

    // Fields that need to be injected by the login graph
    @Inject
    LoginViewModel loginViewModel;

    @Override
    public void onAttach(Context context) {
        super.onAttach(context);

        ((LoginActivity) getActivity()).loginComponent.inject(this);

        // Now you can access loginViewModel here and onCreateView too
        // (shared instance with the Activity and the other Fragment)
    }
}

Şekil 3'te Dagger grafiğinin yeni alt bileşenle nasıl göründüğü gösterilmektedir. Sınıflar beyaz noktalı (UserRepository, LoginRetrofitService ve LoginViewModel) ilgili bileşenlerine göre ayarlanmış benzersiz bir örneğe sahip olanlardır.

Son alt bileşen eklendikten sonra uygulama grafiği

Şekil 3. Oluşturduğunuz grafiğin gösterimi Android uygulaması örneğine bakalım.

Grafiğin bölümlerini ayrıntılı olarak inceleyelim:

  1. NetworkModule (ve dolayısıyla LoginRetrofitService) dahil edilir ApplicationComponent içinde, bileşende belirttiğiniz için geçerli.

  2. UserRepository öğesi, kapsamında olduğu için ApplicationComponent içinde kalır ApplicationComponent. Proje büyüdükçe projenin Örneğin, Kayıt gibi farklı özelliklerde kullanabilirsiniz.

    UserRepository, ApplicationComponent kapsamında olduğu için bağımlılıkları (ör. UserLocalDataSource ve UserRemoteDataSource) burada olmalıdır bileşenini de ziyaret edebilirsiniz.UserRepository

  3. LoginViewModel, yalnızca gerekli olduğu için LoginComponent bölümüne dahil edildi LoginComponent tarafından yerleştirilen sınıflar tarafından. LoginViewModel dahil değildir ApplicationComponent ihtiyacında bağımlılık olmadığından ApplicationComponent LoginViewModel

    Benzer şekilde, UserRepository öğesini ApplicationComponent olarak ayarlamamış olsaydınız Dagger, UserRepository ve bağımlılıklarını otomatik olarak dahil ederdi şu anda tek yer bu olduğu için LoginComponent kapsamında UserRepository kullanılıyor.

Nesnelerin farklı bir yaşam döngüsünü kapsamanın dışında alt bileşenler oluşturmak, uygulamanızın farklı bölümlerini özetlemek için iyi bir uygulama bir şeyler öğrenmeye çalışır.

Uygulamanızı akışa bağlı olarak farklı Dagger alt grafikleri oluşturacak şekilde yapılandırma yardımcı olan daha yüksek performanslı ve ölçeklenebilir bir uygulama başlatma süresi açısından önemlidir.

Dagger grafiği oluştururken en iyi uygulamalar

Uygulamanız için Dagger grafiğini oluştururken:

  • Bir bileşen oluştururken, bu bileşenden hangi öğenin sorumlu olduğunu o bileşenin ömrü boyunca kullanabilirsiniz. Bu örnekte, Application sınıfı ApplicationComponent ve LoginActivity kontrolünden sorumlu LoginComponent.

  • Kapsam oluşturmayı yalnızca mantıklı olduğunda kullanın. Kapsam oluşturmanın aşırı kullanılması, uygulamanın çalışma zamanı performansını etkiler: nesne kullanıldığı süre boyunca daha pahalı olacaktır. Dagger, nesneyi sağladığında, DoubleCheck kilitlenmeyi kullanır. tedarikçi firma.

Dagger kullanan bir projeyi test etme

Dagger gibi bağımlılık yerleştirme çerçeveleri kullanmanın avantajlarından biri de kodunuzu test etmeyi kolaylaştırır.

Birim testleri

Birim testleri için Dagger'ı kullanmanız gerekmez. Örneğin, oluşturucu yerleştirmeyi seçerseniz bu sınıfı örneklendirmek için Dagger kullanmanız gerekmez. Kurucusunu doğrudan ya da sahte bağımlılıklar geçirerek çağırabilirsiniz ve bunlar gibi doğrudan ek açıklamalar ekleyebilirsiniz.

Örneğin, LoginViewModel test edilirken:

Kotlin

@ActivityScope
class LoginViewModel @Inject constructor(
    private val userRepository: UserRepository
) { ... }

class LoginViewModelTest {

    @Test
    fun `Happy path`() {
        // You don't need Dagger to create an instance of LoginViewModel
        // You can pass a fake or mock UserRepository
        val viewModel = LoginViewModel(fakeUserRepository)
        assertEquals(...)
    }
}

Java

@ActivityScope
public class LoginViewModel {

    private final UserRepository userRepository;

    @Inject
    public LoginViewModel(UserRepository userRepository) {
        this.userRepository = userRepository;
    }
}

public class LoginViewModelTest {

    @Test
    public void happyPath() {
        // You don't need Dagger to create an instance of LoginViewModel
        // You can pass a fake or mock UserRepository
        LoginViewModel viewModel = new LoginViewModel(fakeUserRepository);
        assertEquals(...);
    }
}

Uçtan uca testler

Entegrasyon testleri için Test amaçlı TestApplicationComponent. Üretim ve test için farklı bir bileşen yapılandırması kullanılır.

Bu, Google Ads'de modüllerin tasarımının daha önceden en iyi yoludur. Test bileşeni, üretim bileşenini genişletir ve farklı bir modül grubu yükler.

Kotlin

// TestApplicationComponent extends from ApplicationComponent to have them both
// with the same interface methods. You need to include the modules of the
// component here as well, and you can replace the ones you want to override.
// This sample uses FakeNetworkModule instead of NetworkModule
@Singleton
@Component(modules = [FakeNetworkModule::class, SubcomponentsModule::class])
interface TestApplicationComponent : ApplicationComponent {
}

Java

// TestApplicationComponent extends from ApplicationComponent to have them both
// with the same interface methods. You need to include the modules of the
// Component here as well, and you can replace the ones you want to override.
// This sample uses FakeNetworkModule instead of NetworkModule
@Singleton
@Component(modules = {FakeNetworkModule.class, SubcomponentsModule.class})
public interface TestApplicationComponent extends ApplicationComponent {
}

FakeNetworkModule, orijinal NetworkModule öğesinin sahte bir kullanımını içeriyor. Burada, değiştirmek istediğiniz içeriğin sahte örneklerini veya taklitlerini kullanabilirsiniz.

Kotlin

// In the FakeNetworkModule, pass a fake implementation of LoginRetrofitService
// that you can use in your tests.
@Module
class FakeNetworkModule {
    @Provides
    fun provideLoginRetrofitService(): LoginRetrofitService {
        return FakeLoginService()
    }
}

Java

// In the FakeNetworkModule, pass a fake implementation of LoginRetrofitService
// that you can use in your tests.
@Module
public class FakeNetworkModule {

    @Provides
    public LoginRetrofitService provideLoginRetrofitService() {
        return new FakeLoginService();
    }
}

Entegrasyon veya uçtan uca testlerinizde TestApplication bir ApplicationComponent yerine TestApplicationComponent oluşturur.

Kotlin

// Your test application needs an instance of the test graph
class MyTestApplication: MyApplication() {
    override val appComponent = DaggerTestApplicationComponent.create()
}

Java

// Your test application needs an instance of the test graph
public class MyTestApplication extends MyApplication {
    ApplicationComponent appComponent = DaggerTestApplicationComponent.create();
}

Ardından bu test uygulaması, kullanacağınız özel bir TestRunner içinde kullanılır. araç testleri yapmak. Bu konuda daha fazla bilgi için Dagger'ı Android uygulamanız codelab'inizde bulabilirsiniz.

Dagger modülleriyle çalışma

Dagger modülleri, nesnelerin semantik olarak nasıl sağlanacağını kapsüllemenin bir yoludur. sağlar. Bileşenlere modül ekleyebilirsiniz, ancak modüller de ekleyebilirsiniz inceleyebilirsiniz. Bu yöntem etkilidir ancak kolayca kötüye kullanılabilir.

Bir modül bir bileşene veya başka bir modüle eklendikten sonra, Dagger grafiğinde yer alır. Hançer, söz konusu bileşende bu nesneleri sağlayabilir. Bir modül eklemeden önce, ilgili modülün Dagger grafiğinin parçası olup olmadığını kontrol edin daha önce bileşene eklenip eklenmediğini kontrol ederek veya projeyi derleyerek ve Dagger’ın söz konusu modül için gerekli bağımlılıkları bulup bulamayacağını kontrol etmelisiniz.

Modüllerin bir bileşende yalnızca bir kez bildirilmesi gerektiği doğru uygulama olarak belirtilmiştir (belirli gelişmiş Dagger kullanım alanları dışında).

Grafiğinizi bu şekilde yapılandırdığınızı varsayalım. ApplicationComponent. Module1 ve Module2 dahildir ve Module1 ModuleX içerir.

Kotlin

@Component(modules = [Module1::class, Module2::class])
interface ApplicationComponent { ... }

@Module(includes = [ModuleX::class])
class Module1 { ... }

@Module
class Module2 { ... }

Java

@Component(modules = {Module1.class, Module2.class})
public interface ApplicationComponent { ... }

@Module(includes = {ModuleX.class})
public class Module1 { ... }

@Module
public class Module2 { ... }

Şu anda Module2, ModuleX tarafından sağlanan sınıflara bağlıdır. Kötü uygulama ModuleX, Module2 içinde iki kez yer aldığından ModuleX dahil aşağıdaki kod snippet'inde gösterildiği gibi grafiği inceleyebilirsiniz:

Kotlin

// Bad practice: ModuleX is declared multiple times in this Dagger graph
@Component(modules = [Module1::class, Module2::class])
interface ApplicationComponent { ... }

@Module(includes = [ModuleX::class])
class Module1 { ... }

@Module(includes = [ModuleX::class])
class Module2 { ... }

Java

// Bad practice: ModuleX is declared multiple times in this Dagger graph.
@Component(modules = {Module1.class, Module2.class})
public interface ApplicationComponent { ... }

@Module(includes = ModuleX.class)
public class Module1 { ... }

@Module(includes = ModuleX.class)
public class Module2 { ... }

Bunun yerine, aşağıdakilerden birini yapmanız gerekir:

  1. Modülleri yeniden düzenleyin ve ortak modülü bir bileşenidir.
  2. Her iki modülün paylaştığı ve ayıkladığı nesnelerle yeni bir modül oluşturun onu bileşene taşır.

Bu şekilde yeniden düzenleme yapılmaması, birbirini içeren birçok modülün kullanılmasına neden olur. bir organizasyon duygusu yoksa ve projenin nerede, ne zaman biteceğini nereden geldiğini görebilirsiniz.

İyi uygulama (1. Seçenek): ModuleX, Dagger grafiğinde bir kez bildirildi.

Kotlin

@Component(modules = [Module1::class, Module2::class, ModuleX::class])
interface ApplicationComponent { ... }

@Module
class Module1 { ... }

@Module
class Module2 { ... }

Java

@Component(modules = {Module1.class, Module2.class, ModuleX.class})
public interface ApplicationComponent { ... }

@Module
public class Module1 { ... }

@Module
public class Module2 { ... }

İyi uygulama (2. Seçenek): Module1 ve Module2 kaynaklı yaygın bağımlılıklar kodu, ModuleX içindeki ModuleXCommon adlı yeni bir modüle çıkartılır. bir değere sahip. Ardından, Google Etiket Yöneticisi'ni kullanarak ModuleXWithModule1Dependencies ve ModuleXWithModule2Dependencies ayrı ayrı bağımlılıklarla oluşturulabilir. Tüm modüller Dagger grafiğinde bir kez açıklanır.

Kotlin

@Component(modules = [Module1::class, Module2::class, ModuleXCommon::class])
interface ApplicationComponent { ... }

@Module
class ModuleXCommon { ... }

@Module
class ModuleXWithModule1SpecificDependencies { ... }

@Module
class ModuleXWithModule2SpecificDependencies { ... }

@Module(includes = [ModuleXWithModule1SpecificDependencies::class])
class Module1 { ... }

@Module(includes = [ModuleXWithModule2SpecificDependencies::class])
class Module2 { ... }

Java

@Component(modules = {Module1.class, Module2.class, ModuleXCommon.class})
public interface ApplicationComponent { ... }

@Module
public class ModuleXCommon { ... }

@Module
public class ModuleXWithModule1SpecificDependencies { ... }

@Module
public class ModuleXWithModule2SpecificDependencies { ... }

@Module(includes = ModuleXWithModule1SpecificDependencies.class)
public class Module1 { ... }

@Module(includes = ModuleXWithModule2SpecificDependencies.class)
public class Module2 { ... }

Yardımlı ekleme

Destekli ekleme, bir nesneyi nerede olursanız olun bazı parametreler DI çerçevesi tarafından sağlanabilir, bazıları ise iletilmelidir kullanıcı tarafından oluşturulan verilerdir.

Android'de bu kalıp, ayrıntılar ekranlarında yaygın olarak öğesi yalnızca çalışma zamanında bilinir, Dagger işlemi derleme sırasında bilinmez DI grafiğini oluşturur. Dagger ile destekli ekleme hakkında daha fazla bilgi edinmek için Dagger belgelerine bakın.

Sonuç

Henüz yapmadıysanız en iyi uygulamalar bölümünü inceleyin. Alıcı: Dagger'ı Android uygulamasında nasıl kullanacağınızı öğrenmek için Android uygulamasında Dagger'ı kullanma codelab'e göz atın.