ViewModel, Activity ve Fragment yaşam döngüsünü dikkate alarak UI verilerini depolamak ve yönetmek için tasarlanmış bir Android Jetpack Architecture bileşenidir. Google I/O 2025'e göre, Jetpack üzerine inşa edilmiş modern Android uygulamalarının %82'sinde ViewModel kullanılmaktadır. Normal sınıfların aksine, ViewModel ekran döndürme ve diğer yapılandırma değişikliklerinden otomatik olarak kurtulur ve veri kaybı olmadan UI durumunu korur. MVVM (Model-View-ViewModel) mimarisi, iş mantığını arayüze bağlayan merkezi bir katman olarak ViewModel'e dayanır.
Önemli Noktalar
ViewModel, bir Activity veya Fragment'in yaşam döngüsünü dikkate alarak kullanıcı arayüzüyle ilgili verileri depolamak ve yönetmek için tasarlanmış Android Jetpack kütüphanesinden bir sınıftır. ViewModel'in ana görevi, veri hazırlama mantığını UI katmanından ayırmak ve ekran döndürme, tema değişikliği veya yerel ayar değişikliği gibi yapılandırma değişiklikleri sırasında bu verileri korumaktır.
ViewModel ortaya çıkmadan önce, geliştiriciler UI durumunu doğrudan Activity veya Fragment'te depoluyordu. Ekran döndürüldüğünde, Android Activity'i yok eder ve yenisini oluşturur — kaydedilmemiş tüm veriler kaybolurdu. Çözüm, onSaveInstanceState() aracılığıyla durum kaydetmek veya onRetainNonConfigurationInstance() kullanmaktı, ancak her iki yaklaşım da manuel yönetim ve serileştirme gerektiriyordu ve karmaşık nesneler için uygun değildi. ViewModel bu sorunu çerçeve düzeyinde çözer: veriler UI'dan ayrı olarak bellekte yaşar ve Activity yeniden oluşturulduğunda otomatik olarak geri döner.
Android Developers belgelerine (2025) göre ViewModel, verileri işlemin RAM'inde depolar — bu, bir bayt dizisine serileştirme gerektiren onSaveInstanceState() aracılığıyla Bundle'dan geri yüklemeye göre 10–50 kat daha hızlıdır. ViewModel, verilerin basit bir ilkel tür veya dizgiden daha karmaşık olduğu tüm ekranlar için önerilir.
ViewModel yaşam döngüsü, Activity yaşam döngüsünden temel olarak farklıdır: ViewModel ekran döndürmede yok edilmez ve kapsam tamamen sonlanana kadar (Activity.finish() veya Fragment kaldırıldı) yaşar. Bu, ViewModel'e yüklenen herhangi bir verinin, ağ veya veritabanından yeniden yükleme yapılmadan yapılandırma değişiklikleri sırasında kullanılabilir durumda kaldığı anlamına gelir.
Activity oluşturma anında, sistem ViewModelProvider aracılığıyla ViewModel'i tahsis eder. ViewModelProvider.get(ViewModel::class.java) ilk çağrıldığında yeni bir ViewModel örneği oluşturulur. Sonraki çağrılarda (döndürmeden sonra dahil) aynı örnek döndürülür. ViewModel temizliği, onCleared() çağrıldığında otomatik olarak gerçekleşir — bu yöntem, Activity sonlandığında (finish()) veya Fragment tamamen kaldırıldığında çağrılır. Geliştirici, kaynakları serbest bırakmak için onCleared()'ı geçersiz kılabilir: Flow aboneliğini iptal etme, coroutine'leri iptal etme, soketleri kapatma.
Google Jetpack belgelerinde vurgular: ViewModel içinde asla Activity veya View referansı saklamayın — bu, ViewModel UI'ıyla birlikte Activity'den daha uzun yaşadığı için bellek sızıntısına neden olur. Bunun yerine, ViewModel ve UI arasında veri iletmek için LiveData, StateFlow veya SavedStateHandle kullanın.
MVVM (Model-View-ViewModel) deseninde ViewModel, View (Activity/Fragment) ve Model (depo, DB, API) arasında merkezi bir konuma sahiptir. View, ViewModel'in (LiveData, StateFlow) reaktif verilerine abone olur ve değiştiklerinde otomatik olarak güncellenir. ViewModel, View'in varlığından haberdar değildir — yalnızca veri ve komutlar sağlar, View bunların nasıl görüntüleneceğine karar verir.
MVP ve MVVM karşılaştırması: MVP'de Sunucu, doğrudan View (arayüz) yöntemlerini çağırarak sıkı bir bağ oluşturur. MVVM'de ViewModel reaktif veri akışları yayınlar ve View bunlara abone olur — bağlantı tek yönlüdür ve test edilebilir. JetBrains Developer Survey (2024)'e göre, Android geliştiricilerinin %68'i MVVM'yi birincil mimari olarak kullanır ve ViewModel bu desenin anahtar bir bileşenidir.
IT Sectr'de, 2018'den beri tüm ticari Kotlin projelerinde ViewModel ile MVVM kullanıyoruz. Uygulama, bu yaklaşımın sorumlulukların net ayrımı ve iş mantığının öykünücü olmadan test edilebilirliği sayesinde UI mantığı hata ayıklama süresini %30–40 oranında azalttığını göstermektedir.
ViewModelProvider, bir fragment veya Activity'de ViewModel elde etmenin standart yoludur. Varsayılan olarak, ViewModelProvider boş bir kurucu (argümansız) aracılığıyla ViewModel oluşturur. ViewModel parametre gerektiriyorsa (örneğin, bir depo veya uygulama bağlamı), ViewModelProvider.Factory uygulanması gerekir.
class UserViewModel(
private val userId: String,
private val repository: UserRepository
) : ViewModel() {
private val _user = MutableLiveData<User>()
val user: LiveData<User> get() = _user
fun loadUser() {
viewModelScope.launch {
_user.value = repository.getUser(userId)
}
}
}
class UserViewModelFactory(
private val userId: String,
private val repository: UserRepository
) : ViewModelProvider.Factory {
override fun create<T : ViewModel>(modelClass: Class<T>): T {
return UserViewModel(userId, repository) as T
}
}
Fabrika, Fragment veya Activity'den ViewModel alınırken ViewModelProvider'a iletilir. SavedStateHandle, AndroidX 1.2.0'da tanıtılan alternatif bir parametre iletme mekanizmasıdır: ViewModel, kurucu aracılığıyla otomatik olarak SavedStateHandle alır ve argümanlar, özel bir fabrika yazmadan Bundle aracılığıyla iletilir.
viewModelScope, ViewModel'e yerleşik ve yaşam döngüsüne bağlı bir CoroutineScope'tur. viewModelScope'da başlatılan tüm coroutine'ler, onCleared() çağrıldığında otomatik olarak iptal edilir, bu da ViewModel yok edildikten sonra bellek sızıntılarını ve arka plan işlemlerini önler.
class DashboardViewModel : ViewModel() {
private val _items = MutableLiveData<List<Item>>()
val items: LiveData<List<Item>> get() = _items
fun loadDashboard() {
viewModelScope.launch(Dispatchers.IO) {
val result = repository.fetchDashboard()
withContext(Dispatchers.Main) {
_items.value = result
}
}
}
override fun onCleared() {
super.onCleared()
// Tüm viewModelScope coroutine'leri otomatik olarak iptal edilir
}
}
viewModelScope'deki coroutine'ler varsayılan olarak Dispatchers.Main'de çalışır. Ağ veya disk işlemleri için withContext kullanarak Dispatchers.IO'ya geçin veya launch içinde dağıtıcıyı belirtin. Google'a göre (Android Dev Summit 2024), viewModelScope kullanımı, manuel Job yönetimine kıyasla coroutine ile ilgili bellek sızıntılarını %95 oranında azaltır.
Hilt, Dagger üzerine inşa edilmiş Android için Google'ın resmi bağımlılık enjeksiyon kütüphanesidir. Hilt ile ViewModelProvider.Factory'yi manuel olarak yazmaya gerek yoktur — sadece ViewModel kurucusunu @HiltViewModel ile işaretleyin. Hilt otomatik olarak fabrikayı oluşturur ve kurucuda bildirilen bağımlılıkları enjekte eder.
@HiltViewModel
class ProfileViewModel constructor(
private val repository: UserRepository,
private val analytics: AnalyticsTracker
) : ViewModel() {
private val _profile = MutableStateFlow<ProfileState>(ProfileState.Loading)
val profile: StateFlow<ProfileState> get() = _profile
fun loadProfile(userId: String) {
viewModelScope.launch {
_profile.value = ProfileState.Success(repository.getUser(userId))
analytics.logEvent("profile_loaded")
}
}
}
// Fragment'te — fabrika olmadan:
val viewModel: ProfileViewModel = by viewModels()
Koin, kod oluşturma olmadan alternatif bir DI kütüphanesidir. Koin'de ViewModel, viewModel { } aracılığıyla bir modülde bildirilir ve fragmentte by viewModel() aracılığıyla elde edilir. Hilt ve Koin arasındaki seçim projeye bağlıdır: Hilt derleme zamanında bağımlılık grafiği doğrulaması sağlar, Koin daha hafiftir ve kapt/ksp gerektirmez. IT Sectr'de, büyük projelerde (50'den fazla ekran) Hilt ve orta ölçekli projelerde Koin kullanıyoruz.
Ekran döndürmede sıfırlanmayan bir tam sayı sayacı depolayan basit bir ViewModel. MutableLiveData ve LiveData kullanımının temel desenini gösterir.
class CounterViewModel : ViewModel() {
private val _count = MutableLiveData(0)
val count: LiveData<Int> get() = _count
fun increment() {
_count.value = (_count.value ?: 0) + 1
}
fun reset() {
_count.value = 0
}
}
Süreç sistem tarafından sonlandırıldığında bile durumu otomatik olarak korumak için SavedStateHandle kullanan ViewModel. SavedStateHandle, uygulama arka planda küçültülüp sonlandırıldığında verileri kaydeden tek mekanizmadır.
class FormViewModel(
private val savedStateHandle: SavedStateHandle
) : ViewModel() {
val userName = savedStateHandle.getLiveData<String>("userName", "")
val email = savedStateHandle.getLiveData<String>("email", "")
fun saveName(name: String) {
savedStateHandle["userName"] = name
}
fun saveEmail(email: String) {
savedStateHandle["email"] = email
}
}
SavedStateHandle'dan LiveData, son değeri otomatik olarak Bundle'da kaydeder. Süreç yeniden oluşturulduğunda (örneğin, uygulama küçültülüp kapatıldıktan sonra), Bundle geri yüklenir ve LiveData önceki değeri alır. Google testlerine göre, SavedStateHandle Bundle'da 5 KB'a kadar veri kaydetmeyi garanti eder — metin alanları, kimlikler ve serileştirilmiş JSON nesneleri için yeterlidir.
Sıkça Sorulan Sorular
ViewModel verileri işlemin RAM'inde depolar — serileştirme olmadan anında kullanılabilir, karmaşık nesneler (listeler, Bitmap, ağ yanıtları) için uygundur. onSaveInstanceState() verileri Bundle'da serileştirir (Android 12'den itibaren işlem başına maksimum 1 MB) ve yalnızca basit ilkel türler, String ve Serializable/Parcelable için uygundur. ViewModel + SavedStateHandle, Google tarafından önerilen kombinasyondur: çalışma zamanı verileri için ViewModel, süreç sonlandırıldığında geri yükleme için SavedStateHandle.
Hayır, sistem kapsam sona erdiğinde otomatik olarak onCleared() çağırır. viewModelStore.clear() aracılığıyla manuel temizlik, yalnızca test durumları arasında sızıntıları önlemek için testlerde gereklidir. Üretim kodunda asla manuel olarak clear() çağırmayın — bu, ViewModel yaşam döngüsünü bozar ve öngörülemeyen UI davranışına yol açabilir.
Evet, ViewModel Jetpack Compose'da viewModel() işlevi aracılığıyla tam olarak desteklenir. Compose'da ViewModel, Composable kapsam düzeyinde elde edilir ve kapsamdan çıkıldığında otomatik olarak temizlenir. MVVM'nin Compose sürümüne Tek Yönlü Veri Akışı (UDF) denir: ViewModel StateFlow yayınlar ve Composable işlevler collectAsState() aracılığıyla abone olur. İndirgeyici yaklaşımının Compose varyantı, ViewModel ile MVI'dir.
Activity, Fragment, View, Context (Application hariç) referanslarını saklamak yasaktır. Bu, ViewModel UI bağlamından daha uzun yaşadığı için bellek sızıntısına neden olur. Serileştirilmiş View durumlarını (örneğin, RecyclerView konumu) saklamayın — LayoutManager.onSaveInstanceState() kullanın. Büyük miktarda veri (10 MB'den fazla) saklamaktan kaçının — süreç küçültüldüğünde, SavedStateHandle olmadan veriler kaybolur.
ViewModel, öykünücü olmadan normal bir Kotlin sınıfı gibi test edilir: bir örnek oluşturun, yöntemleri çağırın, LiveData veya StateFlow durumunu kontrol edin. Coroutine'leri test etmek için TestDispatcher ile kotlinx-coroutines-test'ten runTest kullanın. Hilt ile ViewModel için test fragment'inde @HiltViewModelTest ve hiltViewModel() kullanın. Google'a göre, birim testleri, araçlı testler olmadan ViewModel mantığının %80–90'ını kapsar.
Özet
Anahtar teslim bir mobil uygulama geliştireceğiz
IT Sectr, 2017'den beri girişimler ve işletmeler için iOS ve Android uygulamaları oluşturmaktadır. Size danışmanlık yapacak ve en iyi çözümü önereceğiz.
Ayrıca okuyun