Junior’muşum gibi açıkla II -Android Mimarisi

Hadi Tok
GDGIstanbul
Published in
4 min readJan 7, 2019

Support library ve Google Play Services hakkindaki serinin ilk bölümüne buradan erişebilirsiniz:

Selimiye Camisi-Edirne

Android uygulama geliştirmede kullanılan mimari yıllar içerisinde çok değişti, o kadar ki daha tecrübeli geliştiriciler için bile takibi zor olabiliyor. Mimaride MVC, MVP, MVVM, MVI ve diğerleri, Dependency Injection tarafında Java’da Dagger, Dagger 2 ve Kotlin’de Koin ve Kodein var. Bu seçeneklerden en doğrusu hangisi ve hangisini kullanmak gerek? Bu soruya cevabım yok fakat bunları neden kullandığımızı açıklamaya çalışacağım.

Bence yazılım mimarisinin yaptığı iki şey var:

  • Kodu birbirinden izole katmanlar halinde ayırma(Layers of Abstraction)
  • Katmanlar arasındaki iletişimi belirleme.

Farklı mimariler bu iki şeyi farklı yapar. MVx mimari modelleri ve Dependency Injection Android’de yaygın olarak kullanıldığı için bunlardan bahsedeceğim.

MVx Mimari Modelleri

MVC, MVP, MVVM ve MVI mimarilerinin isimlerine baktığımızda dikkati çeken ilk şey isimlerindeki ortak MV ve farkı olan son kısım. Buna dayanarak bu modellerin üç katmandan oluştuğunu söyleyebiliriz.

  • Model: Bu katman gösterilmesi gereken data’ya ve bu datanın kaynağı olan katmanı ifade eder
  • View: Bu katman kullanıcı ile etkileşimin sağlandığı yer.
  • x-Logic: MVC’de bu biraz farklı olsa da bu katmanda model katmanından alınan veriler kullanıcının göreceği şekle dönüştürülür ve kullanıcının aldığı aksiyonlar uygun data formatına çevrilip gerekli değişikliklerin yapılması için Model katmanına aktarılır.

Bu katmanlarda yapılan şeyler aynı olsa da Logic katmanı aradaki bağlantıların nasıl yapılacağını belirler. Yani aslında saydığımız mimarilerdeki temel farklılık katmanlar arasındaki iletişimin nasıl olduğudur. Gördüğümüz gibi bu bu modellerde kullanıcıya bakan bir katman olduğunu görüyoruz, bu da bu mimarilerin kullanıcı arayüzü olan sistemlerde kullanıldığı anlamına geliyor. Bu mimari modelleri inceleyip karar vermek size kalmış fakat Android Jetpack ViewModel ve LiveData ile birlikte MVVM’i desteklediğini belirtmek isterim. Bu konuya Architecture Components ile birlikte bir sonraki yazıda değineceğim.

Mimari modeller Separation of Concerns (SoC) prensibine dayanır. Bunu kabaca ilgili şeylerin birleştirilerek birbirlerinden ayrılması olarak açıklayabiliriz. İlgili şeyleri gruplayarak, ayrı parçaları diğerlerini etkilemeden değiştirebiliriz. Bunun önemsiz olduğunu söylemesem de bu mimarilerin Android’de kullanım gerekçesi biraz farklı. Buna değinmeden önce Dependency Injection hakkında konuşalım.

Çerkes Kaması

Dependency Injection (DI)

Dependency injection korkutucu bir isme sahip olsa da yaptığı şey aslında basit. Sınıflar bazı işleri yapabilmek için diğer sınıfların nesnelerine (instance) ihtiyaç duyarlar bunlara da dependency yani bağımlılık denilir, mesela bir API’a istek atmak istediğimizde bir Retrofit nesnesine ihtiyaç duyarız. Dependency injection: nesneleri kullanacağımız yerde oluşturmaktansa dışarıda oluşturup gerekli yerlere aktarılmasına denir. DI kullanmadan Retrofit’e loglama eklemek istediğimizde bunu tek yerde yapmak yerine tüm Retrofit nesnelerini oluşturduğumuz yerlerde yapmamız gerekir. Nesne oluşturma işini kodun genelinden ayırmış oluyoruz, bunu Separation of Concerns prensibinin başka bir uygulaması olduğunu söyleyebiliriz. DI Android mimarisinin önemli bir parçasıdır. DI ile katmanlar arasındakı bağlantıları kurabilir ve ayrıca kullanılan kaynaklar üzerinde kolayca kontrol sağlayabiliriz.

Mimari Android’de hangi problemleri çözer?

Unit Testler

Android’de mimari modeller ve DI kullanmamızın en büyük sebeplerinden birisi Unit test yazabilmektir. Unit test etmek istediğiniz birimi (sınıf, fonksiyon vb.) izole edip girdi ve çıktılarını kontrol etmekten geçer. Bunu sağlamak için test ettiğimiz birimin kullandığı diğer nesneleri(dependency) kontrol etmemiz gerekir. Bu kontrolü, gerçek nesneler yerine mock denilen, girdileri belirleyebildiğimiz ve çıktıların doğruluğunu kontrol edebildiğimiz nesneler ile sağlarız. DI kullanarak uygulama çalıştığında gerçek nesneleri, testler çalışırken de mock’lanmış nesneleri gerekli yerlere sağlayabiliriz.

Android’de Activity, Service, veFragment’dan türettiğimiz sınıflar ile Android işletim sistemi ile etkileşime geçeriz. Fakat bu unit testleri zorlaştırır, çünkü dışarıdan bizim sağladığımız kaynaklar yerine ana sınıflardan (genellikle Context'den) kalıtım olarak aldığımız kaynakları kullanırız ve bunları test sırasında kolaylıkla kontrol edemeyiz . Bu problemi en aza indirmek için Android sınıflarından türettiğimiz sınıfların içerisinde mümkün olduğunca az kod olsun isteriz. Şunu da belirtmek isterim ki static üyeleri mock’lamak her zaman mümkün olmadığı için kullanımı çok tavsiye edilmez. Mockito static’leri(Kotlin‘de companion object) mock’layamasa da , Mockk ve Power Mock gibi frameworkler static mock yapabilir. Fakat DI framework’leri ile singleton kullanmamıza gerek kalmaz.

Görüldüğü üzere unit test yazabilmek için kullandığımız mimari ile Android ile etkileşime giren kodu mümkün olduğunca izole ettik ve DI frameworkleri ile de birimlerin kullandıkları kaynakları kontrol edebilmiş olduk.

Activity Döngüsü Problemi

Bir önceki yazıda Activity hayat döngüsü problemlerinden bahsetmiştim. Konfigürasyon değişikliklerinde (genellikle ekran döndürülmesinde) Activity yok edilir ve yenisi oluşturulur. Bu sırada farklı bir thread’da API çağrısı gibi uzun süren bir işlem çalışıyorsa, main thread’de kullanıcıyı bilgilendirecek olan callback ilk Activity’i yerinde bulamaz. Eğer doğru olarak ele alınmazsa bu crashlara sebep olur. Bunun yanında uzun süreli işlemin sonucunu da kaybedebiliriz. Bazı durumlarda işlemi yeniden başlatabiliriz fakat para transferi gibi tek seferde yapılması gereken bir işlemse bu pek mümkün olmaz. Mimari katmanları Activity ya da Fragment yaşam döngüsünden etkilenmez ve sonucu o anda var olan Activity ya da Fragment’a iletebilir. Ayrıca hafızada tutulması gereken şeyleri Activity yerine logic katmanında tutmamız, Activity yeniden yaratıldığında bunların kaybolmasını engeller. Activity ile DI frameworkleri Activity tekrar yaratıldıktan sonra bağlantıları tekrar kurar. Böylece mimari modeller ve DI kullanarak Activity yaşam döngüsünden etkilenmeyiz.

Özellikle projenin sürdürülebilirliği konusunda bir çok artısı olsa da mimari kullanımının Android’e özgü faydalarından bahsetmeye çalıştım. Bahsettiklerimin dışında başka mimariler ve DI frameworkleri mevcut, bunları araştırıp kararı vermek size kalmış. Ayrıca bunları öğrenmenin ve özümsemenin biraz zor olabileceğini belirteyim, bu konuda endişeye kapılmayın. Neden kullanıldıklarını ve ne yaptıklarını anlamaya çalışın. Serinin son bölümü Andriod Architecture Components’a burdan erişebilirsiniz:

Görüşmek üzere.

--

--

Hadi Tok
GDGIstanbul

Google Developers Expert for Android | Software Engineer @Facebook | ⋰Ẍ⋱Circassian⋰Ẍ⋱