ViewModel — apa itu, mengelola data UI di Android Jetpack

Penulis: IT Sectr Diterbitkan: 2026-02-19 Waktu membaca: 9 mnt

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 — komponen Jetpack untuk menyimpan data UI, tahan terhadap rotasi layar dan pembuatan ulang Activity.
  • Siklus hidup ViewModel terikat pada scope (Activity/Fragment/Composable), bukan pada instance Activity terpisah.
  • viewModelScope — coroutine bawaan di dalam ViewModel, otomatis dibatalkan saat pembersihan ViewModel.
  • ViewModelProvider — pabrik untuk membuat ViewModel dengan dukungan dependency injection melalui Hilt atau Koin.
  • Di MVVM, ViewModel menggantikan presenter dari MVP, menghilangkan ikatan ke View tertentu melalui LiveData atau StateFlow.

Apa itu ViewModel di Android?

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: perbedaan dengan Activity

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.

ViewModel dalam arsitektur MVVM

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 dan pabrik: membuat dengan parameter

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.

kotlin
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 dan coroutine di ViewModel

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.

kotlin
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.

ViewModel dengan Hilt dan Koin: pendekatan DI

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.

kotlin
@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.

Contoh kode: ViewModel di Kotlin

Contoh 1: ViewModel dasar dengan penghitung

ViewModel paling sederhana yang menyimpan penghitung bilangan bulat yang tidak disetel ulang saat rotasi layar. Mendemonstrasikan pola dasar penggunaan MutableLiveData dan LiveData.

kotlin
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
    }
}

Contoh 2: ViewModel dengan SavedStateHandle

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.

kotlin
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

Apa perbedaan ViewModel dengan onSaveInstanceState?

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.

Apakah perlu membersihkan ViewModel secara manual?

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.

Bisakah ViewModel digunakan di Compose?

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.

Apa yang tidak boleh disimpan di 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.

Bagaimana cara menguji ViewModel?

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

  • ViewModel — komponen Jetpack untuk menyimpan data UI, bertahan dari perubahan konfigurasi tanpa kehilangan status.
  • Siklus hidup ViewModel terikat pada scope (Activity/Fragment), bukan pada instance Activity — pembersihan terjadi saat scope selesai.
  • ViewModelProvider — metode pabrik untuk membuat ViewModel; untuk parameter diimplementasikan ViewModelProvider.Factory.
  • viewModelScope — CoroutineScope bawaan, secara otomatis membatalkan coroutine saat onCleared(), menghilangkan kebocoran memori.
  • SavedStateHandle — mekanisme penyimpanan status saat proses dimatikan, terintegrasi di konstruktor ViewModel.
  • Hilt dan @HiltViewModel — cara standar DI untuk ViewModel di proyek besar; Koin — alternatif ringan tanpa pembuatan kode.
  • ViewModel — dasar arsitektur MVVM dan UDF, digunakan di 82% aplikasi Jetpack menurut Google I/O 2025.

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.

Diskusikan proyek

Baca juga