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 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 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 seringkali harus melakukan panggilan asinkron yang membutuhkan 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 ketika 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-tugas tersebut 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 tampilan kepemilikan data dari logika pengontrol UI.

Mengimplementasikan ViewModel

Komponen Arsitektur memberikan class penunjang 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 Anda menetapkan tanggung jawab untuk memperoleh dan menyimpan daftar pengguna ke ViewModel, bukan ke aktivitas atau fragmen, seperti yang ditunjukkan oleh kode contoh berikut:

Kotlin

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

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

Objek ViewModel dirancang untuk aktif lebih lama dibandingkan pembuatan instance tampilan tertentu atau LifecycleOwners. Desain ini juga berarti bahwa Anda dapat menulis pengujian untuk mencakup ViewModel dengan lebih mudah karena 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 konstruktor, karena class Application memperluas Context.

Siklus Hidup ViewModel

Objek ViewModel disertakan 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 hidup 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 Anda pertama kali meminta ViewModel sampai aktivitas selesai dan dihancurkan.

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:

Kotlin

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

    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> { 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 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 bekerja seperti biasa.
  • Setiap fragmen memiliki siklus prosesnya sendiri, dan tidak terpengaruh oleh siklus proses fragmen lain. Jika satu fragmen menggantikan yang lainnya, UI terus bekerja 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 akan 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 ponsel. Room memberikan informasi ke LiveData saat database berubah, kemudian LiveData mengupdate UI dengan data baru.

Gambar 3. Pemuatan data menggunakan 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

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

Codelab

Blog

Video