Çok modüllü uygulamalarda Dagger'ı kullanma

Birden fazla Gradle modülü bulunan projeler çok modüllü proje olarak bilinir. Özelliği olmayan tek bir APK olarak sunulan çok modüllü bir projede çoğuna bağımlı olabilen bir app modülüne sahip olmak, ve bir base ya da core modülüne sahip olursunuz. bağlı olarak değişiklik gösterir. app modülü genellikle Application sınıfı, base modülü, projenizdeki tüm modüllerde paylaşılan tüm ortak dersleri içerir.

app modülü, uygulama bileşeninizi beyan etmek için iyi bir yerdir ( örnek, aşağıdaki resimde ApplicationComponent), nesnelerin uygulamanızın tekil bileşenlerinin yanı sıra başka bileşenleri de test edebilirsiniz. Kullanıcı OkHttpClient gibi sınıflar, JSON ayrıştırıcılar, veritabanınıza erişimciler, core modülünde tanımlanabilecek SharedPreferences nesne, app modülünde tanımlanan ApplicationComponent tarafından sağlanır.

app modülünde, daha kısa ömürlü başka bileşenler de bulunabilir. Kullanıcıya özel yapılandırmaya sahip bir UserComponent örnek olarak gösterilebilir (UserSession gibi).

Projenizin farklı modüllerinde en az bir tane tanımlayabilirsiniz. Şekil 1'de görüldüğü gibi, o modüle özgü bir mantığa sahip alt bileşendir.

Şekil 1. Dagger grafiği örneği çok modüllü proje

Örneğin, bir login modülünde LoginComponent Yaygın nesneleri sağlayabilecek özel bir @ModuleScope ek açıklamasıyla kapsamlıdır LoginRepository örneğinde bulabilirsiniz. Bu modülde şunları da yapabilirsiniz: özel bir LoginComponent öğesine bağlı başka bileşenlere sahip kapsam, örneğin @FeatureScope için LoginActivityComponent veya Özelliğe daha özgü mantığın kapsamına girebileceğiniz TermsAndConditionsComponent ViewModel nesne gibi.

Registration gibi diğer modüllerde benzer kuruluma sahip olursunuz.

Çok modüllü projeler için genel kural olarak, aynı seviyedeki modüllerin birbirine bağımlı olmamalıdır. Varsa ortak bir mantığa (aradaki bağımlılıklar) üst modülün bir parçası olmalıdır. Öyleyse, sınıfları üst modüle taşımak için yeniden düzenleme; Modül yoksa yeni bir modül oluşturun hem de orijinal modüllerin her ikisine de sahip kullanabilirsiniz.

En iyi uygulama olarak, bir bileşeni genellikle modülünü kullanabilirsiniz:

  • LoginActivityComponent işleminde olduğu gibi alan yerleştirme işlemi gerçekleştirmeniz gerekir.

  • LoginComponent'de olduğu gibi nesnelerin kapsamını belirlemeniz gerekir.

Bu durumların ikisi de geçerli değilse ve Dagger'a bu durumu nesnelerini oluşturmak için bir Dagger modülü oluşturun ve @Provides ya da Bu sınıflar için inşaat yerleştirme mümkün değilse @Binds yöntemleri.

Dagger alt bileşenleriyle uygulama

Android uygulamalarında Dagger'ı kullanma doküman sayfasında, uygulama oluşturma ve kullanma alt bileşenlerde bulunur. Ancak, aynı kodu kullanamazsınız çünkü özellik modüllerinin app modülü hakkında bilgi sahibi olmadığını unutmayın. Örneğin, projenizin giriş akışı ve önceki sayfada bulunan kod hakkında derleyin:

Kotlin

class LoginActivity: Activity() {
  ...

  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)
    ...
  }
}

Java

public class LoginActivity extends Activity {
    ...

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

        ...
    }
}

Bunun nedeni, login modülünün MyApplication veya appComponent. Bunu yapmak için özellikte bir arayüz tanımlamanız gerekir. MyApplication ürününün ihtiyaç duyduğu bir FeatureComponent sağlayan modül pek çok yolu vardır.

Aşağıdaki örnekte, bir LoginComponentProvider arayüzü tanımlayabilirsiniz login modülünde Giriş akışı için bir LoginComponent sağlar:

Kotlin

interface LoginComponentProvider {
    fun provideLoginComponent(): LoginComponent
}

Java

public interface LoginComponentProvider {
   public LoginComponent provideLoginComponent();
}

Artık LoginActivity, kod snippet'i yerine bu arayüzü kullanacak. aşağıda tanımlandığı gibi:

Kotlin

class LoginActivity: Activity() {
  ...

  override fun onCreate(savedInstanceState: Bundle?) {
    loginComponent = (applicationContext as LoginComponentProvider)
                        .provideLoginComponent()

    loginComponent.inject(this)
    ...
  }
}

Java

public class LoginActivity extends Activity {
    ...

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        loginComponent = ((LoginComponentProvider) getApplicationContext())
                                .provideLoginComponent();

        loginComponent.inject(this);

        ...
    }
}

Şimdi, MyApplication uygulamasının bu arayüzü uygulaması ve gerekli yöntemler:

Kotlin

class MyApplication: Application(), LoginComponentProvider {
  // Reference to the application graph that is used across the whole app
  val appComponent = DaggerApplicationComponent.create()

  override fun provideLoginComponent(): LoginComponent {
    return appComponent.loginComponent().create()
  }
}

Java

public class MyApplication extends Application implements LoginComponentProvider {
  // Reference to the application graph that is used across the whole app
  ApplicationComponent appComponent = DaggerApplicationComponent.create();

  @Override
  public LoginComponent provideLoginComponent() {
    return appComponent.loginComponent.create();
  }
}

Çok modüllü bir projede Dagger alt bileşenlerini bu şekilde kullanabilirsiniz. Özellik modüllerinde çözüm, Google'ın birbirine bağlıdır.

Özellik modülleriyle bileşen bağımlılıkları

Özellik modüllerinde modüllerin çalışma şekli genellikle içeren ters çevrilmiştir. Özellik içeren app modülü yerine modüllerinde, özellik modülleri app modülüne bağlıdır. Şekil 2'ye bakın temsil eder.

Şekil 2. Dagger grafiği örneği özellik modülleri içeren proje

Dagger'da, bileşenlerin alt bileşenleri hakkında bilgi sahibi olması gerekir. Bu bilgiler üst bileşene (örneğin, SubcomponentsModule modülüne ait bilgileri Android uygulamalarında Dagger kullanma başlıklı makalede bulabilirsiniz.

Ne yazık ki uygulama ile özellik modülünde değilse alt bileşen, app modülünden derleme yolunda değildir. Örneğin, LoginComponent login özellik modülü ApplicationComponent, app modülünde tanımlandı.

Dagger, müşterilerin ihtiyaçlarını karşılamak için bileşen bağımlılıkları adlı bir mekanizmaya sahiptir. çözer. Alt bileşen, üst bileşene, alt bileşen de üst bileşene bağımlıdır. Entegre alt-üst ilişkisinin olmadığını; bileşenler artık diğer kullanıcılara belirli bağımlılıkları elde etmeniz gerekir. Bileşenlerin grafikteki türleri göstermesi gerekir bağımlı bileşenlerin tüketilmesine de izin veriliyor.

Örneğin: login adlı bir özellik modülü Şu bağlamda kullanılabilen AppComponent değerine bağlı olarak LoginComponent: app Gradle modülü.

Aşağıda, şu sınıfın bir parçası olan sınıfların ve AppComponent için tanımlar: app Gradle modülü:

Kotlin

// UserRepository's dependencies
class UserLocalDataSource @Inject constructor() { ... }
class UserRemoteDataSource @Inject constructor() { ... }

// UserRepository is scoped to AppComponent
@Singleton
class UserRepository @Inject constructor(
    private val localDataSource: UserLocalDataSource,
    private val remoteDataSource: UserRemoteDataSource
) { ... }

@Singleton
@Component
interface AppComponent { ... }

Java

// UserRepository's dependencies
public class UserLocalDataSource {

    @Inject
    public UserLocalDataSource() {}
}

public class UserRemoteDataSource {

    @Inject
    public UserRemoteDataSource() { }
}

// UserRepository is scoped to AppComponent
@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;
    }
}

@Singleton
@Component
public interface ApplicationComponent { ... }

app gradle modülünü içeren login gradle modülünüzde LoginViewModel örneği yerleştirilmesi gereken LoginActivity:

Kotlin

// LoginViewModel depends on UserRepository that is scoped to AppComponent
class LoginViewModel @Inject constructor(
    private val userRepository: UserRepository
) { ... }

Java

// LoginViewModel depends on UserRepository that is scoped to AppComponent
public class LoginViewModel {

    private final UserRepository userRepository;

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

LoginViewModel, kullanılabilir UserRepository verisine dayalı bir bağımlılığa sahip ve AppComponent olarak ayarlanır. Şuna bağlı bir LoginComponent oluşturalım: LoginActivity eklemek için AppComponent:

Kotlin

// Use the dependencies attribute in the Component annotation to specify the
// dependencies of this Component
@Component(dependencies = [AppComponent::class])
interface LoginComponent {
    fun inject(activity: LoginActivity)
}

Java

// Use the dependencies attribute in the Component annotation to specify the
// dependencies of this Component
@Component(dependencies = AppComponent.class)
public interface LoginComponent {

    void inject(LoginActivity loginActivity);
}

LoginComponent, AppComponent kaynağına ekleyerek bir bağımlılığı belirtir. bağımlılık ek açıklamasına karşılık gelir. Çünkü LoginActivity, bir mekanizmanın Dagger tarafından yerleştirilmesi için arayüze inject() yöntemini ekleyin.

Bir LoginComponent oluşturulurken AppComponent örneğinin şöyle olması gerekir: başarılı oldu. Bu işlemi yapmak için bileşeni fabrika ayarlarına kullanın:

Kotlin

@Component(dependencies = [AppComponent::class])
interface LoginComponent {

    @Component.Factory
    interface Factory {
        // Takes an instance of AppComponent when creating
        // an instance of LoginComponent
        fun create(appComponent: AppComponent): LoginComponent
    }

    fun inject(activity: LoginActivity)
}

Java

@Component(dependencies = AppComponent.class)
public interface LoginComponent {

    @Component.Factory
    interface Factory {
        // Takes an instance of AppComponent when creating
        // an instance of LoginComponent
        LoginComponent create(AppComponent appComponent);
    }

    void inject(LoginActivity loginActivity);
}

Artık LoginActivity, LoginComponent öğesinin bir örneğini oluşturabilir ve inject() yöntemi.

Kotlin

class LoginActivity: Activity() {

    // You want Dagger to provide an instance of LoginViewModel from the Login graph
    @Inject lateinit var loginViewModel: LoginViewModel

    override fun onCreate(savedInstanceState: Bundle?) {
        // Gets appComponent from MyApplication available in the base Gradle module
        val appComponent = (applicationContext as MyApplication).appComponent

        // Creates a new instance of LoginComponent
        // Injects the component to populate the @Inject fields
        DaggerLoginComponent.factory().create(appComponent).inject(this)

        super.onCreate(savedInstanceState)

        // Now you can access loginViewModel
    }
}

Java

public class LoginActivity extends Activity {

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

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        // Gets appComponent from MyApplication available in the base Gradle module
        AppComponent appComponent = ((MyApplication) getApplicationContext()).appComponent;

        // Creates a new instance of LoginComponent
        // Injects the component to populate the @Inject fields
        DaggerLoginComponent.factory().create(appComponent).inject(this);

        // Now you can access loginViewModel
    }
}

LoginViewModel, UserRepository metriğine bağlıdır; ve LoginComponent için AppComponent üzerinden erişebiliyorsa, AppComponent tarafından şurada kullanıma sunulması gerekir: arayüzü:

Kotlin

@Singleton
@Component
interface AppComponent {
    fun userRepository(): UserRepository
}

Java

@Singleton
@Component
public interface AppComponent {
    UserRepository userRepository();
}

Bağımlı bileşenlere sahip kapsam kuralları, alt bileşenlerde bulunur. LoginComponent, AppComponent öğesinin bir örneğini kullandığından, aynı kapsam ek açıklamasını kullanamazlar.

LoginViewModel kapsamını LoginComponent kapsamına almak isterseniz bunu şu şekilde yaparsınız: özel @ActivityScope ek açıklamasını kullanıyor.

Kotlin

@ActivityScope
@Component(dependencies = [AppComponent::class])
interface LoginComponent { ... }

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

Java

@ActivityScope
@Component(dependencies = AppComponent.class)
public interface LoginComponent { ... }

@ActivityScope
public class LoginViewModel {

    private final UserRepository userRepository;

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

En iyi uygulamalar

  • ApplicationComponent, her zaman app modülünde olmalıdır.

  • Alan yerleştirme işlemi gerçekleştirmeniz gerekiyorsa modüllerde Dagger bileşenleri oluşturma veya nesnelerinizin belirli bir akışı için en iyi yoludur.

  • Yardımcı program veya yardımcı olması amaçlanan ve ihtiyacınız olmayan Gradle modülleri için için bir grafik oluşturmak (bu nedenle bir Dagger bileşenine ihtiyacınız vardır), Herkese açık olan ve @Provides ve @Binds yöntemlerine sahip bu sınıfların oluşturucu yerleştirmeyi desteklemez.

  • Dagger'ı özellik modülleriyle bir Android uygulamasında kullanmak için bileşen tarafından sağlanan bağımlılıklara erişebilmek için ihtiyaç app modülünde ApplicationComponent tanımlandı.