Ringkasan ViewModel   Bagian dari Android Jetpack.

Class ViewModel adalah pemegang logika bisnis atau pemegang 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 lebih lanjut tentang pemegang status, lihat panduan pemegang status. Demikian pula, untuk 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 penyimpanan. ViewModel menyediakan API yang nyaman untuk persistensi data yang menyelesaikan masalah ini.

Manfaat utama class ViewModel pada dasarnya ada dua:

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

Persistensi

ViewModel memungkinkan kelangsungan 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, fragmen, atau jenis lain yang mengimplementasikan antarmuka. ViewModel Anda kemudian tercakup ke Siklus proses ViewModelStoreOwner. ViewModel tetap ada dalam memori hingga ViewModelStoreOwner-nya hilang secara permanen.

Rentang class adalah subclass langsung atau tidak langsung dari antarmuka ViewModelStoreOwner. Subclass langsung adalah ComponentActivity, Fragment, dan NavBackStackEntry. Untuk mengetahui daftar lengkap subclass tidak langsung, lihat referensi ViewModelStoreOwner.

Saat fragmen atau aktivitas yang dicakup dalam ViewModel dihancurkan, pekerjaan asinkron akan berlanjut di ViewModel yang dicakup. Ini adalah kunci untuk persistensi.

Untuk informasi selengkapnya, lihat bagian di bawah tentang siklus proses ViewModel.

SavedStateHandle

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

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.

Jetpack Compose

Saat menggunakan Jetpack Compose, ViewModel adalah cara utama untuk mengekspos status UI layar ke composable Anda. Dalam aplikasi campuran, aktivitas dan fragmen hanya menghosting fungsi composable Anda. Hal ini merupakan pergeseran dari pendekatan sebelumnya yang tidak sederhana dan intuitif untuk membuat UI yang dapat digunakan kembali dengan aktivitas dan fragmen, yang menyebabkannya menjadi lebih aktif sebagai pengontrol UI.

Hal terpenting yang perlu diingat saat menggunakan ViewModel dengan Compose adalah Anda tidak dapat menentukan cakupan ViewModel ke composable. Hal ini disebabkan karena composable bukan ViewModelStoreOwner. Dua instance composable yang sama dalam Komposisi, atau dua composable yang berbeda mengakses jenis ViewModel yang sama dalam ViewModelStoreOwner yang sama akan menerima instance yang sama dari ViewModel, yang sering kali bukanlah perilaku yang diharapkan.

Untuk mendapatkan manfaat ViewModel di Compose, hosting setiap layar di Fragment atau Activity, atau gunakan Compose Navigation dan gunakan ViewModel dalam fungsi composable sedekat mungkin dengan tujuan Navigation. Hal ini karena Anda dapat mencakup ViewModel ke tujuan Navigation, grafik Navigation, Activity, dan Fragment.

Untuk informasi selengkapnya, lihat panduan tentang Status dan Jetpack Compose.

Mengimplementasikan ViewModel

Berikut adalah contoh penerapan ViewModel: Kasus penggunaan sedang menampilkan daftar pengguna.

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 MyViewModel extends ViewModel {

    // Expose screen UI state
    private MutableLiveData<List<User>> users;
    public LiveData<List<User>> getUsers() {
        if (users == null) {
            users = new MutableLiveData<List<User>>();
            loadUsers();
        }
        return users;
    }

    // Handle business logic
    private void loadUsers() {
        // Do an asynchronous operation to fetch users.
    }
}

Anda kemudian dapat mengakses daftar tersebut dari aktivitas dengan cara sebagai berikut:

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

import androidx.lifecycle.ViewModelProvider;

public class MyActivity extends AppCompatActivity {
    public void onCreate(Bundle 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.

        MyViewModel model = new ViewModelProvider(this).get(MyViewModel.class);
        model.getUsers().observe(this, users -> {
            // 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
}

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. Seperti yang disebutkan di atas, ViewModel terikat dengan cakupannya. ViewModel tetap berada dalam memori sampai ViewModelStoreOwner yang dicakupnya menghilang. Hal ini dapat terjadi dalam konteks berikut:

  • Dalam kasus aktivitas, setelah selesai.
  • Dalam kasus fragmen, saat fragmen terlepas.
  • Dalam kasus entri Navigation, saat entri tersebut dihapus dari data sebelumnya.

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. Status dasar yang sama juga diterapkan untuk siklus proses suatu fragmen.

Menggambarkan siklus proses ViewModel saat suatu aktivitas berganti status.

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

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 yang berbeda dari komponen UI yang sama dalam ViewModelStoreOwner yang sama.
  • 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 Activity, fragmen, atau fungsi composable tingkat layar. 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

Contoh