MVVM Deseni ile Android Geliştirme Prensipleri

Betül Sarıcı
Huawei Developers - Türkiye
7 min readMay 31, 2022
MVVM

GİRİŞ

Herkese merhaba,

Bu yazımda, son zamanlarda adını sıklıkla duyduğumuz MVVM tasarım deseni kavramını basit bir “Note App” uygulaması üzerinde anlatacağım.

Yazılımcılar için asıl hedef az enerji harcayarak temiz, sürdürülebilir ve test edilebilir kodlarla proje geliştirmektir. Yazılımcıların bu amacına hizmet eden bu kavramı pratiğe dökmekle birlikte, gelin kendilerinden kısaca bahsedelim.

MVVM (Model-View-ViewModel)

“Separation of concern” mantığına dayanan bu tasarım deseni uygulamanın business logic kısmı ile kullanıcıya sunulan UI kısmını birbirinden ayırır. Bu şekilde proje üzerindeki herhangi bir noktada yapılan değişimin diğer alanları etkilemesini ve geliştirme sürecinin kesintiye uğramasını önler.

MVVM yapısını oluşturan yapıları daha yakından inceleyelim.

MVVM Katmanları

View: Kullanıcıya uygulamanın sunulduğu bir UI bütünüdür. Activity, fragment bileşenlerini içerir. Data veya iş mantığı içermez. ViewModel üzerinden aldığı veriyi kullanıcıya gösterir, kullanıcının UI üzerindeki eylemlerini ViewModel’a iletir.

Model: Model katmanında, kullanıcıya sunmak amacıyla kullanacağımız datalarımızı oluştururuz. Aynı zamanda oluşturduğumuz dataları barındırdığımız veritabanları bu katmanda oluşturulur.

Data source içinde adlandırabileceğimiz iki tip veritabanımız mevcuttur. 1.Remote Database — Oluşturacağımız projeye göre web servisleri ile dışarıdan datalar ile bağlantı sağlayabileceğimiz, API kullanarak oluşturabileceğimiz veritabanı tipidir.

2.Local Database — SQLite database kütüphanesinden faydalanarak verileri bu tür veritabanlarında saklamak mümkündür.

Repository- Remote database ve local database üzerinden verilere erişir. Bu şekilde View için daha temiz bir veri topluluğu sağlar. ViewModel, verileri bu sınıftan alarak View’a gönderir.

ViewModel: Kullanıcıya gösterilen UI ile dataların bulunduğu model katmanı arasında köprü görevi görür. Birbirleriyle iletişim kurmalarını sağlayarak repository üzerinde toplanan verileri kullanıcıya göstermek üzere hazırlar. LiveData yapısı burada üretilir ve kullanılır.

Kavramları özetle açıklamamızın ardından uygulama kısmına geçebiliriz.

Uygulama içinde MVVM paketleri

1.Model

Model katmanı

Bu katmanımızda farklı data kaynaklarını tanımlıyoruz. Öncelikle kullanıcıya sunacağımız verileri alacağımız ve kullanıcıdan aldığımız verileri depolayabileceğimiz bir alana ihtiyacımız var. Biz bunun için local database türlerinden olan Room database’ten faydalanacağız. build.gradle (module) üzerinde Room database için gerekli implement işlemlerini yaptıktan sonra NoteDatabase ve NoteDao olmak üzere iki sınıfa ihtiyacımız olacak.

NoteDao — Interface olarak oluşturduğumuz bu sınıfı, notların bulunduğu veritabanına erişebilmek için oluşturuyoruz. Burada, veritabanı üzerine notları ekleme (Insert), güncelleme (Update), veritabanından notları silme (Delete) veya almak (Query) için belirli fonksiyonlar tanımlıyoruz.

NoteDatabase — RoomDatabase ’i extend ederek abstract class şeklinde oluşturduğumuz sınıfımızda @Database(..) yapısını kullanarak içine veritabanına konulacak entity içeriğinin hangi sınıftan alınacağını ifade ediyoruz. NoteDao objesini bu sınıfa tanıttıktan sonra veritabanını oluşturduğumuz kod topluluğu aşağıda mevcuttur:

NoteRepository — Bu sınıfta verilere NoteDao aracılığıyla erişerek kullanıcının yaptığı ekleme, silme, değiştirme işlemlerinin veritabanı üzerinde gerçekleşmesiyle veritabanının son halini güncel kılarak verileri kullanıcıya hazırlıyoruz.

Note — Bu sınıfta entity yapısını kullanarak bir note ögesinin ne içereceğini, database üzerine hangi verileri depolayacağını tanımlıyoruz. Aynı zamanda ViewModel, View yapıları burada oluşturduğumuz entity yapıları üzerinden verileri alarak kullanıcıdan eylem bekler.

INoteClick — Oluşturduğumuz bu interface birden fazla sınıfın implement edebileceği onNoteClick ve onDeleteIconClick fonksiyonlarını içeriyor. Oluşturulan bu fonksiyonlar aynı paket içinde bulunan entity olarak yarattığımız Note data sınıfı içinden obje kullanır. Bizim projemizde bu iki fonksiyonu sadece MainActivity sınıfında çağırdık ancak talep edilmesi durumunda diğer sınıflar da implement ederek interface içinde bulunan fonksiyonlardan faydalanabilir.

Main Activity üzerinde implement edilmesini ve kullanımını inceleyelim:

2. View

Note App uygulamamızda inceleyecek olursak uygulamamızın View içeriği şu sınıflardan oluşmaktadır:

View Katmanı

Yukarıda da göreceğimiz üzere bu katman activity ve adapter içeriyor. Bu katmanda view ve UI bileşenlerini oluşturur, ViewModel ile iletişime geçmesini sağlarız.

AddEditNoteActivity & MainActivity — XML sayfalarında bulunan bileşenleri (buton, edit text vb.) ViewBinding kullanarak verilere tanıtmak üzere ayrıca sayfalar arası bağlantıları sağlayacak şekilde hazırlıyoruz. Sınıf içinde binding oluşturduktan sonra, bu değişken üzerinden XML üzerindeki bağlamak istediğimiz bir bileşeni çağırarak yapmasını istediğimiz eylemi çağırmamız yeterli olacaktır. Bunu findViewById yöntemi ile de yapabilmemiz mümkün. Ancak ViewBinding, bileşenleri teker teker tanımlamamızı önleyerek bizi kod fazlalığından kurtarır. Daha hızlı ve az maliyetlidir. ViewModel’i burada bağlayarak LiveData ile var olan güncel datayı .observe() metoduyla işleme alıyoruz. Kodlar üzerinde görelim:

Viewbinding ile sayfalar arası geçiş yaparak, AddEditNoteActivity sayfasındaki kodlar dolayısıyla kullanıcının not başlığı, içeriği oluşturması beklenirken notun oluşturulma zamanı, uygulamamız tarafından güncel bir şekilde sunulacak bir detay olacak:

NoteAdapter — Kodlarımızın içinde MainActivity sınıfında ayrıca NoteAdapter class ile bir bağlantı mevcuttur. Oluşturduğumuz adapter sınıfı UI ile çalışır, View’a aittir ancak adapter sınıfının data kaynağını ViewModel içerir. Ne işe yaradığı hakkında konuşacak olursak her bir note ögesini bir bütün halinde sunabilmek için yarattığımız liste yapısını RecyclerView içinde data layer katmanından gelen verilerle doldurmamızı sağlıyor. Bu verileri ekleyebilmek için liste ile bağlantı kurmasını sağlamalıyız. Bu durumda RecyclerView.Adapter uzantıları yardımımıza koşuyor.

3.ViewModel

ViewModel katmanı

NoteViewModel — Teorik olarak da bahsettiğimiz üzere bu sınıfımız datalar ile View arasında bağlantıyı kuracak. Yani içerisinde binding, activity veya fragment bağlantıları bulunmamalı.

Aynı zamanda data katmanında model üzerinde oluşturduğumuz repository verilerine burada ihtiyacımız oluyor. Data katmanındaki NoteDao fonksiyonlarını constructor içinde çağırarak repository ile bağlıyoruz. Bu şekilde dao aracılığıyla repository üzerinde toplanmış verileri, LiveData tipinde oluşturduğumuz listenin kullanabilmesini sağlayabiliriz. Listemizin LiveData şeklinde olmasının sebebinden bahsedecek olursak, LiveData, geliştirmemizin lifecycle’ı üzerinde olası bir aksaklık, uygulamanın durması durumunda mevcut güncel veriyi kullanmamızı sağlar, Lifecycle tekrar çalışır durumda geldiğinde ise veri akışı kaldığı yerden devam eder.

Kodlarımızın devamında göreceğimiz bir diğer yapı coroutine oluyor. Main thread üzerinde uzun süren işlerimiz çalıştırılıyorken diğer işler aksayabilir, bloklanabilir. Coroutine eşzamanlı olarak aynı thread üzerinde birden fazla işi yapmamıza yardımcı olabiliyorken aynı şekilde farklı thread ler üzerinde de çalışabiliyorken neden böyle bir şey olsun ki ? 😄 Bu sebeple ViewModel üzerinde coroutine yapısını oluştururuz. Dispatchers kullanımı ile farklı thread ler üzerinde işlemler gerçekleşir. Özellikle güncelleme, ekleme ve silme gibi işlemlerimiz database üzerinden gerçekleştirileceği için buradaki aksamalardan kaçınmalıyız. Bu sebeple kullandığımız dispatcher ın türüne dikkat çekelim : Dispatchers.IO. Bu tip Coroutine dispatcher, kurulan bağlantılarla alakalı sorunlardan kaçınmak için kullanılır. Biz de database veri akışında aksama veya sorunla karşılaşmamak için bu tip dispatcher dan faydalanabiliriz.

SONUÇ

Kodlarımızı tüm eylemleri gerçekleştirmek üzere hazırladık. Peki kullanıcı bu durumda ne görecek? Bu eylemleri nasıl gerçekleştirecek? XML şeklinde hazırladığımız UI sayfalarında bulunan bileşenlerle tabii. Bizim elimizde şu an da 3 XML sayfası mevcut durumda. Bunlara bir göz atıp, uygulamamızın nasıl çalıştığını birlikte görelim.

UI sayfaları

note_item.xml sayfamızda her bir not ürettiğimizde anasayfa üzerinde nasıl göründüğüne dair kod yapılarımız mevcut. Aşağıdaki ekran resminde gördüğümüz gibi her bir not ögesi için Note data sınıfında Entity olarak oluşturduğumuz veri tipleri görünmektedir.

activity_main

Her bir eklenen notun anasayfaya (activity_main.xml) üzerine yüklenmesiyle birlikte bu sayfanın sağ alt köşesinde gördüğümüz buton ile not başlığını, içeriğini oluşturmamızı ve kaydetmemizi sağlayan 2. bir sayfaya (activity_add_edit_note.xml) geçiş yapıyoruz. “Save Note” butonuna bastığımızda oluşturulan not anasayfamızda belirecektir.

activity_main -> activity_add_edit_note -> activity_main (not ekleme)

Oluşturduğumuz TEST1 notu içinde herhangi bir değişiklik yapmak istediğimizde ise TEST1 notuna bastığımızda karşımıza notumuzun detayları ve “Update note” butonu içeren sayfamız çıkıyor. Update edilmesi durumunda değişiklikler kaydediliyor ve anasayfada update edilen son zaman yenileniyor.

activity_add_edit_note -> activity_main (not güncelleme)

Anasayfada bulunan herhangi bir notu silmek istememiz durumunda, eklediğimiz delete ikonu ile kolay bir şekilde notumuzu silmemiz mümkün.

activity_main (not silme)

Gördüğümüz üzere yaptığımız uygulamalarda MVVM kullanmak işimizi kolaylaştırmakla kalmıyor, temiz kod yazmamızı da sağlıyor. Bu şekilde uygulamalarımızı sürdürülebilir ve güncellenebilir kılmakla birlikte test edilmesini de daha kolay hale getiriyor. Özellikle büyük projelerde etkisini daha fazla görebileceğimiz bu kavram için anlatacaklarım bu kadar. Faydalı bir yazı olduğunu umut ediyorum, tekrardan görüşmek dileğiyle! 😊

--

--