ViewModel'e genel bakış Android Jetpack'in bir parçasıdır.

ViewModel sınıfı, bir işletme mantığı veya ekran düzeyinde durum sahibidir. Durumu kullanıcı arayüzüne gösterir ve ilgili iş mantığını içerir. En önemli avantajı, durumu önbelleğe alması ve yapılandırma değişiklikleriyle devam ettirebilmesidir. Böylece etkinlikler arasında gezinirken veya ekranı döndürme gibi yapılandırma değişikliklerini takip ederken kullanıcı arayüzünüzün tekrar veri getirmesi gerekmez.

Devlet sahipleri hakkında daha fazla bilgi için devlet sahipleri kılavuzunu inceleyin. Benzer şekilde, kullanıcı arayüzü katmanı hakkında genel olarak daha fazla bilgi edinmek için kullanıcı arayüzü katmanı kılavuzuna bakın.

ViewModel'in avantajları

ViewModel'in alternatifi, kullanıcı arayüzünüzde görüntülediğiniz verileri tutan düz bir sınıftır. Bu durum, etkinlikler veya Navigasyon hedefleri arasında gezinirken bir soruna dönüşebilir. Bu işlem, örnek durum mekanizmasını kullanarak depolamazsanız bu verileri yok eder. ViewModel, veri kalıcılığı için bu sorunu çözen kullanışlı bir API sunar.

ViewModel sınıfının temel avantajları temelde ikidir:

  • Kullanıcı arayüzü durumunu korumanızı sağlar.
  • İş mantığına erişim sağlar.

Kalıcı

ViewModel, hem bir ViewModel'in barındırdığı durum hem de bir ViewModel'in tetiklediği işlemler aracılığıyla kalıcılığa izin verir. Bu önbelleğe alma, ekran rotasyonu gibi yaygın yapılandırma değişiklikleriyle verileri tekrar getirmenize gerek olmadığı anlamına gelir.

Kapsam

Bir ViewModel'i örneklediğinizde, buna ViewModelStoreOwner arayüzünü uygulayan bir nesne iletiyorsunuz. Bu bir Navigasyon hedefi, Gezinme grafiği, etkinlik, parça veya arayüzü uygulayan başka bir tür olabilir. Daha sonra ViewModel'iniz, ViewModelStoreOwner Yaşam Döngüsü kapsamına alınır. ViewModelStoreOwner kalıcı olarak ortadan kalkana kadar bellekte kalır.

Bir sınıf aralığı, ViewModelStoreOwner arayüzünün doğrudan veya dolaylı alt sınıflarıdır. Doğrudan alt sınıflar: ComponentActivity, Fragment ve NavBackStackEntry. Dolaylı alt sınıfların tam listesi için ViewModelStoreOwner referansına bakın.

ViewModel'in kapsamının aldığı parça veya etkinlik kaldırıldığında, eşzamansız çalışma, kapsamına alınan ViewModel'de devam eder. Kalıcılığın anahtarı budur.

Daha fazla bilgi için aşağıdaki ViewModel yaşam döngüsü ile ilgili bölüme bakın.

Kaydedilen Durum Tanımlayıcısı

SaveStateHandle, verileri yalnızca yapılandırma değişiklikleriyle değil, aynı zamanda işlem yeniden oluşturma yoluyla da tutmanıza olanak tanır. Yani, kullanıcı uygulamayı kapatıp daha sonra açtığında bile kullanıcı arayüzü durumunun değişmeden kalmasını sağlar.

İş mantığına erişim

İş mantığının büyük çoğunluğu veri katmanında bulunsa da kullanıcı arayüzü katmanı, iş mantığını da içerebilir. Bu durum, ekran kullanıcı arayüzü durumunu oluşturmak için birden fazla depodaki verileri birleştirirken veya belirli bir veri türünün veri katmanı gerektirmediği durumlarda geçerli olabilir.

ViewModel, kullanıcı arayüzü katmanında iş mantığını işlemek için doğru yerdir. ViewModel, etkinlikleri yönetmek ve uygulama verilerini değiştirmek için iş mantığının uygulanması gerektiğinde bunları hiyerarşinin diğer katmanlarına aktarmaktan da sorumludur.

Jetpack Compose

Jetpack Compose'u kullanırken ViewModel, ekran kullanıcı arayüzü durumunu oluşturduğunuz öğelerinize göstermenin birincil yöntemidir. Karma uygulamalarda, etkinlikler ve parçalar, oluşturulabileceğiniz işlevlerinizi barındırır. Bu, eski yaklaşımlardan bir değişim. Etkinlikler ve parçalarla yeniden kullanılabilir kullanıcı arayüzü parçaları oluşturmak, bu açıdan kullanıcı arayüzü denetleyicileri olarak çok daha aktif olmalarına neden olan basit ve sezgisel bir yaklaşım değildi.

ViewModel'i Compose ile birlikte kullanırken unutulmaması gereken en önemli nokta, bir ViewModel'in kapsamını derlenen bir öğeye ayarlayamayacağınızdır. Bunun nedeni, composable'ın ViewModelStoreOwner olmamasıdır. Bestedeki aynı kompozisyon öğesinin iki örneği veya aynı ViewModelStoreOwner altında aynı ViewModel türüne erişen iki farklı beste öğesi, ViewModel'in aynı örneğini alır. Bu, genellikle beklenen davranış değildir.

Compose'da ViewModel'in avantajlarından yararlanmak için her ekranı bir Parça veya Etkinlik'te barındırın ya da Gezinme Oluştur'u kullanın ve ViewModel'leri Gezinme hedefine olabildiğince yakın şekilde, oluşturulmuş işlevlerde kullanın. Çünkü bir ViewModel'in kapsamını Gezinme hedeflerine, Gezinme grafiklerine, Etkinliklere ve Parçalara ayarlayabilirsiniz.

Daha fazla bilgi için Jetpack Compose'un eyalet kaldırma rehberini inceleyin.

ViewModel uygulama

Aşağıda, kullanıcının zar atabilmesini sağlayan bir ekran için ViewModel'in örnek uygulaması gösterilmektedir.

Kotlin

data class DiceUiState(
    val firstDieValue: Int? = null,
    val secondDieValue: Int? = null,
    val numberOfRolls: Int = 0,
)

class DiceRollViewModel : ViewModel() {

    // Expose screen UI state
    private val _uiState = MutableStateFlow(DiceUiState())
    val uiState: StateFlow<DiceUiState> = _uiState.asStateFlow()

    // Handle business logic
    fun rollDice() {
        _uiState.update { currentState ->
            currentState.copy(
                firstDieValue = Random.nextInt(from = 1, until = 7),
                secondDieValue = Random.nextInt(from = 1, until = 7),
                numberOfRolls = currentState.numberOfRolls + 1,
            )
        }
    }
}

Java

public class DiceUiState {
    private final Integer firstDieValue;
    private final Integer secondDieValue;
    private final int numberOfRolls;

    // ...
}

public class DiceRollViewModel extends ViewModel {

    private final MutableLiveData<DiceUiState> uiState =
        new MutableLiveData(new DiceUiState(null, null, 0));
    public LiveData<DiceUiState> getUiState() {
        return uiState;
    }

    public void rollDice() {
        Random random = new Random();
        uiState.setValue(
            new DiceUiState(
                random.nextInt(7) + 1,
                random.nextInt(7) + 1,
                uiState.getValue().getNumberOfRolls() + 1
            )
        );
    }
}

Ardından, aşağıdaki şekilde bir etkinlikten ViewModel'e erişebilirsiniz:

Kotlin

import androidx.activity.viewModels

class DiceRollActivity : AppCompatActivity() {

    override fun onCreate(savedInstanceState: Bundle?) {
        // Create a ViewModel the first time the system calls an activity's onCreate() method.
        // Re-created activities receive the same DiceRollViewModel instance created by the first activity.

        // Use the 'by viewModels()' Kotlin property delegate
        // from the activity-ktx artifact
        val viewModel: DiceRollViewModel by viewModels()
        lifecycleScope.launch {
            repeatOnLifecycle(Lifecycle.State.STARTED) {
                viewModel.uiState.collect {
                    // Update UI elements
                }
            }
        }
    }
}

Java

public class MyActivity extends AppCompatActivity {
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        // Create a ViewModel the first time the system calls an activity's onCreate() method.
        // Re-created activities receive the same MyViewModel instance created by the first activity.
        DiceRollViewModel model = new ViewModelProvider(this).get(DiceRollViewModel.class);
        model.getUiState().observe(this, uiState -> {
            // update UI
        });
    }
}

Jetpack Compose

import androidx.lifecycle.viewmodel.compose.viewModel

// Use the 'viewModel()' function from the lifecycle-viewmodel-compose artifact
@Composable
fun DiceRollScreen(
    viewModel: DiceRollViewModel = viewModel()
) {
    val uiState by viewModel.uiState.collectAsStateWithLifecycle()
    // Update UI elements
}

ViewModel ile eş yordamları kullanma

ViewModel, Kotlin eş yordamlarını destekler. Bu işlev, kullanıcı arayüzü durumunu koruduğu gibi eşzamansız çalışmayı da sürdürebilir.

Daha fazla bilgi için Android Mimari Bileşenleri ile Kotlin eş yordamlarını kullanma bölümüne bakın.

Bir ViewModel'in yaşam döngüsü

ViewModel'in yaşam döngüsü, doğrudan kapsamına bağlıdır. ViewModel, kapsama dahil edildiği ViewModelStoreOwner kaybolana kadar bellekte kalır. Bu, aşağıdaki bağlamlarda gerçekleşebilir:

  • Bir etkinlik söz konusu olduğunda, etkinlik tamamlandığında.
  • Bir parçanın ayrılması durumunda.
  • Bir Navigasyon girişi söz konusu olduğunda, bu giriş arka yığından kaldırıldığında.

Bu açıdan ViewModels, yapılandırma değişikliklerinden sonra dayanabilen verileri depolamak için mükemmel bir çözümdür.

Şekil 1'de, bir aktivitenin rotasyondan geçip bittiği sırada çeşitli yaşam döngüsü durumları gösterilmektedir. Bu çizimde, ilişkili etkinlik yaşam döngüsünün yanında ViewModel'in kullanım ömrü de gösterilmektedir. Bu şemada bir etkinliğin durumları gösterilmektedir. Bir parçanın yaşam döngüsü için de aynı temel durumlar geçerlidir.

Etkinlik durumu değiştikçe ViewModel&#39;in yaşam döngüsünü gösterir.

Sistem bir etkinlik nesnesinin onCreate() yöntemini ilk kez çağırdığında genellikle bir ViewModel isteğinde bulunursunuz. Sistem, bir etkinlik olduğunda (ör. cihaz ekranı döndürüldüğünde) onCreate()'i birkaç kez çağırabilir. ViewModel, ViewModel için ilk istekte bulunmanızdan etkinlik bitip silinene kadar devam eder.

ViewModel bağımlılıklarını temizleme

ViewModelStoreOwner, yaşam döngüsü içinde görünümü yok ettiğinde ViewModel onCleared yöntemini çağırır. Bu sayede, ViewModel'in yaşam döngüsünü takip eden işleri veya bağımlılıkları temizleyebilirsiniz.

Aşağıdaki örnekte viewModelScope yerine bir alternatif gösterilmektedir. viewModelScope, ViewModel'in yaşam döngüsünü otomatik olarak takip eden yerleşik bir CoroutineScope'tir. ViewModel, işle ilgili işlemleri tetiklemek için bunu kullanır. Daha kolay test için viewModelScope yerine özel bir kapsam kullanmak isterseniz ViewModel, oluşturucuda bağımlılık olarak bir CoroutineScope alabilir. ViewModelStoreOwner, yaşam döngüsünün sonunda ViewModel'i temizlediğinde ViewModel, CoroutineScope öğesini de iptal eder.

class MyViewModel(
    private val coroutineScope: CoroutineScope =
        CoroutineScope(SupervisorJob() + Dispatchers.Main.immediate)
) : ViewModel() {

    // Other ViewModel logic ...

    override fun onCleared() {
        coroutineScope.cancel()
    }
}

Yaşam döngüsü 2.5 sürümünden ve sonraki sürümlerde, ViewModel örneği temizlendiğinde otomatik olarak kapanan ViewModel oluşturucuya bir veya daha fazla Closeable nesnesi aktarabilirsiniz.

class CloseableCoroutineScope(
    context: CoroutineContext = SupervisorJob() + Dispatchers.Main.immediate
) : Closeable, CoroutineScope {
    override val coroutineContext: CoroutineContext = context
    override fun close() {
        coroutineContext.cancel()
   }
}

class MyViewModel(
    private val coroutineScope: CoroutineScope = CloseableCoroutineScope()
) : ViewModel(coroutineScope) {
    // Other ViewModel logic ...
}

En iyi uygulamalar

Aşağıda, ViewModel'i uygularken izlemeniz gereken birkaç önemli en iyi uygulama verilmiştir:

  • Kapsamları nedeniyle ViewModels'i ekran düzeyinde durum sahibinin uygulama ayrıntıları olarak kullanın. Bunları çip grupları veya formlar gibi yeniden kullanılabilir kullanıcı arayüzü bileşenlerinin eyalet sahipleri olarak kullanmayın. Aksi takdirde, aynı ViewModel örneğini, aynı ViewModelStoreOwner altında aynı kullanıcı arayüzü bileşeninin farklı kullanımlarında elde edersiniz.
  • ViewModels, kullanıcı arayüzü uygulama ayrıntılarını bilmemelidir. ViewModel API'nin sunduğu yöntemlerin adlarını ve kullanıcı arayüzü durum alanlarının mümkün olduğunca genel adlarını koruyun. Bu şekilde, ViewModel'iniz her türlü kullanıcı arayüzünü barındırabilir: cep telefonu, katlanabilir, tablet, hatta Chromebook!
  • ViewModels, ViewModelStoreOwner ürününden daha uzun süre dayanabileceğinden bellek sızıntılarını önlemek için Context veya Resources gibi yaşam döngüsüyle ilgili API'lere referans içermemelidir.
  • ViewModel'leri diğer sınıflara, işlevlere veya diğer kullanıcı arayüzü bileşenlerine iletmeyin. Platform bunları yönettiği için onları mümkün olduğunca yakın bir yerde tutmalısınız. Etkinlik, parça veya ekran düzeyinde oluşturulabilen işlevinize yakın. Bu, alt düzey bileşenlerin ihtiyaç duyduğundan daha fazla veriye ve mantığa erişmesini önler.

Daha fazla bilgi

Verileriniz daha karmaşık hale geldikçe verileri yüklemek için ayrı bir sınıf kullanmayı tercih edebilirsiniz. ViewModel'in amacı, verilerin yapılandırma değişikliklerinden etkilenmesini sağlamak için bir kullanıcı arayüzü denetleyicisine ait verileri kapsüllemektir. Yapılandırma değişiklikleri genelinde verileri yükleme, saklama ve yönetme hakkında bilgi edinmek için Kayıtlı Kullanıcı Arayüzü Durumları bölümüne bakın.

Android Uygulama Mimarisi Kılavuzu, bu işlevleri işlemek için bir depo sınıfı derlemeyi önerir.

Ek kaynaklar

ViewModel sınıfı hakkında daha fazla bilgi edinmek için aşağıdaki kaynakları inceleyin.

Dokümanlar

Sana Özel