Foto oleh rawpixel di Unsplash

Data Binding Library (yang selanjutnya akan kami sebut ‘DB library’ dalam postingan ini) menawarkan cara yang fleksibel dan kuat untuk mengikat data ke UI Anda, tetapi seperti pepatah lama: ‘kekuatan yang besar diikuti tanggung jawab yang besar’. Hanya karena Anda menggunakan pengikatan data, bukan berarti Anda bisa menghindar dari menjadi pelaku UI yang baik.
Saya telah menggunakan pengikatan data di Android selama beberapa tahun terakhir dan postingan ini merinci beberapa hal yang telah saya pelajari selama ini.



Gunakan pengikatan standar bila memungkinkan

Adapter pengikat khusus adalah cara terbaik untuk menambahkan fungsionalitas khusus ke View dengan mudah. Seperti banyak developer, saya bertindak agak jauh dengan adapter pengikatan dan berakhir dengan class yang penuh berisi 15 adapter dengan kualitas yang berbeda-beda.
Penyebabnya adalah sejumlah adapter yang menghasilkan string terformat dan menyetelnya di TextViews. Adapter biasanya dirujuk hanya dalam satu layout:

Meskipun ini mungkin terlihat pintar, tetapi ada tiga kelemahan besar:
  1. Sulit mengaturnya. Kecuali jika Anda adalah seorang yang sangat teratur, Anda cenderung memiliki satu file besar yang berisi semua metode adapter Anda. Antitesis yang kohesif dan terpisah.
  2. Anda harus menggunakan instrumentasi untuk pengujian. Menurut definisi, adapter pengikatan tidak menampilkan nilai, mereka mengambil input kemudian menetapkan properti pada tampilan. Ini berarti Anda harus menggunakan instrumentasi untuk menguji logika khusus Anda, yang membuat pengujian menjadi lebih lambat dan mungkin lebih sulit dijaga.
  3. Kode adapter pengikatan khusus (biasanya) tidak optimal. Jika Anda memperhatikan pengikatan teks bawaan [di sini], Anda akan melihat bahwa ia melakukan banyak pemeriksaan untuk menghindari pemanggilan TextView.setText(), sehingga mengefisienkan pemberian layout yang tidak terpakai. Saya jatuh ke dalam perangkap berpikir bahwa DB Library akan secara otomatis mengoptimalkan update tampilan. Dan itu benar, tetapi hanya jika Anda menggunakan adapter pengikat bawaan yang dioptimalkan dengan cermat.
Sebagai gantinya, pisahkan logika metode Anda menjadi class yang kohesif (saya menyebutnya kreator teks), kemudian teruskan ke pengikatan. Dari sana Anda bisa memanggil kreator teks dan menggunakan pengikatan tampilan bawaan: Dengan cara ini, kita mendapatkan semua efisiensi dari pengikatan bawaan, dan kita bisa dengan mudah menguji unit kode yang menciptakan string terformat.



Menjadikan adapter pengikatan khusus Anda lebih efisien

Jika Anda benar-benar perlu menggunakan adapter khusus, karena fungsionalitas yang Anda inginkan tidak tersedia, maka cobalah membuatnya seefisien mungkin. Maksud saya adalah dengan menggunakan semua optimalisasi UI Android standar: hindari memicu perubahan ukuran/layout bila memungkinkan.
Hal ini bisa hal yang sederhana seperti memeriksa apa yang sedang digunakan oleh tampilan vs. apa yang Anda setel. Berikut adalah contoh di mana kita menerapkan kembali adapter ImageView standar untuk android:drawable: Sayangnya, tampilan tidak selalu bisa mengekspos status mengenai apa yang perlu kita periksa. Berikut adalah contoh dengan setelan toggle max-lines pada TextView. Ia memfungsikan toggle dengan mengubah properti maxLines TextView, bersama dengan transisi layout tertunda.





Supaya Anda bisa tahu tentang apa yang dilakukannya

Sebelumnya, adapter pengikatan berformat sederhana dan selalu menyetel properti maxLines, bersama dengan listener klik. TextView akan selalu memicu layout ketika setMaxLines() dipanggil, yang berarti bahwa setiap kali adapter pengikatan dijalankan, layout akan terpicu.

Jadi mari kita perbaiki. Karena fungsionalitas ini sepenuhnya terpisah dari TextView (kita hanya memanggil setMaxLines() dengan nilai yang berbeda saat diklik), kita perlu menyimpan sendiri status ‘current’. Untungnya, View memberikan kita cara praktis untuk melakukannya melalui mekanisme tag. Di sini, kita hanya menyimpan nilai collapsedMaxLines yang saat ini disetel dalam tag, dan ketika adapter dijalankan, kita memanggil setMaxLines(), dll. hanya jika nilainya berbeda.



Hati-hati dengan apa yang Anda berikan sebagai variabel

Saya perlahan-lahan merancang-ulang Tivi menggunakan sesuatu yang mirip MVI, menggunakan MvRx library istimewa untuk menyusunnya. Artinya dalam praktik adalah bahwa fragmen/tampilan saya menganut ke ViewModel, dan menerima instance ViewState. Instance tersebut berisi semua kondisi yang diperlukan untuk menampilkan UI.
Berikut adalah contoh class status dari Tivi (link): Anda bisa melihat bahwa ini hanyalah class data sederhana yang berisi semua hal yang diperlukan UI untuk menampilkan UI detail tentang acara TV.
Kedengarannya seperti kandidat yang sempurna untuk diberikan ke instance pengikatan data, dan mengizinkan ekspresi pengikatan kita mengupdate UI, bukan? Ya, ia memang bekerja dengan baik, tetapi ada beberapa hal yang harus diperhatikan, dan itu dikarenakan cara kerja ‘DB Library’.
Dalam pengikatan data, Anda mendeklarasikan input, melalui tag <variable>, dan kemudian menuliskan ekspresi pengikatan yang merujuk variabel-variabel tersebut pada tampilan (atribut). Ketika salah satu variabel dependen berubah, ‘DB Library’ akan menjalankan ekspresi pengikatan Anda (sehingga mengupdate tampilan). Deteksi perubahan ini adalah pengoptimalan hebat yang Anda dapatkan secara gratis.
Jadi kembali ke skenario saya. Layout saya menjadi seperti ini:

Jadi saya akhirnya memiliki instance ViewState global berukuran besar yang berisi seluruh status UI, dan seperti yang bisa Anda bayangkan, perubahannya cukup banyak. Setiap perubahan kecil di status UI menghasilkan ViewState baru yang dihasilkan dan diteruskan ke instance pengikatan data kita.
Jadi apa masalahnya? Nah, karena kita hanya memiliki satu variabel input, semua ekspresi pengikatan akan merujuk ke variabel tersebut, yang berarti bahwa ‘DB Library’ tidak bisa lagi secara selektif memilih ekspresi yang akan dijalankan. Dalam praktiknya, ini berarti bahwa setiap kali variabel berubah (tidak peduli seberapa kecil), setiap ekspresi pengikatan akan dijalankan.
Masalah ini tidak terkait secara khusus dengan MVI, ini hanyalah sebuah artefak dari penggabungan status dan penggunaannya dengan pengikatan data.



Jadi, apa yang bisa Anda lakukan?

Alternatifnya adalah dengan mendeklarasikan secara eksplisit setiap variabel dari ViewState di layout Anda, kemudian secara eksplisit meneruskan nilai-nilainya dari instance status gabungan Anda, seperti:
Terdapat lebih banyak kode yang harus dijaga dan disinkronkan bagi Anda sebagai developer, tetapi hal ini berarti bahwa ‘DB Library’ bisa mengoptimalkan ekspresi mana yang akan dijalankan. Saya akan menggunakan pola ini jika status UI Anda tidak sering berubah (mungkin beberapa kali saat dibuat) dan jumlah variabelnya tidak banyak.
Secara pribadi, saya terus menggunakan variabel tunggal di layout, memberikan instance ViewState saya, dan mengandalkan fakta bahwa pengikatan tampilan kami melakukan hal yang benar. Inilah sebabnya mengapa mengefisienkan pengikatan tampilan adalah hal yang sangat penting.
Hal lain yang perlu diperhatikan adalah bahwa Tivi adalah pengguna berat RecyclerView, dengan Epoxy + Data Binding, yang berarti bahwa terdapat tataran kalkulasi perubahan tambahan yang terjadi di DiffUtil. Jadi jika UI Anda sebagian besar juga terdiri dari RecyclerViews, Anda tetap mendapatkan pengoptimalan serupa secara gratis.



Sedikit demi sedikit, lama-lama menjadi bukit

Semoga postingan ini memperjelas beberapa hal kecil yang bisa Anda lakukan untuk mengoptimalkan implementasi pengikatan data. Mengetahui sedikit tentang bagaimana ‘DB Library’ bekerja secara internal bisa membantu Anda mengefisienkan pengikatan data, dan meningkatkan kinerja UI Anda.