Google I/O’da tanıtılan ve aslında biz Android geliştiricileri oldukça heyecanlandıran en önemli gelişmelerden biri de Lifecyle-Aware Component’lerdi. Ben de yeni hakkında detaylı bir yazma fırsatı bulabiliyorum. 22 Temmuz’da Google Dome’da GDG Istanbul olarak gerçekleştireceğimiz codelab’in konularından biri de bu componentler. Merak edenlerle hep beraber inceleyebiliriz.

Componentlerin detaylarını konuşmaya başlamadan Android Activity Lifecycle’ı bir hatırlamakta fayda var. Aşağıdaki imaj aslında bize tüm akışı özetlemiş oluyor. Activity launch edildiğinde sırasıyla onCreate(Activity’nin yaratıldığı state), onStart(Activity’nin kullanıcıya visible olduğu state), onResume methodları trigger ediliyor ve Activity run edilmiş oluyor. Eğer üzerinde başka bir activity açılırsa, başka bir app’e giderse vs onPause methodu çağrılıyor. Activity sistem tarafından sonlanıyorsa ise onPause ardından, onStop(Activity tamamen görünmez olur) ve onDestroy methodları da tetikleniyor. Eğer ki memory ihtiyacı sebebiyle sonlanmışsa onDestroy’a girmeden sonlanıp tekrar açıldığından en baştan, yani onCreate’den süreç başlıyor. Ancak başka bir Activity açmak ya da farklı bir app’e geçip geri gelmek için Acitivity’den çıkıldıysa en baştan create edilmeyeceği için onCreate’e girmeyecektir. Bu durumda onResume’dan devam eder. Bu sebeple eğer ki onPause’da activity’den çıktığı için yaptığımız bir şey var ise, bunun karşılığına onResume’da dikkat etmemiz gerekir. onPause’la ilgili bir başka dikkat etmemiz gereken nokta Android 7’yle beraber multiwindow geldiğinden beri sadece bir tek acitivity aynı anda focus alabileceği için biri focus aldığında diğerinin onPause’a düşecek olmasıdır. Aynı zamanda Activity’imizin üzerine bir diyalog açıldığında da görünmesine rağmen focus üzerinde olmayacağı için onPause methoduna girecektir. Tabiki bu methodların hepsini implement etmek zorunda değiliz ama yapılan işin ne kadar kompleks olduğuna göre hangilerinin implement edilmesi gerektiği değişecektir, bunun için de her birinin ne zaman trigger edildiğini detaylı olarak bilmek çok önemlidir.

https://developer.android.com/guide/components/activities/activity-lifecycle.html

Peki o zaman Architecture componentlerin neden kullanmalıyız, niçin hayatımıza girdi dersek. Bu componentler, data tutarlılığını, lifecycle’ı yönetmemize yardımcı olmayı, uygulamamızın modüler olması, memory leak’lerden kaçınmamıza ve tekrar eden kodlardan kurtulmamıza yardımcı olmayı amaçlıyor. Tabi ki daha önce de her birimiz çeşitli yollarla bunu sağlıyorduk. Bu bazen bir lib kullanmak bazense kendi geliştirdiğimiz bir çözüm olabiliyordu.

Livedata temel olarak datadaki değişiklikleri izlememizi sağlayan observable bir data holderdır. Data değişikliklerindeki bizi notify eder, böylece ui’ı değiştirebiliriz. Livedata abstract class’dır, extend edebilir ya da MutableLiveData’yı kullanabiliriz. LiveData’yı Room‘la beraber kullandığımızda ise daha iyi sonuçlar elde edebiliriz. Livedata kullanımına örnek ekranda gösterdiğimiz listenin datanın değiştiğinde RecyclerView’i otomatik update etmesi olabilir mesela. LiveData LifeCycle aware bir componentdir. Aslında en önemli özelliği de burada karşımıza çıkıyor. Çünkü daha önce benzeri geliştirmeler yaptığımızda yaşabileceğimiz crashlerin Android’in kendisi tarafından handle edilmesini sağlıyor. Lifecycle-aware demekle yazının başındaki Activity’yi örnek alarak üzerine konuştuğumuz state’leri düşünebileceğiniz gibi fragmentleri, servisleri de düşünebilirsiniz. Şöyle özetleyebiliriz; Livedata activity’nizin ekranda mı, değil mi yoksa tamamen destroy mu olduğunu bilir ve updateleri buna göre gönderir. Bunun için ise iki interface vardır. Biri lifecycle owners, activity’ler, fragmentler; diğeri ise livecycle observers, livecycle owner’ı izleyip, lifecycle observersları notifiy eder. UI componentleri LiveData’yı, LiveData da LifeCycleOwner’ları observe eder.

Peki geriye bir tek şey kalıyor. Artık bizim componentlerimiz de Activity, Fragment’in state’lerinden haberdar ancak, örneğin her konfigürasyon değişikliğinde, telefonu rotate ettiğimizde de yeniden LiveData’nın create olmaması datayı koruması gerekir. Bunu da ViewModel‘le yapıyoruz. Bunun için de Activity’imiz için gerekli olan tüm datayı tutacağımız bir class yaratıp bunu AndroidViewModel’den extends etmemiz gerekir.

Her bir senaryo için mükemmel çalışacak bir mimari kurmak biraz zor olabilir ama architecture components’i developer.android‘de anlatıldığı şekilde açıklamaya çalışacağım.

Daha önce de yazdığım gibi, ViewModel Activity,Fragment için gerekli olan datanın tutulmasından sorumlu olacak. Ancak sorumluluğu bununla sınırlı kalır. Activity, Fragment’in recreation’larından, konfigürasyon değişikliklerinden etkilenmez.

Bu durumda örneğin bir kullanıcı sayfamız olacaksa, bunun activity ya da fragment ve xml’ine ek olarak bir de ViewModel sınıfımız olacak.

Bu arada yine developer.android’de yazılana göre şu an bu yapı stabil olmadığı için eğer kullanmak istiyorsak, LifecycleFragment’den extend ederek kullanmamız gerekiyor. Ama stabil olduktan sonra Fragment’den extend etmemiz yeterli olacakmış.

Şu an 3 modülümüz oldu. Bunları nasıl bağlayacağımızın cevabı ise LiveData. LiveData observable bir object. Data değişikliklerinde UI’ı trigger ediyor. Öncesinde bunu kendi geliştirdiğimiz çeşitli yöntemlerle ya da RxJava gibi kütüphanelerle sağlıyorduk. LiveData’yla ilgili de en muhteşem şey lifecycle-aware olması. İhtiyaç olmadığında referanslarını otomatik olarak temizlenmesi. Manuel yöntemlerle geliştirme yaptığımız en hataya açık kısım da burası olmuş oluyordu.

Artık LiveData data değiştiğinde bizi trigger ettiği için, aşağıdaki gibi bir yapı kurabiliriz. Böylece bir değişiklik olduğunda LiveData haber verecek ve biz de UI’ı update edebileceğiz.

Data değiştikçe ui’ın nasıl update edileceğinden konuştuk ama datayı nasıl nerede çekeceğimizi konuşmadık. Bunu ViewModel içerisinde yapmak da bir çözüm olabilir ancak o zaman başka ViewModel’lerde aynı data gerektiğinde aynı kodu replike etmemiz gerekir ayrıca birden fazla responsibility’nin aynı yerde implement edilmesine neden olur ki bu da seperation of concerns‘e uygun olmaz. Bu yüzden datanın çekilmesinden sorulu farklı bir class söz konusu olmaya başlıyor.

Aşağıdaki örnek retrofit kullanıldığı varsayılarak yazılmış.

Bu işlerden ise sorumlu olarak Repository modülleri atanabilir. Aslında buna Repository yerine farklı bir isim de verilebilir ancak developer.android’in verdiği isimlendirmeye bağlı kalmak bizi diğer developerlarla ortak bir dil konuşmamızı sağlar.

UserRepository ve ViewModel’i nasıl bağlayacağımız ise aşağıdaki gibidir.

Şu ana kadar konuşmadığımız konulardan biri de cache. Her app’in cache ihtiyacı bulunmamakla hatta bazıların cacheleme yapmama zorunluluğu olmasıyla birlikte ihtiyaç durumunda bu da Repository’nin sorumluluğunda olmalıdır. Eğer cache de data varsa bunu döndürüp yoksa yeni datayı çekebilir.

Konuşmadığımız bir diğer konu da data’nın sürekliliği.  Uygulama var olduğu sürece burada cache işimizi görebilir. Ancak kullanıcıyı uygulama saatlerce bırakır ve Android sistemi de uygulamayı kill ederse işler değişir. Yukarıdaki implementasyon bu case’de datanın tekrar çekilmesini sağlar. app’inizin yapısı gereği buna ihtiyacınız olacağı gibi, ihtiyacınız yoksa bu hem kullanıcı için kötü bir deneyim de gereksiz mobil data kullanımı olacaktır. Yine architecture componentlerle beraber hayatımıza giren Room da bu soruna cevap için geliştirilmiş durumda. Room’un çözümü SQLite ORM ya da Realm ın sunduğunda benzer bir çözüm aslında. Ancak bu yazıda Room’a çok fazla girmek istemiyorum. Özetle local storage yapıyor olarak düşünebiliriz.

Umarım architecture components’in neden çıktığı ve nasıl kullanılacağı hakkında genel bir bakış açısı yaratabilmişimdir. Daha detaylı yazılar da paylaşmaya çalışacağım. 🙂

Referanslar:

Reklamlar