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

ViewModel sınıfı, bir iş mantığı veya ekran düzeyinde durum tutucudur. 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 bunu sürdürmesidir. Bu sayede, etkinlikler arasında gezinirken veya yapılandırma değişikliklerini (ör. ekranı döndürmek) takip ederken kullanıcı arayüzünün tekrar veri getirmesi gerekmez.

Eyalet sahipleri hakkında daha fazla bilgi için eyalet sahipleriyle ilgili kılavuzu inceleyin. Benzer şekilde, genel olarak kullanıcı arayüzü katmanı hakkında 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ünde görüntülediğiniz verileri barındıran düz bir sınıftır. Bu, etkinlikler veya Navigasyon hedefleri arasında gezinirken bir soruna neden olabilir. Bu işlem, örnek kaydetme durum mekanizmasını kullanarak depolamadığınız verileri kaldırır. 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 sahip olduğu durum hem de bir ViewModel'in tetiklediği işlemler aracılığıyla kalıcılığa olanak tanır. Bu önbelleğe alma, ekran döndürme gibi yaygın yapılandırma değişiklikleri aracılığıyla verileri tekrar getirmenize gerek olmadığı anlamına gelir.

Kapsam

ViewModel'i örneklendirdiğinizde ona ViewModelStoreOwner arayüzünü uygulayan bir nesne iletirsiniz. Bu bir Gezinme hedefi, gezinme grafiği, etkinlik, parça veya arayüzü uygulayan başka bir tür olabilir. ViewModel'iniz daha sonra ViewModelStoreOwner öğesinin Yaşam Döngüsü olarak ayarlanır. Uygulama, ViewModelStoreOwner kalıcı olarak kaldırılana kadar bellekte kalır.

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

ViewModel'in kapsamının ayarlandığı parça veya etkinlik kaldırıldığında, kapsama alınan ViewModel'de eşzamansız çalışma 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.

SavedStateHerkese Açık

SavedStateHandle, verileri yalnızca yapılandırma değişiklikleriyle değil, aynı zamanda süreç yeniden oluşturma yoluyla korumanıza olanak tanır. Diğer bir deyişle, 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 mevcut olsa da, kullanıcı arayüzü katmanı aynı zamanda iş mantığını da içerebilir. Bu durum, ekran kullanıcı arayüzü durumu oluşturmak için birden fazla depodan veri birleştirildiğinde veya belirli bir veri türünün veri katmanı gerektirmediği durumlarda geçerli olabilir.

ViewModel, kullanıcı arayüzü katmanındaki iş mantığını işlemek için doğru yerdir. ViewModel, uygulama verilerini değiştirmek için iş mantığının uygulanması gerektiğinde etkinlikleri işlemekten ve hiyerarşinin diğer katmanlarına yetki vermekten de sorumludur.

Jetpack Compose

Jetpack Compose'u kullanırken ViewModel, ekran kullanıcı arayüzü durumunu composable'larınıza göstermenin birincil yoludur. Karma bir uygulamada, etkinlikler ve parçalar yalnızca composable işlevlerinizi barındırır. Bu, eski yaklaşımlardan değişiyor. Etkinlikler ve parçalarla yeniden kullanılabilir kullanıcı arayüzü parçaları oluşturmanın o kadar basit ve sezgisel olmadığı, bu yüzden de kullanıcı arayüzü denetleyicileri olarak çok daha etkin olmalarını sağladı.

ViewModel'i Compose ile kullanırken unutulmaması gereken en önemli nokta, bir ViewModel'in kapsamını bir composable'a ayarlayamayacağınızdır. Bunun nedeni, bir composable'ın ViewModelStoreOwner olmamasıdır. Bestede aynı composable'ın iki örneği veya aynı ViewModelStoreOwner altında aynı ViewModel türüne erişen iki farklı composable, 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 ve ViewModelleri'ni Gezinme hedefine mümkün olduğunca yakın olan composable işlevlerde kullanın. Bunun nedeni, bir ViewModel'i Gezinme hedeflerine, Gezinme grafiklerine, Etkinliklere ve Parçalara dahil edebilmenizdir.

Daha fazla bilgi için Jetpack Compose için eyalet kaldırma rehberine bakın.

ViewModel Uygulama

Aşağıda, kullanıcının zar atmasına izin veren bir ekran için ViewModel uygulamasına bir örnek verilmiştir.

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
            )
        );
    }
}

Daha sonra, ViewModel'e bir etkinlikten aşağıdaki şekilde 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. Eşzamansız çalışmayı kullanıcı arayüzü durumunda olduğu gibi sürdürebilir.

Daha fazla bilgi için Android Mimari Bileşenleri ile Kotlin eş yordamlarını kullanma konusuna bakın.

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

Bir ViewModel'nın yaşam döngüsü doğrudan onun kapsamına bağlıdır. ViewModel öğesi, kapsama dahil olduğu 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ça durumunda, ayrıldığında.
  • Gezinme girişi olması durumunda, söz konusu giriş arka yığından kaldırıldığında.

Bu da ViewModels'i yapılandırma değişikliklerinden korkan verilerin depolanması için mükemmel bir çözüm haline getirir.

Şekil 1'de, bir etkinlik rotasyona tabi tutulup biten çeşitli yaşam döngüsü durumları gösterilmektedir. Resimde, ilişkilendirilmiş etkinlik yaşam döngüsünün yanında ViewModel kullanım ömrü de gösterilmektedir. Bu özel şema, bir etkinliğin durumunu gösterir. 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.

Genellikle sistem, bir etkinlik nesnesinin onCreate() yöntemini ilk kez çağırdığında ViewModel isteğinde bulunursunuz. Sistem, bir etkinlik devam ederken (ör. cihaz ekranı döndürüldüğünde) onCreate() numarasını birkaç kez çağırabilir. ViewModel, ilk ViewModel isteğinde bulunduğunuz andan etkinlik tamamlanıp kaldırılana kadar kullanımda kalır.

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

ViewModel, ViewModelStoreOwner tarafından yaşam döngüsü içinde kaldırıldığında onCleared yöntemini çağırır. Böylece ViewModel'in yaşam döngüsünü takip eden tüm işleri veya bağımlılıkları temizleyebilirsiniz.

Aşağıdaki örnekte viewModelScope için bir alternatif gösterilmektedir. viewModelScope, ViewModel'in yaşam döngüsünü otomatik olarak takip eden yerleşik bir CoroutineScope öğesidir. 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şturucusuna bir bağımlılık olarak 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ü sürüm 2.5 ve sonraki sürümlerinden, ViewModel örneği temizlendiğinde otomatik olarak kapanan ViewModel oluşturucusuna 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

ViewModel'i uygularken izlemeniz gereken bazı temel en iyi uygulamalar şunlardır:

  • Kapsamları nedeniyle, ViewModelleri bir ekran düzeyi durumu sahibinin uygulama ayrıntıları olarak kullanın. Bunları çip grupları veya formlar gibi yeniden kullanılabilir kullanıcı arayüzü bileşenlerinin durum sahipleri olarak kullanmayın. Aksi takdirde, çip başına açık bir görünüm modeli anahtarı kullanmadıkça, aynı ViewModelStoreOwner altında aynı kullanıcı arayüzü bileşeninin farklı kullanımlarında aynı ViewModel örneğini alırsınız.
  • ViewModel'ler, kullanıcı arayüzü uygulama ayrıntıları hakkında bilgi sahibi olmamalıdır. ViewModel API'nin sunduğu yöntemlerin adlarını ve kullanıcı arayüzü durum alanlarının adlarını mümkün olduğunca genel tutun. Bu şekilde, ViewModel'iniz her türlü kullanıcı arayüzünü kullanabilir: cep telefonu, katlanabilir cihaz, tablet, hatta Chromebook!
  • ViewModelleri, ViewModelStoreOwner cihazından 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 alakalı API referanslarını tutmamalıdır.
  • ViewModelleri başka sınıflara, işlevlere veya diğer kullanıcı arayüzü bileşenlerine geçirmeyin. Platform bunları yönettiği için bunları mümkün olduğunca birbirine yakın tutmalısınız. Etkinlik, parça veya ekran düzeyinde composable işlevinize yakın bir yerde. Bu, alt düzey bileşenlerin ihtiyaç duyduklarından daha fazla veriye ve mantığa erişmesini önler.

Daha fazla bilgi

Verileriniz daha karmaşık hale geldikçe yalnızca verileri yüklemek için ayrı bir sınıf oluşturmayı seçebilirsiniz. ViewModel'ın amacı, verilerin yapılandırma değişikliklerinden etkilenmemesi için kullanıcı arayüzü denetleyicisine yönelik verileri kapsüllemektir. Yapılandırma değişiklikleri genelinde verileri yükleme, kalıcı hale getirme 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'nda, bu işlevlerin işlenmesi için bir depo sınıfının oluşturulması önerilir.

Ek kaynaklar

ViewModel sınıfı hakkında daha fazla bilgi için aşağıdaki kaynaklara başvurun.

Dokümanlar

Numuneler