ViewModel — komponen Android Jetpack Architecture yang dirancang untuk menyimpan dan mengelola data UI dengan mempertimbangkan siklus hidup Activity dan Fragment. Menurut Google I/O 2025, ViewModel digunakan di 82% aplikasi Android modern yang dibangun di atas Jetpack. Tidak seperti kelas biasa, ViewModel secara otomatis bertahan dari rotasi layar dan perubahan konfigurasi lainnya, mempertahankan status UI tanpa kehilangan data. Arsitektur MVVM (Model-View-ViewModel) mengandalkan ViewModel sebagai lapisan sentral yang menghubungkan logika bisnis dengan antarmuka.
Poin utama
ViewModel — kelas dari pustaka Android Jetpack yang dirancang untuk menyimpan dan mengelola data yang terkait dengan antarmuka pengguna, dengan mempertimbangkan siklus hidup Activity atau Fragment. Tugas utama ViewModel adalah memisahkan logika persiapan data dari lapisan UI dan mempertahankan data ini saat perubahan konfigurasi seperti rotasi layar, perubahan tema, atau lokalisasi.
Sebelum munculnya ViewModel, pengembang menyimpan status UI langsung di Activity atau Fragment. Saat rotasi layar, Android menghancurkan Activity dan membuat yang baru — semua data yang tidak disimpan akan hilang. Solusinya adalah menyimpan status melalui onSaveInstanceState() atau menggunakan onRetainNonConfigurationInstance(), tetapi kedua pendekatan memerlukan pengelolaan manual, serialisasi, dan tidak cocok untuk objek kompleks. ViewModel memecahkan masalah ini di tingkat framework: data hidup di memori terpisah dari UI dan secara otomatis kembali saat Activity dibuat ulang.
Menurut dokumentasi Android Developers (2025), ViewModel menyimpan data di RAM proses — ini 10–50 kali lebih cepat daripada pemulihan dari Bundle melalui onSaveInstanceState(), yang memerlukan serialisasi ke array byte. ViewModel direkomendasikan untuk semua layar di mana data lebih kompleks dari primitif atau string sederhana.
Siklus hidup ViewModel secara fundamental berbeda dari siklus hidup Activity: ViewModel tidak dihancurkan saat rotasi layar dan hidup hingga penyelesaian penuh scope (Activity.finish() atau Fragment removed). Ini berarti bahwa data apa pun yang dimuat ke ViewModel tetap tersedia saat perubahan konfigurasi tanpa memuat ulang dari jaringan atau basis data.
Saat Activity dibuat, sistem mengalokasikan ViewModel melalui ViewModelProvider. Pada panggilan pertama ViewModelProvider.get(ViewModel::class.java), instance ViewModel baru dibuat. Pada panggilan berikutnya (termasuk setelah rotasi), instance yang sama dikembalikan. Pembersihan ViewModel terjadi otomatis saat onCleared() dipanggil — metode ini dipanggil ketika Activity selesai (finish()) atau Fragment sepenuhnya dihapus. Pengembang dapat mengganti onCleared() untuk membebaskan sumber daya: berhenti berlangganan dari Flow, membatalkan coroutine, menutup soket.
Google dalam dokumentasi Jetpack menekankan: jangan pernah menyimpan referensi ke Activity atau View di dalam ViewModel — ini menyebabkan kebocoran memori karena ViewModel hidup lebih lama dari Activity dengan UI. Sebagai gantinya, gunakan LiveData, StateFlow, atau SavedStateHandle untuk mentransfer data antara ViewModel dan UI.
Dalam pola MVVM (Model-View-ViewModel), ViewModel menempati posisi sentral antara View (Activity/Fragment) dan Model (repositori, basis data, API). View berlangganan data reaktif ViewModel (LiveData, StateFlow) dan secara otomatis diperbarui saat data berubah. ViewModel tidak tahu tentang keberadaan View — ia hanya menyediakan data dan perintah, dan View memutuskan bagaimana menampilkannya.
Perbandingan MVP dan MVVM: di MVP, Presenter secara langsung memanggil metode View (antarmuka), menciptakan ikatan yang erat. Di MVVM, ViewModel memublikasikan aliran data reaktif, dan View berlangganan padanya — komunikasi bersifat satu arah dan dapat diuji. Menurut survei JetBrains Developer Survey (2024), 68% pengembang Android menggunakan MVVM sebagai arsitektur utama, dan ViewModel adalah komponen kunci dari pola ini.
Di IT Sectr, kami menerapkan MVVM dengan ViewModel sejak 2018 di semua proyek komersial di Kotlin. Praktik menunjukkan bahwa pendekatan ini mengurangi waktu debugging logika UI sebesar 30–40% berkat pemisahan tanggung jawab yang jelas dan kemampuan pengujian logika bisnis tanpa emulator.
ViewModelProvider — cara standar untuk mendapatkan ViewModel di fragment atau Activity. Secara default, ViewModelProvider membuat ViewModel melalui konstruktor kosong (tanpa argumen). Jika ViewModel memerlukan parameter (misalnya repositori atau konteks aplikasi), perlu mengimplementasikan ViewModelProvider.Factory.
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
}
}
Pabrik diteruskan ke ViewModelProvider saat mendapatkan ViewModel dari Fragment atau Activity. SavedStateHandle — mekanisme alternatif untuk mengirimkan parameter, muncul di AndroidX 1.2.0: ViewModel secara otomatis menerima SavedStateHandle melalui konstruktor, dan argumen dikirimkan melalui Bundle tanpa menulis pabrik sendiri.
viewModelScope — CoroutineScope yang terpasang di ViewModel dan terikat pada siklus hidupnya. Semua coroutine yang dijalankan di viewModelScope secara otomatis dibatalkan saat onCleared() dipanggil, mencegah kebocoran memori dan operasi latar belakang setelah ViewModel dihancurkan.
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()
// Semua coroutine viewModelScope dibatalkan secara otomatis
}
}
Coroutine di viewModelScope secara default berjalan di Dispatchers.Main. Untuk operasi jaringan atau disk, beralihlah ke Dispatchers.IO dengan withContext atau tentukan dispatcher di launch. Menurut Google (Android Dev Summit 2024), penggunaan viewModelScope mengurangi kebocoran memori terkait coroutine sebesar 95% dibandingkan dengan pengelolaan Job manual.
Hilt — pustaka dependency injection resmi dari Google untuk Android, dibangun di atas Dagger. Dengan Hilt, tidak perlu menulis ViewModelProvider.Factory secara manual — cukup anotasi konstruktor ViewModel dengan anotasi @HiltViewModel. Hilt secara otomatis membuat pabrik dan menyuntikkan dependensi yang dideklarasikan di konstruktor.
@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")
}
}
}
// Di Fragment — tanpa pabrik:
val viewModel: ProfileViewModel = by viewModels()
Koin — pustaka DI alternatif tanpa pembuatan kode. Di Koin, ViewModel dideklarasikan dalam modul melalui viewModel { }, dan di fragment diperoleh melalui by viewModel(). Pilihan antara Hilt dan Koin tergantung pada proyek: Hilt menyediakan pemeriksaan graf dependensi pada tahap kompilasi, Koin — lebih ringan dan tidak memerlukan kapt/ksp. Di IT Sectr, kami menggunakan Hilt di proyek besar (lebih dari 50 layar) dan Koin di proyek menengah.
ViewModel paling sederhana yang menyimpan penghitung bilangan bulat yang tidak disetel ulang saat rotasi layar. Mendemonstrasikan pola dasar penggunaan MutableLiveData dan LiveData.
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
}
}
ViewModel yang menggunakan SavedStateHandle untuk menyimpan status secara otomatis bahkan saat proses dihancurkan oleh sistem. SavedStateHandle adalah satu-satunya mekanisme yang mempertahankan data saat aplikasi diminimalkan ke latar belakang dan diakhiri.
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
}
}
LiveData dari SavedStateHandle secara otomatis menyimpan nilai terakhir di Bundle. Saat proses dibuat ulang (misalnya setelah diminimalkan dan aplikasi dimatikan), Bundle dipulihkan dan LiveData menerima nilai sebelumnya. Menurut pengujian Google, SavedStateHandle menjamin penyimpanan hingga 5 KB data di Bundle — cukup untuk bidang teks, ID, dan objek JSON yang diserialisasi.
Pertanyaan yang sering diajukan
ViewModel menyimpan data di RAM proses — data tersedia seketika tanpa serialisasi, cocok untuk objek kompleks (daftar, Bitmap, respons jaringan). onSaveInstanceState() menyerialisasi data ke Bundle (maksimum 1 MB per transaksi mulai Android 12) dan hanya cocok untuk primitif sederhana, String, dan Serializable/Parcelable. ViewModel + SavedStateHandle — kombinasi yang direkomendasikan Google: ViewModel untuk data runtime, SavedStateHandle untuk pemulihan saat proses dimatikan.
Tidak, sistem secara otomatis memanggil onCleared() saat scope selesai. Pembersihan manual melalui viewModelStore.clear() hanya diperlukan dalam pengujian untuk mencegah kebocoran antar kasus uji. Dalam kode produksi jangan pernah memanggil clear() secara manual — ini melanggar siklus hidup ViewModel dan dapat menyebabkan perilaku UI yang tidak terduga.
Ya, ViewModel sepenuhnya didukung di Jetpack Compose melalui fungsi viewModel(). Di Compose, ViewModel diperoleh di tingkat scope Composable dan secara otomatis dibersihkan saat keluar dari scope. Versi Compose dari MVVM disebut Unidirectional Data Flow (UDF): ViewModel memublikasikan StateFlow, dan fungsi Composable berlangganan melalui collectAsState(). Varian Compose dari pendekatan reducer — MVI dengan ViewModel.
Dilarang menyimpan referensi ke Activity, Fragment, View, Context (kecuali Application). Ini menyebabkan kebocoran memori karena ViewModel hidup lebih lama dari konteks UI. Jangan simpan status View yang diserialisasi (misalnya posisi RecyclerView) — gunakan LayoutManager.onSaveInstanceState(). Hindari menyimpan data dalam jumlah besar (lebih dari 10 MB) — saat proses diminimalkan, data akan hilang tanpa SavedStateHandle.
ViewModel diuji seperti kelas Kotlin biasa tanpa emulator: buat instance, panggil metode, periksa status LiveData atau StateFlow. Untuk menguji coroutine, gunakan runTest dari kotlinx-coroutines-test dengan TestDispatcher. Untuk ViewModel dengan Hilt, gunakan @HiltViewModelTest dan hiltViewModel() di fragmen pengujian. Menurut Google, pengujian unit mencakup 80–90% logika ViewModel tanpa pengujian instrumental.
Kesimpulan
Kami akan mengembangkan aplikasi seluler turnkey
IT Sectr membuat aplikasi iOS dan Android untuk startup dan bisnis sejak 2017. Kami akan memberi saran dan mengusulkan solusi terbaik.
Baca juga