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ı kapsar. En önemli avantajı durumu önbelleğe alması ve yapılandırma değişiklikleri boyunca sürdürebilmesidir. 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 verileri tekrar getirmesi gerekmez.

Devlet sahipleri hakkında daha fazla bilgi için eyalet sahiplerinin rehberini 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ünüzde görüntülediğiniz verileri tutan düz bir sınıftır. Bu durum, etkinlikler arasında gezinirken veya Navigasyon hedefleri arasında gezinirken sorun oluşturabilir. Bu işlem, örnek durum mekanizmasını kullanarak depolamazsanız 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ı şunlardır:

  • 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 izin verir. Bu önbelleğe alma, ekran rotasyonu gibi yaygın yapılandırma değişiklikleri üzerinden verileri tekrar getirmenize gerek olmadığı anlamına gelir.

Kapsam

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

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ındaki parça veya etkinlik kaldırıldığında, eşzamansız çalışma, kapsama 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ü bölümüne bakın.

SavedStateHandleez

SavedStateHandle, verileri yalnızca yapılandırma değişiklikleriyle değil, aynı zamanda işlem yeniden oluşturma süreciyle de tutmanıza olanak tanır. Yani, kullanıcı uygulamayı kapatıp daha sonra açtığında bile kullanıcı arayüzü durumunu olduğu gibi korumanızı 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ı, iş mantığını da içerebilir. Ekran kullanıcı arayüzü durumunu oluşturmak için birden fazla depodan veri birleştirirken veya belirli bir veri türü veri katmanı gerektirmediğinde bu durum geçerli olabilir.

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

Jetpack Compose

Jetpack Compose'u kullanırken ViewModel'i kullanarak ekran kullanıcı arayüzü durumlarını composable'lara gösterebilirsiniz. Karma uygulamalarda, etkinlikler ve parçalar composable işlevlerinizi barındırır. Bu, etkinlikler ve parçalarla yeniden kullanılabilir kullanıcı arayüzü parçaları oluşturmanın basit ve sezgisel olmadığı geçmiş yaklaşımlardan uzaklaşıp kullanıcı arayüzü denetleyicileri olarak çok daha aktif olmalarını sağlayan bir geçiş.

ViewModel'i Compose ile kullanırken unutulmaması gereken en önemli şey, ViewModel'i bir composable'a ayarlayamayacağınızdır. Bunun nedeni, composable'ın ViewModelStoreOwner olmamasıdır. Beste içindeki aynı composable'ın iki örneği veya aynı ViewModel türüne erişen iki farklı composable, aynı ViewModelStoreOwner altında 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 Etkinlikte barındırın ya da Oluşturma Gezinme özelliğini kullanın ve ViewModel öğelerini Gezinme hedefine mümkün olduğunca yakın composable işlevlerde kullanın. Çünkü bir ViewModel’i Gezinme hedeflerine, Gezinme grafiklerine, Etkinliklere ve Parçalara kapsama alabilirsiniz.

Daha fazla bilgi için Jetpack Compose'da eyalet yükseltme rehberini inceleyin.

ViewModel uygulama

Aşağıda, kullanıcının zar atmasına izin veren bir ekran için ViewModel'in uygulanması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 şu ş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. Sistem, kullanıcı arayüzü durumunu koruduğu gibi eşzamansız çalışmayı da devam ettirebilir.

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ü

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

  • Bir etkinlik olduğunda, etkinlik bittiğinde.
  • Ayrılan parça olması durumunda.
  • Bir Navigasyon girişi olması durumunda, giriş arka yığından kaldırıldığında.

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

Şekil 1'de bir aktivite rotasyona girip ardından biterken oluşan çeşitli yaşam döngüsü durumları gösterilmektedir. Bu görselde, ilişkili etkinlik yaşam döngüsünün yanında ViewModel ömrü de gösterilmektedir. Bu şemada, bir etkinliğin durumu gösterilmektedir. Bir parçanın yaşam döngüsü için de aynı temel durumlar geçerlidir.

Etkinlik durumu değiştiğinde bir 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, cihaz ekranı döndürüldüğünde olduğu gibi bir etkinliğin varlığı boyunca 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 modeli yok ettiğinde ViewModel onCleared yöntemini çağırır. Bu, ViewModel'in yaşam döngüsünü takip eden işleri veya bağımlılıkları temizlemenize olanak tanır.

Aşağıdaki örnekte viewModelScope'in bir alternatifi gösterilmektedir. viewModelScope, ViewModel'in yaşam döngüsünü otomatik olarak takip eden yerleşik bir CoroutineScope'dir. ViewModel, işle ilgili işlemleri tetiklemek için bu modeli kullanır. Daha kolay test yapmak için viewModelScope yerine özel bir kapsam kullanmak isterseniz ViewModel, oluşturucusuna 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 ve sonraki sürümlerinden, ViewModel örneği temizlendiğinde otomatik olarak kapanan ViewModel oluşturucuya bir veya daha fazla Closeable nesnesi geçirebilirsiniz.

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 birkaç önemli en iyi uygulama aşağıda verilmiştir:

  • Kapsamları nedeniyle ViewModel'leri, 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 durum 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 alırsınız.
  • ViewModels, kullanıcı arayüzü uygulama ayrıntıları hakkında bilgi sahibi olmamalıdır. ViewModel API'nin sunduğu yöntemlerin ve kullanıcı arayüzü durumu alanlarının adlarını mümkün olduğunca genel bir şekilde belirtin. Bu sayede ViewModel'iniz cep telefonu, katlanabilir cihaz, tablet ve hatta Chromebook olmak üzere her tür kullanıcı arayüzünü barındırabilir.
  • ViewModel'ler, ViewModelStoreOwner'dan 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 vermemelidir.
  • ViewModel'leri diğer sınıflara, işlevlere veya diğer kullanıcı arayüzü bileşenlerine geçirmeyin. Platform bunları yönettiğinden kullanıcıları mümkün olduğunca yakın tutmalısınız. Etkinlik, parça veya ekran düzeyinde composable işlevinizin yakınında. 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, verileri yüklemek için ayrı bir sınıf oluşturmayı tercih edebilirsiniz. ViewModel'in amacı, verilerin yapılandırma değişikliklerinden sonra da dayanmasını sağlamak için bir kullanıcı arayüzü denetleyicisine ait verileri kapsüllemektir. Yapılandırma değişikliklerindeki verileri yükleme, saklama ve yönetme hakkında bilgi edinmek için Kayıtlı Kullanıcı Arayüzü Durumları'na göz atın.

Android Uygulama Mimarisi Kılavuzu'nda, bu işlevleri işlemek için bir depo sınıfı derleyebilirsiniz.

Ek kaynaklar

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

Dokümanlar

Numuneler