Android MVVM — Part 4 View Yang Sangat Reaktif

Membangun Aplikasi MVVM Dengan Kotlin, Coroutines dan Architecture Component

Satria Adi Putra
UNIKOM Codelabs

--

View

View adalah bagian yang dilihat oleh pengguna kita. Mau bikin animasi yang kece-kece? Itu semua urusan View. Ini emang part terakhir yang dibutuhin buat ngerti tentang MVVM, tapi abis nyelesein bagian ini apa kita bisa bilang kalau kita ahli MVVM?

Ya kaga lah! Kita udah bisa implementasiin MVVM ke dalam project sederhana. Masih jauh banget buat nganggep diri kita seorang ahli. Ini bukan akhir perjalanan buat belajar MVVM. Cuma karena kita bisa ngelakuin sesuatu, bukan berarti kita punya alesan buat berhenti belajar. Pelajari terus terus dan terus. Belajar dari orang lain juga, pasti bakal ada detil kecil yang kelewat waktu kita belajar sebelumnya. So, pastikan hanya kematian yang boleh menghentikan kita dari belajar.

Yuk Bikin!

Kita bakal implementasiin Single Activity Architecture. Dengan menggunakan library Navigation dari Android Jetpack, implementasinya jadi lebih gampang. Di setiap pertama kali membuat project, Android Studio udah nyiapin file MainActivity. Kita ubah file MainActivity nya jadi kayak gini

Di onCreate, kita memanggil fungsi setupActionBarWithNavController. Tugas dari fungsi ini adalah melakukan sinkronisasi antara komponen navigasi dengan toolbar yang bakal kita pake. Jadi nanti kalo pindah fragment, judul di toolbar bakal diganti otomatis sama si navController-nya. Kita juga ganti fungsi onSupportNavigateUp jadi fungsi navigateUp-nya si navController. Jadi semua urusan navigasi kita serahin dan percayain ke navController.

Selanjutnya kita ubah file activity_main.xml menjadi seperti ini.

Satu-satunya yang asing dari file ini adalah tag fragment. Biasanya kalo pake fragment kita bakal pake FrameLayout sebagai placeholdernya. Waktu pake komponen Navigation, kita cukup bilang kalo fragment yang bakal kita pake itu adalah NavHostFragment dari komponen itu sendiri. Sisanya biarkan komponen tersebut yang bekerja.

Oh iya karena kita pake Toolbar custom kita sendiri, pastiin kita juga ubah file styles.xml nya ya.

Konfigurasi Komponen Navigation

Buat ngelakuin konfigurasi komponen Navigation, pertama kita bikin dulu file resource baru.

Kita bisa kasih nama filenya bebas. Yang penting pastiin kalau Resource Type nya Navigation.

Buka file nav_graphnya. Kemudian klik “Click to add a destination” dan pilih “Create new destination”. Buat Fragment SetListFragment. Setelah itu tambahkan lagi destinasi baru. Tambahkan 2 buah Fragment PokemonListFragment dan PokemonCardDetailFragment.

Atur ke-3 Fragment tersebut dan tarik garis hingga menjadi seperti ini.

Klik pada SetListFragment dan pastikan propertinya sama seperti berikut ini.

Setelah itu jadikan SetListFragment sebagai tampilan utama yang akan ditampilkan dengan klik gambar rumah.

Pada properti PokemonListFragment ubah menjadi seperti ini.

Argument seperti intent extra yang biasa kita kirimkan saat memulai activity. Lalu dengan mengatur label menjadi {setName} maka judul pada Toolbar akan dinamis bergantung dengan argumen yang diberikan saat kita melakukan aksi menuju PokemonListFragment.

Terakhir kita atur properti dari PokemonCardDetailFragment-nya menjadi seperti ini.

Semua udah terkonfigurasi, langkah terakhir itu buat build projectnya. Caranya gampang klik Build -> Make Project. Langkah ini dibutuhin soalnya komponen Navigation butuh beberapa Class yang digenerate sama librarynya.

Layouting

Selesai deh konfigurasi komponen Navigation. Ya, itu baru konfigurasi komponen Navigation-nya sih. Belum masuk ke layouting. Oke sekarang kita mulai layouting, ini kode buat masing-masing fragment-nya.

Ga ada perbedaan yang signifikan antara file fragment_set_list.xml sama file fragment_pokemon_list.xml. Masing-masing layout punya 1 TextView buat nampilin pesan error kalau ada error waktu ngelakuin request http dan 1 RecyclerView buat nampilin list. Terus tiap layout juga punya SwipeRefreshLayout, jadi kalo misal ada error pengguna kita ga perlu lagi tutup aplikasi dan buka biar ngelakuin request http ulang. Mereka cukup swipe ke bawah supaya ngelakuin request http ulang.

Karena kita punya 2 RecyclerView, jadi kita juga harus buat 2 layout item yang bakal nampilin tiap item dari list yang bakal ditampilin.

Make the View Reactive

Abis bikin layoutnya, sekarang waktunya kita kasih logic di Fragment. Kita bakal bikin View ini sereaktif mungkin sama LiveData yang ada di ViewModel masing-masing. Pertama kita buat dulu SetListFragment.

Di sini kita bisa liat ada 2 class yang ga dikenal. Yang pertama ada SetListAdapter dan yang kedua ada SetListFactory. Selain itu juga ada ViewModelProviders apa itu? Kenapa kita ga bikin ViewModelnya kayak waktu bikin pengujian aja? Ayo kita liat lifecycle dari ViewModel.

Dari gambar di atas, kita bisa liat kalau ViewModel bakal terus hidup sampe Activity diancurin atau masuk ke onDestroy. Kalau kita bikin ViewModel-nya sama kayak waktu bikin pengujian, kita ga akan bisa dapetin keuntungan ini. Jadi kita bakal pake bantuan dari ViewModelProviders yang bakal nentuin apa ViewModel harus diinstansiasi baru atau pake yang udah ada.

Masalahnya ViewModelProviders cuma support instansiasi dari ViewModel yang constructornya ga ada parameternya dan AndroidViewModel yang butuh Application buat parameter di constructor-nya. ViewModel yang kita buat di SetListViewModel, dia butuh PokemonSetRepository. Solusinya adalah kita buat ViewModelFactory yang baru buat bikin ViewModel yang pake PokemonSetRepository sebagai parameter di constructornya. Itulah kenapa ada SetListFactory.

Tugas dari class ini hanya menginstansiasi ViewModel yang kita butuhin sesuai sama parameter yang dibutuhin juga.

Kalau SetListAdapter, dia hanya class RecyclerView.Adapter biasa.

Satu hal yang mungkin asing di adapter ini adalah

containerView?.setOnClickListener { view ->
val action =
SetListFragmentDirections.actionSetListFragmentToPokemonListFragment(item.name)
view.findNavController().navigate(action)
}

Kode ini fungsinya buat navigasi ke fragment yang dituju. Biasanya pake Intent atau FragmentTransaction, kalo pake komponen Navigation yang dipake itu NavigationController. Tugas dia buat pindahin Fragment terus juga buat ngubah title di Toolbar sesuai dengan yang udah dikonfigurasi sebelumnya.

Kita balik lagi ke SetListFragment, di sini mungkin kita bakal liat satu kode yang lagi-lagi asing.

viewState.observe(this@SetListFragment, Observer(this@SetListFragment::handleState))

Dari awal kita selalu bilang kita bakal bikin view yang reaktif. Gimana sih caranya biar view kita reaktif? Kita bisa pake Observer Pattern. Ada beberapa komponen utama dari Observer Pattern, yang terutama adalah Observable dan Observer. Observable adalah object yang diamati dan Observer adalah objek yang mengamati. Waktu ada perubahan di Observable, dia bakal ngasih pemberitahuan ke Observer dan Observer bakal langsung ngelakuin sesuatu sesuai dengan perubahan di Observable. Kasus sederhananya bisa kita liat misalnya PewDiePie dan subscribernya. Waktu PewDiePie upload video baru di channel youtubenya, beberapa subscribernya yang udah nyalain lonceng bakal dapet pemberitahuan dan bakal langsung nonton videonya atau ada yang ditunda beberapa jam karena memang ada urusan.

Gimana cara implementasinya? Beruntungnya Google udah implementasiin Observer Pattern itu buat kita. LiveData sebagai Observable dan LifecycleOwner sebagai Observer. Jadi setiap ada perubahan di LiveData, LifecyclerOwner bisa saja Activity atau Fragment, bakal langsung dikasih tau jadi mereka bisa langsung bereaksi.

Kalau kita ngelakuin observe di Activity, kemungkinan besar kita bakal pake Activity itu sendiri sebagai LifecycleOwner. Lain cerita kalau kita observe dari Fragment. Kita bisa pake Activity yang jadi host dari Fragment-nya atau kita bisa juga pake Fragment itu sendiri sebagai LifecycleOwner. Kapan harus pake Activity kapan harus pake Fragment waktu melakukan observe dari Fragment?

  • Kalau pake Single Activity Architecture, bakal lebih bagus kalau kita pake Activity sebagai LifecycleOwner-nya. Kita bisa manfaatin lifecycle dari ViewModel sebagai cache dari data yang besar jadi ga perlu sering-sering ngelakuin request ke internet.
  • Pake Activity sebagai LifecycleOwner kalau butuh data yang sama di dua fragment berbeda.
  • Pake Fragment sebagai LifecycleOwner kalau data yang dibutuhin ga terlalu besar dan Fragment-nya emang butuh data yang spesifik.

Pada akhirnya semua balik lagi ke proses bisnis yang kita punya.

Selanjutnya kita bakal bikin PokemonListFragment.

Fragment ini ga beda jauh dengan fragment sebelumnya, kita butuh factory dan juga butuh adapter. Satu hal yang berbeda adalah pada baris

private val args: PokemonListFragmentArgs by navArgs()

PokemonListFragmentArgs memungkinkan kita untuk mengakses semua parameter yang diberikan saat memulai Fragment menggunakan NavigationController.

Yang terakhir kita bakal bikin PokemonCardDetailFragment.

Fragment terakhir ini sangat berbeda dengan sebelumnya. Kita tidak membutuhkan factory ataupun adapter karena kita tidak menggunakan RecyclerView dan ViewModel yang kita gunakan pun cukup sederhana. Jadi aplikasi kita selesai, ayo kita coba jalanin.

Loh kok ga muncul apa-apa ya? Padahal dari hasil test sebelumnya ga ada yang error. Kalau kita liat lagi di class BaseRepository ini.

Ada satu fungsi init. Fungsi init ini buat set dependency yang dibutuhin dari class Repository kita. Kenapa ga jadi parameter aja di constructor? Karena kita pengen implementasi Singleton Pattern di semua class Repository. Jadi cuma ada 1 object Repository buat semua jenis Repository di memori. Kalau gitu ayo kita coba liat cara kita nguji salah satu class Repository.

Ada satu baris yang belum kita panggil.

pokemonSetRepository = PokemonSetRepository.instance.apply {            init(localDataStore!!, remoteDataStore!!)        }

Selama ini kita cuma panggil variable instance dari class PokemonSetRepository tanpa panggil fungsi init. Jadi kita tau sekarang kalau kita harus panggil fungsi init buat semua jenis Repository yang bakal kita pake. Tapi di mana kita harus panggil fungsi ini? Kalau kita terus-terusan panggil .apply{ init() } berarti ga ada Singletonnya sama sekali dong?

Intinya kita harus panggil fungsi ini sekali selama aplikasi jalan. Yups, kita bisa panggil fungsi ini di class Application kita sendiri.

Setelah ini kita set supaya Android bakal jalanin class BaseApplication yang kita buat ini dan bukan class Application yang bawaan. Caranya kita edit di AndroidManifest.xml-nya jadi.

Tambahan android:name=”.BaseApplication” bakal mastiin kalau class BaseApplication yang bakal dijalanin di setiap aplikasi kita dibuka. Ayo kita coba jalanin lagi.

Berhasil! Kita sama sekali ga pernah nyentuh judul yang ada di Toolbar, tapi kalau kita liat hasilnya semua judul di Toolbar otomatis keganti dan dinamis sesuai dengan apa yang kita klik.

Finally!

Photo by Vasily Koloda on Unsplash

Yey, aplikasi sederhana dengan MVVM dan Android Jetpack kita udah beres. Banyak banget yang kita pelajari, dari mulai kenapa arsitektur itu penting, beberapa design pattern kayak Singleton Pattern, Repository Pattern dan Observer Pattern. Belum lagi kita juga bahas sedikit tentang beberapa prinsip SOLID. Hampir semua yang kita bahas di sini sebenernya masih dasarnya aja. Lebih banyak hal yang ga dibahas, tapi ini bisa jadi modal awal kalian untuk coba implementasi MVVM di project Android kalian.

Link project github akan dishare di akhir bagian ini. Ada banyak yang masih harus kita pelajari. Jadi apa lagi yang kita tunggu buat pelajari itu? Tapi pada akhirnya, semua ilmu yang kita punya tidak akan pernah bermanfaat kalau kita cuma pendam sendiri aja. Ayo kita mulai budaya untuk membagikan dan mengimplementasikan ilmu kita punya.

--

--