Birden fazla Gradle modülü bulunan projelere çok modüllü proje denir.
Özellik modülü olmayan tek bir APK olarak gönderilen çok modüllü bir projede, projenizin çoğu modülüne bağlı olabilen bir app
modülüne ve geri kalan modüllerin genellikle bağımlı olduğu base
veya core
modülüne sahip olmak yaygın bir durumdur. app
modülü genellikle Application
sınıfınızı içerirken base
modülü, projenizdeki tüm modüllerde paylaşılan tüm ortak sınıfları içerir.
app
modülü, uygulamanızın tekil tonlarının yanı sıra diğer bileşenlerin ihtiyaç duyabileceği nesneleri de sağlayabilen uygulama bileşeninizi (örneğin, aşağıdaki resimde ApplicationComponent
) tanımlamak için uygundur. Örnek olarak OkHttpClient
, JSON ayrıştırıcılar, veritabanınızın erişimcileri veya core
modülünde tanımlanabilecek SharedPreferences
nesneleri gibi sınıflar 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.
Örnek olarak, giriş yaptıktan sonra kullanıcıya özel yapılandırmaya (UserSession
gibi) sahip bir UserComponent
verilebilir.
Projenizin farklı modüllerinde, Şekil 1'de görüldüğü gibi ilgili modüle özgü mantığa sahip en az bir alt bileşen tanımlayabilirsiniz.
Örneğin, bir login
modülünde, LoginRepository
gibi özelliklerle yaygın olarak kullanılan nesneleri sağlayabilen özel bir @ModuleScope
ek açıklamasına sahip bir LoginComponent
kapsamınız olabilir. Bu modülün içinde, farklı bir özel kapsama sahip LoginComponent
öğesine bağlı başka bileşenler de bulunabilir. Örneğin, LoginActivityComponent
için @FeatureScope
veya ViewModel
nesneleri gibi özelliğe daha çok özgü mantığın kapsamına girebileceğiniz bir TermsAndConditionsComponent
.
Registration
gibi diğer modüller için de benzer bir kurulumunuz olurdu.
Çok modüllü projelerde genel kural, aynı düzeydeki modüllerin birbirine bağlı olmaması gerektiğidir. Çalışıyorlarsa bu paylaşılan mantığın (aralarındaki bağımlılıklar) üst modülün bir parçası olup olmayacağını göz önünde bulundurun. Bu durumda, sınıfları üst modüle taşımak için yeniden düzenleyin. Taşıyamıyorsanız üst modülü genişleten ve orijinal modüllerin her ikisinin de yeni modülü genişletmesini sağlayan yeni bir modül oluşturun.
En iyi uygulama olarak, aşağıdaki durumlarda genellikle bir modül içinde bileşen oluşturursunuz:
LoginActivityComponent
'te olduğu gibi alan ekleme işlemini gerçekleştirmeniz gerekir.LoginComponent
ile olduğu gibi nesnelerin kapsamını ayarlamanız gerekir.
Bu kumarhanelerin hiçbiri geçerli değilse ve Dagger'a bu modülden nesneleri nasıl sağlayacağını bildirmeniz gerekiyorsa, Dagger modülünü @Provides
veya @Binds
yöntemleriyle oluşturup açığa çıkarın (bu sınıflarda inşaat yerleştirmesi mümkün değilse).
Dagger alt bileşenleriyle uygulama
Android uygulamalarında Dagger'ı kullanma doküman sayfasında alt bileşenlerin nasıl oluşturulacağı ve kullanılacağı ele alınmaktadır. Ancak özellik modülleri app
modülü hakkında bilgi sahibi olmadığı için aynı kodu kullanamazsınız. Örneğin, tipik bir Giriş akışını ve önceki sayfada bulunan kodu düşündüğünüzde artık derlenmez:
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
ve appComponent
hakkında bilgi sahibi olmamasıdır. Bunu yapmak için özellik modülünde MyApplication
uygulamasının uygulaması gereken FeatureComponent
sağlayan bir arayüz tanımlamanız gerekir.
Aşağıdaki örnekte, login
modülünde Giriş akışı için LoginComponent
sağlayan bir LoginComponentProvider
arayüzü tanımlayabilirsiniz:
Kotlin
interface LoginComponentProvider { fun provideLoginComponent(): LoginComponent }
Java
public interface LoginComponentProvider { public LoginComponent provideLoginComponent(); }
Artık LoginActivity
, yukarıda tanımlanan kod snippet'i yerine bu arayüzü kullanacaktır:
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
ürününün bu arayüzü ve gerekli yöntemleri uygulaması gerekiyor:
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ülleriyle ilgili çözüm, modüllerin birbirine bağlılığı nedeniyle farklıdır.
Özellik modülleriyle bileşen bağımlılıkları
Özellik modüllerinde, modüllerin genellikle birbirine bağlı olma şekli tersine çevrilir. Özellik modüllerini içeren app
modülü yerine, özellik modülleri app
modülüne bağlıdır. Modüllerin nasıl yapılandırıldığını görmek için Şekil 2'ye bakın.
Dagger'da, bileşenlerin alt bileşenleri hakkında bilgi sahibi olması gerekir. Bu bilgiler, üst bileşene eklenen bir Dagger modülünde yer alır (Android uygulamalarında Dagger'ı kullanma bölümündeki SubcomponentsModule
modülü gibi).
Maalesef uygulama ile özellik modülü arasındaki tersine bağımlılık nedeniyle alt bileşen, derleme yolunda olmadığı için app
modülünden görünmez. Örneğin, bir login
özellik modülünde tanımlanan LoginComponent
, app
modülünde tanımlanan ApplicationComponent
öğesinin alt bileşeni olamaz.
Dagger'ın bu sorunu çözmek için kullanabileceğiniz, bileşen bağımlılıkları adlı bir mekanizması vardır. Alt bileşen, üst bileşenin alt bileşeni yerine üst bileşene bağımlıdır. Bununla birlikte, üst-alt ilişkisi yoktur. Artık bileşenler belirli bağımlılıkları elde etmek için diğerlerine bağımlıdır. Bağımlı bileşenlerin tüketmesi için bileşenlerin grafikten türleri göstermesi gerekir.
Örneğin, login
adlı bir özellik modülü, Gradle modülünde bulunan AppComponent
öğesine dayalı bir LoginComponent
oluşturmak istiyor.app
Aşağıda, app
Gradle modülünün parçası olan sınıf ve AppComponent
ile ilgili tanımlar verilmiştir:
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ğinin eklenmesi gereken bir LoginActivity
var:
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 ve kapsamı AppComponent
kapsamındaki UserRepository
için bir bağımlılığa sahip. LoginActivity
öğesini eklemek için AppComponent
öğesine bağlı bir LoginComponent
oluşturalım:
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
için bağımlılığı bileşen ek açıklamasının bağımlılık parametresine ekleyerek belirtir. LoginActivity
, Dagger tarafından yerleştirileceği için arayüze inject()
yöntemini ekleyin.
LoginComponent
oluşturulurken AppComponent
örneğinin aktarılması gerekir. Bunu 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
örneği oluşturabilir ve inject()
yöntemini çağırabilir.
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
ürününe bağlıdır. LoginComponent
ürününün AppComponent
ürününden erişebilmesi için AppComponent
, bu öğeyi arayüzünde görüntülemelidir:
Kotlin
@Singleton @Component interface AppComponent { fun userRepository(): UserRepository }
Java
@Singleton @Component public interface AppComponent { UserRepository userRepository(); }
Bağımlı bileşenlere sahip kapsam oluşturma kuralları, alt bileşenlerle aynı şekilde çalışır. LoginComponent
, AppComponent
örneğini kullandığından aynı kapsam ek açıklamasını kullanamaz.
LoginViewModel
kapsamını LoginComponent
olarak ayarlamak isterseniz daha önce özel @ActivityScope
ek açıklamasını kullanarak bunu yapmanız gerekir.
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
daimaapp
modülünde olmalıdır.Söz konusu modülde alan yerleştirme yapmanız veya nesnelerin kapsamını uygulamanızın belirli bir akışı için kapsamanız gerekiyorsa modüllerde Dagger bileşenleri oluşturun.
Yardımcı programlar veya yardımcılar olması amaçlanan ve grafik oluşturmanız gerekmeyen Gradle modülleri için (Bu nedenle Dagger bileşeni gerekir) genel Dagger modüllerini, oluşturucu yerleştirmeyi desteklemeyen bu sınıfların @Provides ve @Binds yöntemlerini kullanarak oluşturun ve kullanıma sunun.
Dagger'ı özellik modülleri içeren bir Android uygulamasında kullanmak için,
app
modülünde tanımlananApplicationComponent
tarafından sağlanan bağımlılıklara erişebilmek amacıyla bileşen bağımlılıklarını kullanın.