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.
- Dagger'a bir arayüzde hangi uygulamanın olması gerektiğini bildirmek için
- 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.
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:
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:
Akış sonrasında
LoginViewModel
örneği bellekte kalmaya devam eder tamamlandı.Her giriş akışı için farklı bir
LoginViewModel
örneği istiyorsunuz. Örneğin, kullanıcı çıkış yaparsa farklı birLoginViewModel
, 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:
Yeni bir Dagger modülü (ör.
SubcomponentsModule
) oluşturarak alt bileşenin sınıfını ek açıklamanınsubcomponents
ö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 { }
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ıkLoginActivity
öğesini yerleştirmesine gerek olmadığını unutmayın Çünkü bu sorumluluk artıkLoginComponent
hesabına aittir, dolayısıyla şunları kaldırabilirsiniz:ApplicationComponent
tarafından sağlananinject()
yöntemi.ApplicationComponent
tüketicilerinin,LoginComponent
. Üst bileşenin Tüketiciler, üst bileşen: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.
Grafiğin bölümlerini ayrıntılı olarak inceleyelim:
NetworkModule
(ve dolayısıylaLoginRetrofitService
) dahil edilirApplicationComponent
içinde, bileşende belirttiğiniz için geçerli.UserRepository
öğesi, kapsamında olduğu içinApplicationComponent
içinde kalırApplicationComponent
. 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
veUserRemoteDataSource
) burada olmalıdır bileşenini de ziyaret edebilirsiniz.UserRepository
LoginViewModel
, yalnızca gerekli olduğu içinLoginComponent
bölümüne dahil edildiLoginComponent
tarafından yerleştirilen sınıflar tarafından.LoginViewModel
dahil değildirApplicationComponent
ihtiyacında bağımlılık olmadığındanApplicationComponent
LoginViewModel
Benzer şekilde,
UserRepository
öğesiniApplicationComponent
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çinLoginComponent
kapsamındaUserRepository
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
veLoginActivity
kontrolünden sorumluLoginComponent
.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:
- Modülleri yeniden düzenleyin ve ortak modülü bir bileşenidir.
- 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.