Ringkasan ViewModel   Bagian dari Android Jetpack.

Coba dengan Kotlin Multiplatform
Multiplatform Kotlin memungkinkan berbagi logika bisnis dengan platform lain. Pelajari cara menyiapkan dan menggunakan ViewModel di KMP

Class ViewModel adalah pemegang logika bisnis atau holder status tingkat layar. Class ini mengekspos status ke UI dan mengenkapsulasi logika bisnis terkait. Keuntungan utamanya adalah melakukan cache status dan mempertahankannya melalui perubahan konfigurasi. Artinya, UI Anda tidak perlu mengambil data lagi saat bernavigasi di antara aktivitas, atau mengikuti perubahan konfigurasi, seperti saat memutar layar.

Untuk informasi selengkapnya tentang holder status, lihat panduan holder status. Demikian pula, untuk mengetahui informasi selengkapnya tentang lapisan UI secara umum, lihat panduan lapisan UI.

Manfaat ViewModel

Alternatif untuk ViewModel adalah class biasa yang menyimpan data yang ditampilkan di UI. Hal ini dapat menjadi masalah saat bernavigasi di antara aktivitas atau tujuan Navigation. Tindakan ini akan menghapus data tersebut jika Anda tidak menyimpannya menggunakan mekanisme status instance tersimpan. ViewModel menyediakan API yang praktis untuk persistensi data yang menyelesaikan masalah ini.

Atau, untuk penampung status murni, Compose menawarkan kemampuan retain yang memungkinkan class biasa bertahan saat terjadi perubahan konfigurasi tanpa infrastruktur lengkap ViewModel. Meskipun kedua mekanisme membantu mempertahankan status, umumnya lebih aman untuk menyediakan ViewModel ke instance yang dipertahankan daripada sebaliknya, karena siklus proses dan perilaku pembersihan keduanya berbeda.

Manfaat utama class ViewModel pada dasarnya ada dua:

  • Memungkinkan Anda mempertahankan status UI.
  • Menyediakan akses ke logika bisnis.

Persistensi

ViewModel memungkinkan persistensi melalui status yang dimiliki ViewModel, dan operasi yang dipicu ViewModel. Cache ini berarti Anda tidak perlu mengambil data lagi melalui perubahan konfigurasi umum, seperti rotasi layar.

Cakupan

Saat membuat instance ViewModel, Anda akan meneruskan objek yang mengimplementasikan antarmuka ViewModelStoreOwner. Ini dapat berupa tujuan Navigation, grafik Navigation, aktivitas, atau jenis lain yang mengimplementasikan antarmuka. Anda juga dapat mencakup ViewModel langsung ke composable menggunakan rememberViewModelStoreOwner API. ViewModel Anda kemudian tercakup ke Lifecycle ViewModelStoreOwner. ViewModel tetap ada dalam memori hingga ViewModelStoreOwner-nya hilang secara permanen (seperti saat pemilik komponen keluar dari Komposisi).

Rentang class adalah subclass langsung atau tidak langsung dari antarmuka ViewModelStoreOwner. Subclass langsung adalah ComponentActivity dan NavBackStackEntry. Untuk mengetahui daftar lengkap subclass tidak langsung, lihat referensi ViewModelStoreOwner. Untuk mencakup ViewModels ke setiap item dalam LazyList atau Pager, gunakan rememberViewModelStoreProvider() untuk mengangkat pengelolaan pemilik ke induk.

Saat aktivitas host mengalami perubahan konfigurasi, pekerjaan asinkron akan berlanjut di ViewModel, baik yang dicakup ke aktivitas maupun ke composable tertentu. Inilah kunci untuk persistensi.

Untuk informasi selengkapnya, lihat bagian siklus proses ViewModel di bawah, API Cakupan ViewModel, dan panduan tentang pengangkatan status untuk Jetpack Compose.

SavedStateHandle

SavedStateHandle memungkinkan Anda mempertahankan data tidak hanya melalui perubahan konfigurasi, tetapi juga seluruh penghentian proses. Artinya, hal ini memungkinkan Anda untuk menjaga status UI tetap utuh bahkan ketika pengguna menutup aplikasi dan membukanya di lain waktu.

Untuk mengetahui informasi selengkapnya tentang menyimpan status UI, lihat Menyimpan status UI di Compose.

Akses ke logika bisnis

Meskipun sebagian besar logika bisnis ada di lapisan data, lapisan UI juga dapat berisi logika bisnis. Hal ini dapat terjadi saat menggabungkan data dari beberapa repositori untuk membuat status UI layar, atau saat jenis data tertentu tidak memerlukan lapisan data.

ViewModel adalah tempat yang tepat untuk menangani logika bisnis di lapisan UI. ViewModel juga bertanggung jawab menangani peristiwa dan mendelegasikannya ke lapisan hierarki lain saat logika bisnis perlu diterapkan untuk mengubah data aplikasi.

Mengimplementasikan ViewModel

Berikut adalah contoh implementasi ViewModel untuk layar yang memungkinkan pengguna melempar dadu.

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

Anda kemudian dapat mengakses ViewModel dari composable tingkat layar dengan cara sebagai berikut:

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
}

Menggunakan coroutine dengan ViewModel

ViewModel mencakup dukungan untuk coroutine Kotlin. API ini dapat mempertahankan pekerjaan asinkron dengan cara yang sama seperti mempertahankan status UI.

Untuk informasi selengkapnya, lihat Menggunakan coroutine Kotlin dengan Komponen Arsitektur Android.

Siklus proses ViewModel

Siklus proses ViewModel terikat langsung dengan cakupannya. ViewModel tetap berada dalam memori sampai ViewModelStoreOwner yang dicakupnya menghilang. Hal ini dapat terjadi dalam konteks berikut:

  • Dalam kasus aktivitas, setelah aktivitas selesai.
  • Dalam kasus entri Navigation, saat entri tersebut dihapus dari data sebelumnya.
  • Dalam kasus composable, saat composable keluar dari Komposisi. Anda dapat menggunakan rememberViewModelStoreOwner untuk mencakup ViewModel secara langsung ke bagian UI yang arbitrer (seperti Pager atau LazyList).

Hal ini membuat ViewModel menjadi solusi yang bagus untuk menyimpan data yang bertahan dari perubahan konfigurasi.

Gambar 1 menunjukkan berbagai status siklus proses suatu aktivitas saat aktivitas tersebut mengalami rotasi dan kemudian selesai. Gambar ini juga menunjukkan masa aktif ViewModel di sebelah siklus proses aktivitas yang terkait. Diagram khusus ini menggambarkan status suatu aktivitas.

Menggambarkan siklus proses ViewModel saat suatu aktivitas berganti status.
Gambar 1. Status siklus proses aktivitas dan ViewModel.

Anda biasanya meminta ViewModel saat sistem pertama kali memanggil metode onCreate() milik objek aktivitas. Sistem mungkin memanggil onCreate() beberapa kali selama masa aktif aktivitas, seperti saat layar perangkat diputar. ViewModel tersedia sejak saat Anda pertama kali meminta ViewModel sampai aktivitas selesai dan dihancurkan.

Menghapus dependensi ViewModel

ViewModel memanggil metode onCleared saat ViewModelStoreOwner menghancurkannya selama siklus prosesnya. Hal ini memungkinkan Anda untuk membersihkan pekerjaan atau dependensi yang mengikuti siklus proses ViewModel.

Contoh berikut menunjukkan alternatif untuk viewModelScope. viewModelScope adalah CoroutineScope bawaan yang otomatis mengikuti siklus proses ViewModel. ViewModel menggunakannya untuk memicu operasi terkait bisnis. Jika Anda ingin menggunakan cakupan kustom, bukan viewModelScope untuk pengujian yang lebih mudah, ViewModel dapat menerima CoroutineScope sebagai dependensi dalam konstruktornya. Saat ViewModelStoreOwner menghapus ViewModel di akhir siklus prosesnya, ViewModel juga akan membatalkan CoroutineScope.

class MyViewModel(
    private val coroutineScope: CoroutineScope =
        CoroutineScope(SupervisorJob() + Dispatchers.Main.immediate)
) : ViewModel() {

    // Other ViewModel logic ...

    override fun onCleared() {
        coroutineScope.cancel()
    }
}

Dari lifecycle versi 2.5 dan yang lebih baru, Anda dapat meneruskan satu atau beberapa objek Closeable ke konstruktor ViewModel yang otomatis ditutup saat instance ViewModel dihapus.

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

Praktik terbaik

Berikut adalah beberapa praktik terbaik utama yang sebaiknya Anda ikuti saat mengimplementasikan ViewModel:

  • Karena cakupan, gunakan ViewModel sebagai detail implementasi pemegang status tingkat layar. Jangan menggunakannya sebagai pemegang status komponen UI yang dapat digunakan kembali seperti grup chip atau formulir. Jika tidak, Anda akan mendapatkan instance ViewModel yang sama dalam penggunaan komponen UI yang sama dengan cara berbeda dalam ViewModelStoreOwner yang sama, kecuali jika Anda menggunakan kunci model tampilan eksplisit per chip.
  • ViewModel tidak boleh mengetahui detail implementasi UI. Pastikan nama metode yang ditampilkan oleh ViewModel API dan kolom status UI seumum mungkin. Dengan cara ini, ViewModel Anda dapat mengakomodasi jenis UI apa pun: ponsel, perangkat foldable, tablet, atau bahkan Chromebook.
  • Karena berpotensi dapat aktif lebih lama dari ViewModelStoreOwner, ViewModel tidak boleh menyimpan referensi apa pun yang terkait dengan API terkait siklus proses seperti Context atau Resources untuk mencegah kebocoran memori.
  • Jangan meneruskan ViewModel ke class, fungsi, atau komponen UI lainnya. Karena platform mengelolanya, Anda harus menyimpannya sedekat mungkin dengan Anda—dekat dengan Aktivitas, fungsi composable tingkat layar, atau tujuan Navigasi. Hal ini mencegah komponen dengan tingkat lebih rendah mengakses lebih banyak data dan logika daripada yang dibutuhkan.

Informasi lebih lanjut

Ketika data Anda bertambah menjadi lebih kompleks, Anda mungkin memilih untuk memiliki class terpisah yang tugasnya hanya untuk memuat data. Tujuan dari ViewModel adalah untuk mengenkapsulasi data bagi pengontrol UI agar data dapat bertahan saat terjadi perubahan konfigurasi. Untuk informasi tentang cara memuat, mempertahankan, dan mengelola data di seluruh perubahan konfigurasi, lihat Status UI Tersimpan.

Panduan Arsitektur Aplikasi Android menyarankan pembuatan class repositori untuk penanganan fungsi-fungsi ini.

Referensi lainnya

Untuk informasi lebih lanjut tentang class ViewModel, lihat referensi berikut.

Dokumentasi

Melihat konten

Contoh