Ringkasan ViewModel Bagian dari Android Jetpack.
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 penyimpanan. ViewModel menyediakan API yang praktis 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 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, 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
SavedStateHandle memungkinkan Anda mempertahankan data tidak hanya melalui konfigurasi perubahan, tetapi pada 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 pengangkatan status untuk Jetpack Compose.
Mengimplementasikan ViewModel
Berikut adalah contoh implementasi ViewModel untuk layar yang memungkinkan pengguna melempar dadu.
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 DiceUiState {
private final Integer firstDieValue;
private final Integer secondDieValue;
private final int numberOfRolls;
// ...
}
public class DiceRollViewModel extends ViewModel {
private final MutableLiveData<DiceUiState> uiState =
new MutableLiveData(new DiceUiState(null, null, 0));
public LiveData<DiceUiState> getUiState() {
return uiState;
}
public void rollDice() {
Random random = new Random();
uiState.setValue(
new DiceUiState(
random.nextInt(7) + 1,
random.nextInt(7) + 1,
uiState.getValue().getNumberOfRolls() + 1
)
);
}
}
Anda kemudian dapat mengakses ViewModel 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
public class MyActivity extends AppCompatActivity {
public void onCreate(Bundle savedInstanceState) {
super.onCreate(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.
DiceRollViewModel model = new ViewModelProvider(this).get(DiceRollViewModel.class);
model.getUiState().observe(this, uiState -> {
// 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. 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 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.
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 Anda
pertama-tama meminta ViewModel
hingga 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 dalam penggunaan yang berbeda dari komponen UI yang sama di bawah ViewModelStoreOwner 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
, ViewModels tidak boleh berisi referensi API terkait siklus proses sepertiContext
atauResources
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
Direkomendasikan untuk Anda
- Catatan: teks link ditampilkan saat JavaScript nonaktif
- Menggunakan coroutine Kotlin dengan komponen yang mendukung siklus proses
- Menyimpan status UI
- Memuat dan menampilkan data yang dibagi-bagi