Adım Adım Dagger2 / 2. Bölüm

Murat Karaöz
5 min readSep 8, 2018

--

Bu adımda örnek bir uygulama ile Dagger’ın bir Adroid uygulamasında nasıl kullanılabileceğini göreceğiz. Uygulama, Bitfinex apisine bağlanarak Bitcoin fiyatını çeken ve bu değeri Shared Preference içine kaydeden basit uygulama. (Kendime not: 1 BTC 6421 USD @8 Eylül 2018)

Image from https://www.simplifiedcoding.net

Projenin kaynak koduna aşağıdaki repodan ulaşabilirsiniz.

  • Öncelikle aşağıdaki bağımlılıkları gradle dosyasına ekliyoruz.
implementation 'com.google.dagger:dagger-android:2.11'
implementation 'com.google.dagger:dagger-android-support:2.11'
annotationProcessor 'com.google.dagger:dagger-compiler:2.14.1'
  • MainActivity içinde bitfinex api’sinden veri çekmek için retrofit tarafından sağlanan BitfinexService sınıfına ve aldığım veriyi kaydetmek için SharedPreferences nesnesine ihtiyacım var. Bu iki sınıfı MainActivity’e inject edeceğiz.
MainActivity.java
  • Inject edilecek olan nesneleri provide eden bir Module sınıfına ihtiyacımız var.
ApplicationModule.java
  • Şimdi module ile inject’i birbirine bağlayacak olan Component arayüzünü yazıyoruz.
ApplicationComponent.java

MainActivity sınıfı field injection metodunu kullanıyor. Constructor injection otomatik gerçekleşirken, field injection elle tetiklenmelidir. Bu nedenle component sınıfı içine MainActivity’i parametre olarak alan bir inject metodu yazıyoruz. Bu metot çağrıldığında MainActivity içine inject edilen değişkenlere değerleri atanır, bu metot çağrılana kadar değişkenler null olur.

Component sınıfının kullandığı modüllerden birinde, bir tane bile scope tanımlamış provide metodu varsa, Component arayüzü de aynı scope değerini kullanmak zorundadır. Component arayzünün scope kullanmaması için hiçbir provide metodunda scope olmamalıdır.

  • Module sınıfı içinde tanımlanan bağımlılıkların hepsi uygulama seviyesi bağımlılıklar ve uygulama hayatta kaldığı sürece kullanılabilirler. bu nedenle bu nesnelerin Application sınıfı içinde yaratılması uygun olacaktır.
MyApp.java

Application sınıfını extend eden sınıfın Manifest dosyası içinde de tanımlanması gerekir. Bunu aşağıdaki şekilde yapabiliriz.

<application
android:name=".MyApp"
android:allowBackup="true"
...
</application>
  • Şimdi her şey hazır olduğuna göre MainActivity içinde injection işlemini başlatabiliriz.
MainActivity.java

Injection işlemi 28. satırda tetikleniyor. Uygulama Dagger tarafından inject edilen değerleri 30. ve 42. satırlarda kullanıyor.

Elimizde çalışan bir uygulama var. Şimdi yaptığımız işin detaylarına bakabiliriz.

annotationProcessor

Gradle dosyasına eklediğimiz annotationProcessor eklentisi Dagger tarafından yaratılan sınıfları IDE path’ine ekler ve bu sayede bu sınıfları görebilir ve debug sırasında içlerine girebiliriz.

Modül Sınıfları

Bir modül dosyası içine her şeyi koyabileceğimiz gibi, modül dosyalarını mantıklı alt modüllere de bölebiliriz. Örneğin AppContext ve shared pref için ayrı modüller oluşturabiliriz.

Component Sınıfı ve inject metotları

Oluşturduğumuz iki farklı module sınıfını tek bir component içinde birleştirebiliriz. @Component annotation’ı virgül ile birbirinden ayrılması şartıyla birden fazla modülü entegre edebilir.

Base class kullanarak sub-class’lara injection yapamayız.

Component içinde tanımlı inject metotları parametre olarak hangi sınıfa injection yapılacaksa o sınıfı almalıdır. Eğer gerçek sınıfı değilde base class’ı kullanırsak Dagger sadece base class içindeki injection işlemini yapar ve sub-class’lara injection yapmaz.

Eğer inject içinde base sınıf kullanmak isterseniz Dagger 2: Even sharper, less square makalesinde Annoyance #1 başlığı altında bir çözüm önerisi var. Yazar base sınıf içinde abstract bir inject metodu tanımlayıp bunu sub classlar içinde implement ediyor.

Base.java
SubClass.java

@Named annotation

Eğer bir modül sınıfı içinde birden fazla metot aynı return tipine sahipse bunları @Named annontation değeri ile isimlendirerek kullanabiliriz. Yukarıdaki örnekte modül sınıfı içinde iki adet metot Retrofit nesnesi dönüyor. Bunlardan biri Scalar converter kullanırken diğer Gson converter kullanıyor.

Hangi nesneyi istiyorsak ihtiyaç duyguğumuz yerde başına @Named yazıp adını verebiliriz.

@Named vs custom annotation

Named annotation değerinin bir alternatifi kendi marker annontation değerlerimizi üretip kullanmaktır.

Kaç tane modül sınıfı yazmalıyız?

Örneğimizde sadece uygulama seviyesi bağımlılıkları inject ettik. Ancak sadece aktivite ve fragment içinde kullanılacak bağımlılıklarımızda olabilir. Örneğin MVP paternini kullanıyorsak her aktivite için bir presenter inject etmemiz gerekecektir. O zaman her aktivite için bir modül sınıfı mı yazmalıyız? Android Architecture Blueprints github sayfasında todo‑mvp‑dagger örneğinde Google öyle yapmış. Buna karşıt görüşü de burada okuyabilirsiniz.

Google uygulama ve aktiviteler için birer tane module sınıfı yazmış. bunların hepsi içinde bir component sınıfı kullanmış.

Karşıt görüş ise uygulama için bir, uygulamadaki her aktivitenin ortak kullanımı için bir, fragmentler için bir, servisler için bir tane olacak şekilde modül sınıfları yazılarak gruplandırma yapılabileceğini, her bir grup içinde bir tane component yazılmasının yeterli olduğunu savunmuş.

Assisted Injection

Modül sınıfı içindeki metotlar parametre olarak sadece Dagger tarafından inject edilebilen nesneleri parametre olarak alabilirler. Biz bu metotlara doğrudan veri gönderemeyiz. Örneğin SharedPreference nesnesi yaratan bir provides metodu String parametresi olarak yaratacağı shared pref dosyasının adını alamaz. Bu işleme assisted injection deniyor ve Dagger bunu desteklemiyor.

Bunu aşmanın bir yolu istediğimiz parametreyi modül sınıfının kurucu metodu içinde tanımlayıp, oraya göndermek olabilir. Modül sınıfı bu parametreyi member olarak tuttuğu bir değişkene atayıp sonrasında provides metodu için kullanabilir.

Bu konu ile ilgili tartışmayı buradan okuyabilirsiniz:

Referanslar

--

--