Dagger 2 ve Android Dependency Injection — 2


Bir önceki yazıma buradan ulaşabilirsiniz. İlk yazımda Dependency Injection ile alakalı basit bir örnek gösterip bu kavramdan bahsettim. Bu yazımda Dagger 2 kullanımını en basit şekilde göstermek istiyorum.

5 yaşındaki bir çocuğa dependency injection’ı anlatmanız istense nasıl anlatırdınız? Benim bunu açıklamam biraz zor olurdu. Ama stackoverflow’da verilen cevap gerçekten mükemmel.

5 yaşında bir çocuk birşeyler almak için buzdolabına gider ise bazı problemlere neden olabilir. Buzdolabının kapağı açık kalabilir veya ebeveynlerinin istemediği şeyleri alabilir. Hatta buzdolabının dışındaki şeylere bakabilir veya buzdolabından tarihi geçmiş birşeyi alabilir. Çocuğun asıl yapması gereken “Öğle yemeğim ile bir içeceğe ihtiyacım var” gibi bir istekte bulunmaktır. Çocuk böyle bir istekte bulunduğunda yemeğini alır ve yer. (Nasıl, nereden alıcam diye düşünmez, istemesi yeter :))

Dependency Injection Frameworklerinin de mantığı budur. Eğer bir nesneye ihtiyacın olduğunda bunu sen oluşturmazsın. Bu nesneye ihtiyacın olduğunu söylersin. Böylece framework o nesneyi oluşturur ve sana getirir. Ve bu nesnenin yaşam döngüsünden framework sorumludur. Böylece sen sadece neye ihtiyacım olduğunu söyler ve o nesneyi kullanırsın.

Dagger 2 Kaynakları

Dagger 2 ile alakalı araştırmalar yaparken bir çok kaynakla karşılaştım. Ve Dagger 2 kullanmadan önce kesinlikle okunması,izlenmesi gereken linkleri topladım.

Sunum ve Video(İngilizce)

Jake Wharton Devoxx 2014 Dagger 2 sunumu

Gregory Kick @Google — Dagger 2 sunumu

Fernando Cejas — Android 10 coder blog

Saúl Molinero’s Blog — When the Avengers meet Dagger2, RxJava and Retrofit in a clean way

Kaynak kodlar

Kaynak Kod 1, Kaynak Kod 2, Kaynak Kod 3, Kaynak Kod 4 (Bu projeleri baz alıp incelerseniz faydalı olucaktır. Tabii ki daha fazla örnek mevcut.)

Dagger 2.0

Dagger 2'yi anlamak başlangıçta biraz zor olacaktır evet.(En azından benim için öyle) Dagger 2 yi anlamak için bu terimleri bilmemiz gerekiyor çünkü genel olarak bunları kullanacağız. (Tam olarak anlaşılmadıysa sorun yok örneklerle destekleyeceğiz)

@Inject : Dependency injection’un inject’i burdan geliyor. Eğer bir sınıfı istiyorsanız bunu Dagger’a söylersiniz ve oluşturup size getirir. Inject etmek istediğimiz yerde bu annotation’u kullanıyoruz.

@Module : inject ettiğimiz yerde daggerın bu sınıfları bir yerden alması gerekiyor. Bunlar da module sınıfları. Kısacası Module, dependency provider diyebiliriz. Bir sınıf oluşturup başına @Module annotation’u ekleriz ve Module sınıfımız oluşturulmuş olur. Ve bu sınıfa başla sınıflardan inject edilcek sınıfların providerlarını method olarak yazarız. Böylece Dagger, inject edilcek sınıfları nerde bulacağını bilir.

@Provide : Module oluşturulduğunda methodlarının başına @Provide annotationu ekleriz bu method böylece dependency provider olarak görev yapar. Dagger, istenilen injection’ı @Provide methodlarına bakarak bulur ve getirir.

@Component : Componentler Module ile @Inject annotationlarımız arasındaki köprüdür diyebiliriz. Component arayüzleri injector’lerdir. Ana görevi @Module ve @Inject i biraraya getirmektir. Arayüz sınıfı oluşturup @Component annotaion’uyla bu sınıfı bir component yapabiliriz. Bu componente bağlı olacak modulleri de bu sınıfta belirtiriz. Kod örneklerinde bunu göreceğiz.

@Scope : Scope’lar yaşam süreçlerini belirler. Oluşturduğumuz bir @PerActivity scope sınıfı kullanıldığında o nesne sadece o activity varolduğu sürece yaşar ve daha sonra framework tarafından yok edilir. Aynı şekilde Bir Kullanıcı Login işlemimiz var ise @UserLogin Scope’u belirleyip o nesnelerin kullanıcı logout olana kadar yaşamasını sağlayabiliriz.

Dagger 2 Uygulama

Dagger 2 frameworkunu kullanarak çok basit bir uygulama çıkartacağız. Bir önceki yazımda Dependency injection’ı Araba ve Motor nesneleri üzerinden anlatmaya çalışmıştım. Bu uygulamada da aynı şekilde devam edeceğim.

Android Studio’da start a New Android Studio Project diyerek yeni bir android projesi oluşturalım.

Dagger’ın çalışması için gerekli compiler, apt plugin ve runtime library’i projemize eklememiz gerekmekte.

build.gradle (Ana projemizdeki)’a apt plugini ekliyoruz.

build.gradle(app module)’a compiler ve runtime kütüphanemizi ekliyoruz.

Artık projemizde dagger 2 frameworkünü kullanabiliriz.

Bir arabamız ve arabada kullanacağımız bir motor var. Ve bir activityde arabamızı çalıştırıp durduracağız. Temiz bir kod yapısına sahip olması için tüm nesnelerimizi arayüz olarak programlayacağız. Böylece daha sonradan araba modellerimiz veya motor modelimiz değiştiğinde projeyi açıp tekrar değiştirmek zorunda kalmayacağız.

Android studio proje yapımız şu şekilde ;

Injector : Dependency Injector paketi

Model : Araba ve Motor sınıf ve arayüzlerimiz

Views : Activity ve Fragment gibi arayüz sınıflarımız.

Model paketimizde;

Car ve Engine arayüzlerimiz var. Tüm Engine sınıfları(LPGEngine ve PetrolEngine) bu arayüzden implement eder. Tüm Car sınfıları da (BMWCar ve MercedesCar) bu arayüzden implement eder.

@Inject kullanarak Engine arayüzüne sahip bir sınıfı alıyoruz ve Car arayüzüne sahip bir sınıfta kullanıyoruz. Peki hangi Engine sınıfını kullanacak? LPG Engine mi Petrol Engine mi ? Bunu modulleri oluştururken biz belirleyeceğiz. (Kafanız karışmasın az sonra daha iyi anlayacağız.)

Injector paketimizde;

AppComponent ve AppModule sınıflarımız var.

Component sınıfımıza bakarsak şu anlamları çıkartırız. CarActivity sınıfı @Inject edecek. Ve @Inject edeceği dependency’leri AppModule.class’dan alacak. AppModule sınıfımızda da @Provides annotation’u ile gösterilen methodlar bizim dependencylerimizdir. @Singleton annotation’u da o nesnenin sadece bir tane oluşturulmasını sağlar.

Buradaki en önemli yapıdan birisi de provide methodlarının specific bir sınıf değil onun arayüzünü return etmesidir. Böylece sınıflara değil arayüzlere bağımlı bir yapı oluşturursunuz.

Componentler Module’lerin sunduğu dependencyler ile diğer sınıfların kullandığı @Inject’lerin arasındaki köprü demiştik. Ve bu köprülerin oluşturulması gerekiyor.

Application sınıfımızda AppComponent’i oluşturuyoruz. (DaggerAppComponent generated bir sınıftır. Dagger frameworkü tarafından generate edilir. Bu sınıfı çağırmakta sorun yaşarsanız gradle’i build edin ya da uygulamayı run edin)

View paketimizde;

Son olarak Activitymizde bu sınıflara @Inject etmeyi deneyelim.

Oncelikle bu Activity’i component’e inject ediyoruz. Yani bu activityde @Inject kullanacağımızı belirtiyoruz. Kullanacağımız Arabaya @Inject edip çalıştırıp durduruyoruz. Şimdi çıktısına bakalım.

BMW Car çalıştırıldı. Benzinli Motor çalıştırıldı. BMW Car durduruldu. Benzinli Motor durduruldu.

Gördüğünüz gibi sınıflarımızda @Inject kullanarak nesneleri elde edebiliyoruz.

Projenin ilerleyen safhalarında BMW değil de Mercedes kullanabiliriz. Ve bu Mercedes arabaya da benzinli değil de LPG takabiliriz. Bunu yapmak için sadece @Provide eden methodları değiştirmemiz yeterli.

Sadece @Provide methodlarını değiştirdim ve çıktıya bakalım.

Araba ve motor özellikleri değişti. Böylece tüm kodu değiştirmek zorunda kalmadık. Eğer yeni bir araba eklemek istersek sadece yeni bir sınıf oluşturup onu Car arayüzünden implement etmemiz ve provider methodu değiştirmemiz yeterli. Projemizde araba modelleri ya da hangi motor tipleriyle ilgilenmek yerine sadece Araba ve Motor ile ilgilenirsek temiz bir kod yapısı sunarız.

Projede neler gerçekleştiğini genel olarak özetlemek gerekirse.

CarActivity sınıfında Car sınıflarına inject etmek için

diyoruz. Peki burada neler gerçekleşiyor. Sırayla anlatalım. Öncelikle @Inject çağrılan activitynin hangi componente inject edildiğine gidelim(AppComponent) daha sonradan bu AppComponentin hangi modülleri kullandığına bakalım.(AppModule) daha sonra bu moduledan return parametresi Car olan @Provides methoduna bakalım(provideCar()) ve bunu Activitymize verelim. Böylece Activitymizde kullanıma hazır bir Car sınıfımız oldu. Eğer Projede hiçbir yeri değiştirmeden Module’deki Car provider methodunu BMW değil de Mercedes oluştursaydık. Tüm projede Araba olarak BMW değiş de mercedes kullanılmaya başlancaktı. Temel olarak işleyiş ve diagram bu şekilde.

Projenin kaynak kodlarına buradan ulaşabilirsiniz. Bu projeyi olduğunca basit tutmaya çalıştım. Sıradaki yazımda Dagger 2 ile ilgi daha detaylı bir android projesi yapmayı düşünüyorum.