Bergabunglah bersama kami di ⁠#Android11: The Beta Launch Show pada tanggal 3 Juni!

Ringkasan ViewModel   Bagian dari Android Jetpack.

Class ViewModel dirancang 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 memusnahkan atau membuat ulang pengontrol UI sebagai respons terhadap tindakan tertentu dari pengguna atau peristiwa perangkat yang benar-benar di luar kendali Anda.

Jika sistem memusnahkan atau membuat ulang pengontrol UI, setiap data terkait UI sementara yang Anda simpan di dalam pengontrol UI akan hilang. Misalnya, aplikasi mungkin menyertakan daftar pengguna dalam salah satu aktivitasnya. Saat aktivitas dibuat ulang untuk perubahan konfigurasi, aktivitas baru tersebut harus mengambil 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 diserialisasikan kemudian dinonserialisasikan, bukan untuk data yang berpotensi memiliki jumlah besar, seperti daftar pengguna atau bitmap.

Masalah lainnya yaitu pengontrol UI sering harus melakukan panggilan asinkron yang memerlukan waktu beberapa saat untuk ditampilkan. Pengontrol UI harus mengelola panggilan ini dan memastikan sistem akan membersihkan panggilan setelah panggilan dimusnahkan untuk menghindari kemungkinan kebocoran memori. Pengelolaan ini memerlukan banyak pemeliharaan, dan dalam kasus di mana objek dibuat ulang untuk perubahan konfigurasi, pembuatan ulang objek menjadi 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 yang berlebih pada pengontrol UI dapat mengakibatkan satu class yang mencoba menangani semua tugas aplikasi dengan sendirinya, dan bukan mendelegasikan tugas ke class lainnya. Menetapkan tanggung jawab yang 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 pembantu ViewModel bagi pengontrol UI yang bertanggung jawab untuk menyediakan data bagi UI. Objek ViewModel secara 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:

Kotlin

    class MyViewModel : ViewModel() {
        private val users: MutableLiveData<List<User>> by lazy {
            MutableLiveData().also {
                loadUsers()
            }
        }

        fun getUsers(): LiveData<List<User>> {
            return users
        }

        private fun loadUsers() {
            // Do an asynchronous operation to fetch users.
        }
    }
    

Java

    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:

Kotlin

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

Java

    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 membersihkan resource.

Objek ViewModel dirancang untuk aktif lebih lama dibandingkan proses instantiasi 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 menjangkau Context.

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.

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.

Berbagi data antar-fragmen

Sangatlah umum bagi dua fragmen atau lebih dalam suatu aktivitas untuk perlu saling berkomunikasi satu sama lain. Bayangkan kasus umum seperti fragmen berdetail master, di mana Anda memiliki fragmen dengan kondisi pengguna memilih suatu item dari daftar dan fragmen yang 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:

Kotlin

    class SharedViewModel : ViewModel() {
        val selected = MutableLiveData<Item>()

        fun select(item: Item) {
            selected.value = item
        }
    }

    class MasterFragment : 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> { item ->
                // Update the UI
            })
        }
    }
    

Java

    public class SharedViewModel extends ViewModel {
        private final MutableLiveData<Item> selected = new MutableLiveData<Item>();

        public void select(Item item) {
            selected.setValue(item);
        }

        public LiveData<Item> getSelected() {
            return selected;
        }
    }

    public class MasterFragment 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 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.
  • Tiap fragmen memiliki siklus prosesnya sendiri, dan tidak terpengaruh oleh siklus proses fragmen lain. Jika satu fragmen menggantikan yang lainnya, UI 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:

Gambar 2. Pemuatan data dengan loader

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

Gambar 3. Pemuatan data dengan ViewModel

Menggunakan coroutine dengan ViewModel

ViewModel mencakup dukungan untuk coroutine Kotlin. Untuk informasi selengkapnya, lihat Menggunakan coroutine Kotlin dengan Komponen Arsitektur Android.

Informasi lebih lanjut

Saat 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 proses build class repositori untuk menangani fungsi ini.

Referensi lainnya

Untuk informasi selengkapnya tentang class ViewModel, lihat referensi berikut.

Contoh

Codelab

Blog

Video