Android ViewModel kullanım amacı ve basit bir örnek

Google, Android için MVVM mimarisini öneriyor. ViewModel ise bu mimarinin 3 ayağından birisi. Bu yazıda genel anlamıyla MVVM'den ziyade ViewModel'e neden ihtiyaç duyarız sorusuna cevap arayacağız.

Konu ile ilgili örnekler genelde çok detaylı olunca; ben, ya sıkılıyorum ya da konuyu anlayamıyorum. O yüzden eğer size de benim gibiyseniz, bana ViewModel'i en iyi anlatan çok basit bir örneği paylaşmak istiyorum.

State yönetimi, gerek ön yüz gerekse de arka yüz olsun hemen her uygulamanın beynini oluşturur. Activity ise Android'in ön yüzünün önemli bir bileşeni. Eğer state yönetimini Activity üzerinde eski yöntemlerle tutmaya kalkacak olursak Activity'nin durumuna göre state'i kaybedebiliriz. State'i kaybettiğimizde yeniden oluşturmamız gerekecek. Bu hem kaynak hem de zaman kaybı demek. Hatta duruma göre veriyi tamamıyla bile kaybedebiliriz.

Activity değişiklikleri

Yukarıdaki şemada basitçe Activity'nin durumlarını göstermeye çalıştım. Uygulamayı ilk açtığımızda Activity onCreate, onStart ve onResume durumlarından geçerek normal çalışma şeklini alıyor. Ekranı yan çevirdiğinizde, Activity onPause durumuna geçiyor ve Activity sonlandırılıyor. Çünkü Activity, Android ön yüzünün bir bileşeni. Ekran yan çevrildiğinde haliyle ön yüz ve nesnelerin şekli oldukça değişecek. Android'in Activity'i sonlandırıp tekrar oluşturması gerekiyor. Ancak bu değişikliklerin sadece görsellikte olması gerekiyor. Ekranın tuttuğu verinin aynen saklanması gerekir haliyle.

Telefon ekranı çevrildiğinde kullanıcı için aslında değişen bir şey yok. O, sadece ekranı çevirdiğini zannetse de arka planda Android, Activity'yi önce durduruyor daha sonra yeniden oluşturuyor. Bu durumda eğer Activity'nin kullandığı veriyi örneğin onCreate yordamında oluşturduysanız bu veri tekrardan oluşturulacak.

Çok çok basit bir örnek yapalım:

Empty Activity şablonunu kullanarak yeni bir Android projesi oluşturun. activity_main.xml dosyasındaki TextView kontrolüne bir id ekleyin:

<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".MainActivity">

    <TextView
        android:id="@+id/myTw"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Hello World!"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintRight_toRightOf="parent"
        app:layout_constraintTop_toTopOf="parent" />

</androidx.constraintlayout.widget.ConstraintLayout>

Yukarıda ben android:id="@+id/myTw" satırını ekledim.

Yeni bir Java sınıfı oluşturun. Herhangi bir isim olabilir.

package com.example.basicviewmodelex;

import androidx.lifecycle.ViewModel;

import java.text.SimpleDateFormat;
import java.util.Date;

public class MyDataGenerator {

    private String tarihSaat = null;

    public String getData(){
        if (tarihSaat == null){
            createData();
        }
        return tarihSaat;
    }

    public void createData(){
        SimpleDateFormat formatter = new SimpleDateFormat("dd/MM/yyyy HH:mm:ss");
        Date date = new Date();
        tarihSaat = formatter.format(date);
    }


}

Bu sınıf, bir örneği (instance) oluşturulduğunda oluşturulduğu tarihi tarihSaat değişkenine atıyor. Tek yaptığı bu.

Şimdi de MainActivity.class dosyasında bu sınıfın bir örneğini oluşturalım:

public class MainActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        MyDataGenerator myDataGenerator = new MyDataGenerator();
        String data = myDataGenerator.getData();

        TextView tw = findViewById(R.id.myTw);
        tw.setText(data);
    }
}

Şimdi projeyi çalıştırın ve metin kutusuna tarih ve saatin yazılmış olduğunu görün.

Activity ilk kez oluşturuldu

Buraya kadar her şey çok normal. Bu tarihi not alın ve telefonu yatay hale getirin.

Activity yatay hale getirildi

Tarihe dikkat edin. Activity yatay hale getirildiğinde ben aslında tarihi tekrar oluşturmak istemiyorum. ViewModel bileşeni bu noktada önemli hale geliyor. Uygulamalarımızın kullanıcıları için Activity bir şey ifade etmiyor. Onlar ekranı çevirseler de, uygulamayı geçici olarak kapatsalar da tekrar doğru veriyi görmek istiyorlar.

Şimdi kodumuzda 2 ufak değişiklik yapalım. MyDataGenerator sınıfını ViewModel sınıfını kullanarak oluşturalım. Kalan kısımlar aynen kalsın.

public class MyDataGenerator extends ViewModel

MainActivity dosyasında ise MyDataGenerator sınıfını çağırdığımız yeri aşağıdaki gibi güncelleyelim:

// MyDataGenerator myDataGenerator = new MyDataGenerator();
MyDataGenerator myDataGenerator = new ViewModelProvider(this).get(MyDataGenerator.class);

Hepsi bu kadar. Bu değişikliklerden sonra ekranı ters de çevirsek metin kutusunda hep aynı tarihi görüyoruz.

Activity dik konumda
Activity yatay konumda

ViewModel, Activity'nin onStop durumundan hiç etkilenmeden değerini korumayı başardı. ViewModel, Activity tamamen sonlana dek durumunu korumayı başaracak.

Örneği ekranı yan çevirme ile ilgili verdik. Bu en kolay gözlemlenebilir örnek olduğu için. Aslında Android'in Activity'i sonlandırabileceği tek yer yan çevirme değildir. Özellikle uygulama arka planda çalışırken Activity Android tarafından onPause konumuna geçebilir. Yani stabil bir state yapısına ihtiyacımız her durumda vardır.