Android Kotlin #9: ViewModel dan LiveData – Arsitektur Modern


Android Kotlin #9: ViewModel dan LiveData – Arsitektur Modern

Halo lagi, calon arsitek aplikasi Android! 

Selama ini kita menulis semua kode di Activity: mulai dari memanggil API, mengelola data, sampai memperbarui UI. Ini seperti menaruh dapur, kamar tidur, dan ruang tamu dalam satu ruangan—berantakan dan susah dirawat. Apalagi kalau layar HP diputar (rotasi), Activity akan dibuat ulang dan data kita hilang! 😱

Nah, untuk mengatasi itu, Google merekomendasikan arsitektur modern dengan ViewModel dan LiveData. Anggap saja ViewModel itu asisten pribadi yang menyimpan data dan tidak mati meskipun Activity-nya berganti. LiveData itu seperti TV yang menayangkan data—setiap kali data berubah, TV (UI) langsung update.

🤣 Kenapa ViewModel tidak ikut mati saat Activity dihancurkan? Karena dia tahan banting kayak tukang ojek! 😆

Teori Singkat

  • ViewModel: kelas yang menyimpan dan mengelola data terkait UI. Dia akan bertahan selama lifecycle View-nya (Activity/Fragment) ada. Jika Activity dibuat ulang (misal rotasi), ViewModel yang sama akan digunakan kembali. Data tidak hilang!
  • LiveData: objek yang bisa diamati (observable). Dia menyimpan data dan memberitahu observer (biasanya Activity/Fragment) setiap kali data berubah. Observer hanya akan menerima update jika dalam keadaan aktif (STARTED/RESUMED).

Langkah 1: Menambahkan Dependency

Buka build.gradle (Module: app). Di bagian dependencies, tambahkan:

implementation 'androidx.lifecycle:lifecycle-viewmodel-ktx:2.7.0'
implementation 'androidx.lifecycle:lifecycle-livedata-ktx:2.7.0'

Klik Sync Now.

Langkah 2: Membuat ViewModel untuk Wisata

Buat kelas Kotlin baru: WisataViewModel.kt (pilih Class). Isinya:

package com.example.wisataapp

import androidx.lifecycle.LiveData
import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import kotlinx.coroutines.launch

class WisataViewModel : ViewModel() {

    // MutableLiveData (bisa diubah) vs LiveData (hanya dibaca)
    private val _listWisata = MutableLiveData<List<DaftarWisata>>()
    val listWisata: LiveData<List<DaftarWisata>> get() = _listWisata

    private val _isLoading = MutableLiveData<Boolean>()
    val isLoading: LiveData<Boolean> get() = _isLoading

    private val _errorMessage = MutableLiveData<String?>()
    val errorMessage: LiveData<String?> get() = _errorMessage

    fun fetchWisata() {
        viewModelScope.launch {
            _isLoading.value = true
            try {
                val response = RetrofitInstance.api.getWisata().execute()
                if (response.isSuccessful) {
                    response.body()?.let {
                        _listWisata.value = it
                    }
                } else {
                    _errorMessage.value = "Gagal mengambil data: ${response.code()}"
                }
            } catch (e: Exception) {
                _errorMessage.value = "Error: ${e.message}"
            } finally {
                _isLoading.value = false
            }
        }
    }
}

Penjelasan:

  • MutableLiveData: versi LiveData yang nilainya bisa diubah (dari dalam ViewModel).
  • LiveData: versi read-only yang diekspos ke Activity.
  • viewModelScope: Coroutine scope yang terkait dengan ViewModel. Akan otomatis dibatalkan saat ViewModel dihapus.
  • _isLoading: untuk menampilkan indikator loading.
  • _errorMessage: untuk menampilkan pesan error.

Langkah 3: Menggunakan ViewModel di MainActivity

Sekarang kita ubah MainActivity.kt untuk menggunakan ViewModel. Kita akan mengamati LiveData dan memperbarui UI saat data berubah.

Kode baru MainActivity.kt:

package com.example.wisataapp

import android.content.Intent
import android.os.Bundle
import android.widget.Toast
import androidx.activity.viewModels
import androidx.appcompat.app.AppCompatActivity
import androidx.lifecycle.Observer
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView

class MainActivity : AppCompatActivity() {

    private lateinit var recyclerView: RecyclerView
    private lateinit var adapter: WisataAdapter

    // Delegasi viewModels() untuk mendapatkan instance ViewModel
    private val wisataViewModel: WisataViewModel by viewModels()

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

        recyclerView = findViewById(R.id.recyclerViewKategori)
        recyclerView.layoutManager = LinearLayoutManager(this)

        // Observer LiveData
        wisataViewModel.listWisata.observe(this, Observer { listWisata ->
            // Update adapter saat data berubah
            adapter = WisataAdapter(listWisata) { wisata ->
                val intent = Intent(this, DetailActivity::class.java)
                intent.putExtra("wisata", wisata)
                startActivity(intent)
            }
            recyclerView.adapter = adapter
        })

        wisataViewModel.isLoading.observe(this, Observer { isLoading ->
            // Bisa tampilkan progress bar di sini
            if (isLoading) {
                // Tampilkan loading
            } else {
                // Sembunyikan loading
            }
        })

        wisataViewModel.errorMessage.observe(this, Observer { errorMsg ->
            errorMsg?.let {
                Toast.makeText(this, it, Toast.LENGTH_LONG).show()
            }
        })

        // Panggil fetch data
        wisataViewModel.fetchWisata()
    }
}

Perhatikan:

  • by viewModels(): delegasi KTX untuk mendapatkan ViewModel yang sudah di-scope ke Activity.
  • observe(this, Observer): mengamati LiveData. Parameter pertama adalah LifecycleOwner (Activity), sehingga observer hanya aktif saat Activity dalam keadaan start/resume.
  • Ketika data berubah (setelah fetch), observer akan dipanggil dan RecyclerView diperbarui.

Langkah 4: Jalankan dan Uji Rotasi

Jalankan aplikasi. Saat pertama kali, data akan dimuat. Coba putar layar HP emulator (Ctrl + F11 di emulator). Seharusnya data tetap ada, tidak perlu memuat ulang dari API. Itulah kehebatan ViewModel!

💡 Kalau ingin melihat efek rotasi dengan jelas, tambahkan log di fetchWisata() dan lihat di Logcat. Seharusnya hanya dipanggil sekali saat pertama kali ViewModel dibuat.

Langkah 5: Menambahkan ProgressBar (Opsional)

Kita bisa menambahkan ProgressBar di layout untuk indikator loading. Tambahkan di activity_main.xml di dalam ConstraintLayout:

<ProgressBar
    android:id="@+id/progressBar"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    app:layout_constraintBottom_toBottomOf="parent"
    app:layout_constraintEnd_toEndOf="parent"
    app:layout_constraintStart_toStartOf="parent"
    app:layout_constraintTop_toTopOf="parent" />

Kemudian di MainActivity, panggil ProgressBar dan ubah visibilitasnya sesuai isLoading:

private lateinit var progressBar: ProgressBar

// di onCreate
progressBar = findViewById(R.id.progressBar)

wisataViewModel.isLoading.observe(this, Observer { isLoading ->
    progressBar.visibility = if (isLoading) View.VISIBLE else View.GONE
})

Langkah 6: (Bonus) ViewModel untuk DetailActivity

Di DetailActivity, kita juga bisa pakai ViewModel jika ingin mengambil data lebih lengkap dari API berdasarkan id. Tapi untuk sekarang, kita kirim data via Intent saja sudah cukup.

Selamat! Aplikasi Kini Lebih Kokoh

Dengan ViewModel dan LiveData, aplikasi kamu sudah mengikuti arsitektur modern yang direkomendasikan Google. Data tidak hilang saat rotasi, kode lebih terstruktur, dan mudah di-test.

Rangkuman

  • ViewModel: menyimpan data UI dan bertahan dari perubahan konfigurasi.
  • LiveData: observable data holder yang sadar lifecycle.
  • MutableLiveData: versi LiveData yang bisa diubah nilainya (di dalam ViewModel).
  • viewModelScope: coroutine scope untuk operasi background di ViewModel.
  • Observer: mengamati LiveData dan memperbarui UI saat data berubah.

Selanjutnya?

Di artikel berikutnya (Android Kotlin #10: Menambah, Mengubah, dan Menghapus Data (POST, PUT, DELETE)), kita akan belajar melakukan operasi tulis ke API. Kita akan menambahkan fitur tambah kategori/wisata, edit, dan hapus dari dalam aplikasi. Sampai jumpa!

😎 Kalau ViewModel-mu error, cek lagi dependency-nya. Jangan sampai ketinggalan lifecycle-viewmodel-ktx. ViewModel itu asisten setia, tapi kalau tidak diundang, ya tidak datang!

Ditulis oleh Kakak programmer yang dulu juga sering kehilangan data karena rotasi. Kalau ada pertanyaan, tulis di komentar ya!

Lebih baru Lebih lama

نموذج الاتصال