Ringkasan ViewModel Bagian dari Android Jetpack.
Class ViewModel
didesain
untuk menyimpan dan mengelola data terkait UI dengan cara yang berbasis siklus proses. Class ViewModel
memungkinkan
data bertahan saat terjadi perubahan konfigurasi seperti pada saat rotasi layar.
Framework Android mengelola siklus proses pengontrol UI, seperti aktivitas dan fragmen. Framework ini dapat memutuskan untuk menghancurkan atau membuat ulang pengontrol UI sebagai respons terhadap tindakan tertentu dari pengguna atau peristiwa perangkat yang benar-benar di luar kendali Anda.
Jika sistem menghancurkan atau membuat ulang pengontrol UI, setiap
data terkait UI sementara yang Anda simpan di dalam pengontrol UI akan hilang. Misalnya, aplikasi Anda mungkin menyertakan
daftar pengguna dalam salah satu aktivitasnya. Saat aktivitas dibuat ulang untuk
perubahan konfigurasi, aktivitas baru tersebut harus menarik ulang daftar pengguna. Untuk
data sederhana, aktivitas dapat menggunakan
metode
onSaveInstanceState()
dan memulihkan datanya dari paket di
onCreate()
, tetapi pendekatan ini hanya
cocok untuk data dalam jumlah sedikit yang dapat diserialisasi kemudian dideserialisasi, bukan
untuk data yang berpotensi memiliki jumlah besar, seperti daftar pengguna atau bitmap.
Masalah lainnya yaitu pengontrol UI sering kali harus melakukan panggilan asinkron yang membutuhkan beberapa saat untuk ditampilkan. Pengontrol UI harus mengelola panggilan ini dan memastikan sistem akan membersihkan panggilan setelah panggilan dihancurkan untuk menghindari kemungkinan kebocoran memori. Pengelolaan ini memerlukan banyak pemeliharaan, dan ketika objek dibuat ulang untuk perubahan konfigurasi, pembuatan ulang objek membuat pemborosan resource karena objek mungkin harus melakukan panggilan ulang yang sebelumnya sudah dilakukan.
Pengontrol UI seperti aktivitas dan fragmen terutama ditujukan untuk menampilkan data UI, bereaksi pada tindakan pengguna, atau menangani komunikasi sistem operasi, misalnya permintaan izin. Mewajibkan pengontrol UI untuk juga bertanggung jawab atas pemuatan data dari database atau jaringan, akan menambahkan penggelembungan pada class. Menetapkan tanggung jawab berlebih pada pengontrol UI dapat mengakibatkan satu class yang mencoba menangani semua tugas aplikasi dengan sendirinya, bukan mendelegasikan tugas-tugas tersebut ke class lainnya. Menetapkan tanggung jawab berlebih pada pengontrol UI dengan cara seperti ini juga membuat pengujian menjadi jauh lebih sulit.
Akan lebih mudah dan lebih efisien untuk memisahkan kepemilikan data tampilan dari logika pengontrol UI.
Mengimplementasikan ViewModel
Komponen Arsitektur memberikan class helper ViewModel
bagi pengontrol UI yang bertanggung jawab untuk menyediakan data bagi UI.
Objek ViewModel
otomatis disimpan pada saat perubahan konfigurasi sehingga data yang disimpan segera tersedia
untuk instance aktivitas atau fragmen berikutnya. Misalnya,
jika Anda perlu menampilkan daftar pengguna di aplikasi, pastikan untuk memberikan
tanggung jawab perolehan dan penyimpanan daftar pengguna ke
ViewModel
, bukan
pada aktivitas atau fragmen, seperti yang ditunjukkan oleh kode contoh berikut:
Views
class MyViewModel : ViewModel() { private val users: MutableLiveData<List<User>> by lazy { MutableLiveData<List<User>>().also { loadUsers() } } fun getUsers(): LiveData<List<User>> { return users } private fun loadUsers() { // Do an asynchronous operation to fetch users. } }
Views
public class MyViewModel extends ViewModel { private MutableLiveData<List<User>> users; public LiveData<List<User>> getUsers() { if (users == null) { users = new MutableLiveData<List<User>>(); loadUsers(); } return users; } private void loadUsers() { // Do an asynchronous operation to fetch users. } }
Anda kemudian dapat mengakses daftar tersebut dari aktivitas dengan cara sebagai berikut:
Views
class MyActivity : 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 MyViewModel instance created by the first activity. // Use the 'by viewModels()' Kotlin property delegate // from the activity-ktx artifact val model: MyViewModel by viewModels() model.getUsers().observe(this, Observer<List<User>>{ users -> // update UI }) } }
Views
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 }); } }
Jika aktivitas dibuat ulang, aktivitas akan menerima instance MyViewModel
yang
sama seperti yang dibuat oleh aktivitas pertama. Saat aktivitas pemilik selesai, framework memanggil metode
onCleared()
milik
objek ViewModel
agar metode tersebut dapat merapikan resource.
Objek ViewModel
dirancang
untuk aktif lebih lama dibandingkan pembuatan instance tampilan tertentu
atau LifecycleOwners
. Rancangan ini
juga berarti bahwa Anda dapat menulis pengujian yang
mencakup ViewModel
dengan lebih mudah
karena rancangan tidak mengetahui tampilan dan
objek Lifecycle
.
Objek ViewModel
dapat berisi
LifecycleObservers
,
seperti
objek LiveData
. Namun,
objek ViewModel
tidak boleh
mengamati perubahan pada observable berbasis siklus proses,
seperti objek LiveData
.
Jika
ViewModel
membutuhkan
konteks Application
, misalnya untuk menemukan layanan sistem,
ViewModel dapat memperluas
class AndroidViewModel
dan
memiliki konstruktor yang menerima Application
di dalam konstruktor, karena class Application
memperluas Context
.
Membuat ViewModel dengan dependensi
Mengikuti praktik terbaik injeksi dependensi, ViewModel dapat
mengambil dependensi sebagai parameter dalam konstruktornya. Sebagian besar jenis ini
berasal dari lapisan domain atau data. Karena framework ini menyediakan ViewModel, mekanisme khusus diperlukan untuk membuat instance darinya. Mekanisme
tersebut adalah antarmuka ViewModelProvider.Factory
. Hanya penerapan
antarmuka ini yang dapat membuat instance ViewModel dalam cakupan yang tepat.
Jika class ViewModel menerima dependensi dalam konstruktornya, sediakan factory
yang mengimplementasikan antarmuka ViewModelProvider.Factory
. Ganti
fungsi create(Class<T>, CreationExtras)
untuk memberikan instance
ViewModel baru.
CreationExtras
memungkinkan Anda mengakses informasi relevan yang membantu
membuat instance ViewModel. Berikut adalah daftar kunci yang dapat diakses dari tambahan:
Kunci | Fungsi |
---|---|
ViewModelProvider.NewInstanceFactory.VIEW_MODEL_KEY |
Key ini menyediakan akses ke kunci kustom yang Anda teruskan
ke ViewModelProvider.get() . |
ViewModelProvider.AndroidViewModelFactory.APPLICATION_KEY |
Menyediakan akses ke instance class Application . |
SavedStateHandleSupport.DEFAULT_ARGS_KEY |
Memberikan akses ke Paket argumen yang harus Anda gunakan untuk membuat
SavedStateHandle . |
SavedStateHandleSupport.SAVED_STATE_REGISTRY_OWNER_KEY |
Memberikan akses ke SavedStateRegistryOwner yang sedang
digunakan untuk membuat ViewModel . |
SavedStateHandleSupport.VIEW_MODEL_STORE_OWNER_KEY |
Memberikan akses ke ViewModelStoreOwner yang sedang
digunakan untuk membuat ViewModel . |
Untuk membuat instance SavedStateHandle
baru, gunakan fungsi
CreationExtras.createSavedStateHandle()
.createSavedStateHandle())
dan teruskan ke ViewModel.
Berikut adalah contoh cara menyediakan instance ViewModel yang
mengambil repositori tercakup ke class Application
dan
SavedStateHandle
sebagai dependensi:
Views
import androidx.lifecycle.SavedStateHandle import androidx.lifecycle.ViewModel import androidx.lifecycle.ViewModelProvider import androidx.lifecycle.ViewModelProvider.AndroidViewModelFactory.Companion.APPLICATION_KEY import androidx.lifecycle.createSavedStateHandle import androidx.lifecycle.viewmodel.CreationExtras class MyViewModel( private val myRepository: MyRepository, private val savedStateHandle: SavedStateHandle ) : ViewModel() { // ViewModel logic // ... // Define ViewModel factory in a companion object companion object { val Factory: ViewModelProvider.Factory = object : ViewModelProvider.Factory { @Suppress("UNCHECKED_CAST") override fun <T : ViewModel> create( modelClass: Class<T>, extras: CreationExtras ): T { // Get the Application object from extras val application = checkNotNull(extras[APPLICATION_KEY]) // Create a SavedStateHandle for this ViewModel from extras val savedStateHandle = extras.createSavedStateHandle() return MyViewModel( (application as MyApplication).myRepository, savedStateHandle ) as T } } } }
Views
import static androidx.lifecycle.SavedStateHandleSupport.createSavedStateHandle; import static androidx.lifecycle.ViewModelProvider.AndroidViewModelFactory.APPLICATION_KEY; import androidx.lifecycle.SavedStateHandle; import androidx.lifecycle.ViewModel; import androidx.lifecycle.viewmodel.ViewModelInitializer; public class MyViewModel extends ViewModel { public MyViewModel( MyRepository myRepository, SavedStateHandle savedStateHandle ) { /* Init ViewModel here */ } static final ViewModelInitializer<MyViewModel> initializer = new ViewModelInitializer<>( MyViewModel.class, creationExtras -> { MyApplication app = (MyApplication) creationExtras.get(APPLICATION_KEY); assert app != null; SavedStateHandle savedStateHandle = createSavedStateHandle(creationExtras); return new MyViewModel(app.getMyRepository(), savedStateHandle); } ); }
Kemudian, Anda dapat menggunakan factory ini saat mengambil instance ViewModel:
Views
import androidx.activity.viewModels class MyActivity : AppCompatActivity() { private val viewModel: MyViewModel by viewModels { MyViewModel.Factory } // Rest of Activity code }
Views
import androidx.appcompat.app.AppCompatActivity; import androidx.lifecycle.ViewModelProvider; public class MyActivity extends AppCompatActivity { MyViewModel myViewModel = new ViewModelProvider( this, ViewModelProvider.Factory.from(MyViewModel.initializer) ).get(MyViewModel.class); // Rest of Activity code }
Compose
import androidx.lifecycle.viewmodel.compose.viewModel @Composable fun MyScreen( modifier: Modifier = Modifier, viewModel: MyViewModel = viewModel(factory = MyViewModel.Factory) ) { // ... }
Atau, gunakan DSL factory ViewModel untuk membuat factory menggunakan API Kotlin yang lebih idiomatis:
Views
import androidx.lifecycle.SavedStateHandle import androidx.lifecycle.ViewModel import androidx.lifecycle.ViewModelProvider import androidx.lifecycle.ViewModelProvider.AndroidViewModelFactory.Companion.APPLICATION_KEY import androidx.lifecycle.createSavedStateHandle import androidx.lifecycle.viewmodel.initializer import androidx.lifecycle.viewmodel.viewModelFactory class MyViewModel( private val myRepository: MyRepository, private val savedStateHandle: SavedStateHandle ) : ViewModel() { // ViewModel logic // Define ViewModel factory in a companion object companion object { val Factory: ViewModelProvider.Factory = viewModelFactory { initializer { val savedStateHandle = createSavedStateHandle() val myRepository = (this[APPLICATION_KEY] as MyApplication).myRepository MyViewModel( myRepository = myRepository, savedStateHandle = savedStateHandle ) } } } }
Factory untuk ViewModel versi yang lebih lama dari 2.5.0
Jika menggunakan versi ViewModel yang lebih lama dari 2.5.0, Anda harus menyediakan
factory dari subset class yang memperluas ViewModelProvider.Factory
dan menerapkan fungsi create(Class<T>)
. Bergantung pada dependensi
yang diperlukan ViewModel, class yang berbeda harus diperluas dari:
AndroidViewModelFactory
jika classApplication
diperlukan.AbstractSavedStateViewModelFactory
jikaSavedStateHandle
harus diteruskan sebagai dependensi.
Jika Application
atau SavedStateHandle
tidak diperlukan, cukup perluas dari
ViewModelProvider.Factory
.
Contoh berikut menggunakan AbstractSavedStateViewModelFactory
untuk
ViewModel yang menggunakan repositori dan jenis SavedStateHandle
sebagai
dependensi:
Views
class MyViewModel( private val myRepository: MyRepository, private val savedStateHandle: SavedStateHandle ) : ViewModel() { // ViewModel logic ... // Define ViewModel factory in a companion object companion object { fun provideFactory( myRepository: MyRepository, owner: SavedStateRegistryOwner, defaultArgs: Bundle? = null, ): AbstractSavedStateViewModelFactory = object : AbstractSavedStateViewModelFactory(owner, defaultArgs) { @Suppress("UNCHECKED_CAST") override fun <T : ViewModel> create( key: String, modelClass: Class<T>, handle: SavedStateHandle ): T { return MyViewModel(myRepository, handle) as T } } } }
Views
import androidx.annotation.NonNull; import androidx.lifecycle.AbstractSavedStateViewModelFactory; import androidx.lifecycle.SavedStateHandle; import androidx.lifecycle.ViewModel; public class MyViewModel extends ViewModel { public MyViewModel( MyRepository myRepository, SavedStateHandle savedStateHandle ) { /* Init ViewModel here */ } } public class MyViewModelFactory extends AbstractSavedStateViewModelFactory { private final MyRepository myRepository; public MyViewModelFactory( MyRepository myRepository ) { this.myRepository = myRepository; } @SuppressWarnings("unchecked") @NonNull @Override protected <T extends ViewModel> T create( @NonNull String key, @NonNull Class<T> modelClass, @NonNull SavedStateHandle handle ) { return (T) new MyViewModel(myRepository, handle); } }
Kemudian, Anda dapat menggunakan factory untuk mengambil ViewModel:
Views
import androidx.activity.viewModels class MyActivity : AppCompatActivity() { private val viewModel: MyViewModel by viewModels { MyViewModel.provideFactory((application as MyApplication).myRepository, this) } // Rest of Activity code }
Views
public class MyActivity extends AppCompatActivity { MyViewModel myViewModel = new ViewModelProvider( this, new MyViewModelFactory(((MyApplication) getApplication()).getMyRepository()) ).get(MyViewModel.class); // Rest of Activity code }
Compose
import androidx.lifecycle.viewmodel.compose.viewModel @Composable fun MyScreen( modifier: Modifier = Modifier, viewModel: MyViewModel = viewModel( factory = MyViewModel.provideFactory( (LocalContext.current.applicationContext as MyApplication).myRepository, owner = LocalSavedStateRegistryOwner.current ) ) ) { // ... }
Siklus proses ViewModel
Objek ViewModel
dicakupkan
ke
Lifecycle
yang diteruskan
ke ViewModelProvider
saat menerima
ViewModel
. ViewModel
tetap
berada di memori
sampai Lifecycle
yang
dicakupnya hilang secara permanen: dalam kasus aktivitas, yaitu saat
aktivitas selesai, sementara dalam kasus fragmen, yaitu sampai fragmen terlepas.
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 saat
Anda pertama kali
meminta ViewModel
sampai
aktivitas selesai dan dimusnahkan.
Berbagi data antar-fragmen
Sangatlah umum bagi dua fragmen atau lebih dalam suatu aktivitas untuk perlu saling berkomunikasi satu sama lain. Bayangkan kasus umum fragmen tampilan terpisah (list-detail
),
yakni saat Anda memiliki fragmen yang memungkinkan pengguna memilih item dari
daftar dan fragmen lain yang menampilkan isi item yang dipilih. Kasus seperti ini
tidaklah sepele karena kedua fragmen harus mendefinisikan beberapa deskripsi antarmuka, dan
aktivitas pemilik harus mengikat keduanya secara bersamaan. Selain itu,
kedua fragmen harus menangani skenario saat fragmen lain belum
dibuat atau terlihat.
Titik permasalahan umum ini dapat diatasi menggunakan
objek ViewModel
. Fragmen
ini dapat berbagi
ViewModel
menggunakan cakupan
aktivitas untuk menangani komunikasi ini, seperti yang ditunjukkan dalam kode contoh
berikut:
Views
class SharedViewModel : ViewModel() { val selected = MutableLiveData- () fun select(item: Item) { selected.value = item } } class ListFragment : Fragment() { private lateinit var itemSelector: Selector // Use the 'by activityViewModels()' Kotlin property delegate // from the fragment-ktx artifact private val model: SharedViewModel by activityViewModels() override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) itemSelector.setOnClickListener { item -> // Update the UI } } } class DetailFragment : Fragment() { // Use the 'by activityViewModels()' Kotlin property delegate // from the fragment-ktx artifact private val model: SharedViewModel by activityViewModels() override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) model.selected.observe(viewLifecycleOwner, Observer
- { item -> // Update the UI }) } }
Views
public class SharedViewModel extends ViewModel { private final MutableLiveData<Item> selected = new MutableLiveData<Item>(); public void select(Item item) { selected.setValue(item); } public LiveData- getSelected() { return selected; } } public class ListFragment extends Fragment { private SharedViewModel model; public void onViewCreated(@NonNull View view, Bundle savedInstanceState) { super.onViewCreated(view, savedInstanceState); model = new ViewModelProvider(requireActivity()).get(SharedViewModel.class); itemSelector.setOnClickListener(item -> { model.select(item); }); } } public class DetailFragment extends Fragment { public void onViewCreated(@NonNull View view, Bundle savedInstanceState) { super.onViewCreated(view, savedInstanceState); SharedViewModel model = new ViewModelProvider(requireActivity()).get(SharedViewModel.class); model.getSelected().observe(getViewLifecycleOwner(), item -> { // Update the UI. }); } }
Perhatikan bahwa kedua fragmen mengambil aktivitas yang memuatnya. Dengan begitu,
saat setiap fragmen menerima
ViewModelProvider
,
fragmen tersebut menerima instance SharedViewModel
yang sama,
yang dicakupkan ke aktivitas ini.
Pendekatan ini memberikan manfaat seperti berikut:
- Aktivitas tidak perlu melakukan, atau mengetahui apa pun tentang komunikasi ini.
- Fragmen tidak perlu mengenali satu sama lainnya
selain kontrak
SharedViewModel
. Jika salah satu fragmen hilang, fragmen lainnya akan terus berfungsi seperti biasa. - Setiap fragmen memiliki siklus prosesnya sendiri, dan tidak terpengaruh oleh siklus proses fragmen lain. Jika satu fragmen menggantikan yang lainnya, UI akan terus berfungsi tanpa masalah.
Mengganti Loader dengan ViewModel
Class Loader seperti CursorLoader
sering digunakan untuk
menyimpan data di UI aplikasi yang disinkronkan dengan database. Anda dapat menggunakan
ViewModel
, dengan
beberapa class lainnya, untuk menggantikan loader. Penggunaan
ViewModel
memisahkan
pengontrol UI dari operasi pemuatan data, yang berarti Anda memiliki lebih sedikit referensi
yang kuat antar-class.
Pada satu pendekatan umum dalam penggunaan loader,
suatu aplikasi mungkin menggunakan CursorLoader
untuk
mengamati konten suatu database. Saat suatu nilai dalam database berubah,
loader secara otomatis memicu pemuatan ulang data dan mengupdate UI:

ViewModel
berfungsi
dengan Room dan
LiveData sebagai pengganti loader.
ViewModel
memastikan bahwa data bertahan saat terjadi perubahan konfigurasi perangkat.
Room memberikan informasi
ke LiveData
saat database
berubah, kemudian
LiveData mengupdate UI dengan data baru.

Menggunakan coroutine dengan ViewModel
ViewModel
mencakup dukungan untuk coroutine Kotlin. Untuk informasi selengkapnya, lihat
Menggunakan coroutine Kotlin dengan Komponen Arsitektur Android.
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
Menyimpan Status UI.
Panduan Arsitektur Aplikasi Android menyarankan pembuatan class repositori untuk penanganan fungsi-fungsi ini.
Referensi lainnya
Untuk informasi selengkapnya
tentang class ViewModel
, lihat referensi berikut.
Contoh
- Sampel dasar Komponen Arsitektur Android
- Sunflower, sebuah aplikasi berkebun yang mengilustrasikan praktik terbaik pengembangan Android dengan Android Jetpack.
Codelab
- Android Room dengan View (Java) (Kotlin)
- Codelab komponen berbasis siklus proses Android
Blog
- ViewModels : A Simple Example
- ViewModels: Persistence, onSaveInstanceState(), Restoring UI State and Loaders
- ViewModels and LiveData: Patterns + AntiPatterns
- Kotlin Demystified: Understanding Shorthand Lambda Syntax
- Kotlin Demystified: Scope functions
- Kotlin Demystified: When to use custom accessors
- Lifecycle Aware Data Loading with Architecture Components