
Setiap pengembang Android menggunakan RecyclerView
untuk menampilkan daftar, dan masing-masing dihadapkan dengan masalah memperbarui data dalam daftar sampai kelas ajaib DiffUtil
muncul pada 2016. Saya akan menjelaskan dengan jari bagaimana cara kerjanya, dan mencoba menghilangkan sihirnya.
Sedikit sejarah
Salah satu elemen paling umum dalam aplikasi seluler adalah daftar, dalam kasus kami RecyclerView
. Ini bisa berupa daftar apa saja: alamat kantor, daftar teman di jejaring sosial. jaringan, riwayat pemesanan dalam aplikasi taksi, dll. Semua kasus ini disatukan oleh kebutuhan untuk secara konstan mengubah data dalam daftar ke yang baru, ketika, misalnya, pengguna melakukan Swipe untuk menyegarkan, memfilter daftar atau dengan cara lain menerima paket data baru dari belakang.
Untuk menerapkan perilaku ini, leluhur pengembang Android modern secara manual memilih data apa dan bagaimana perubahan, dan memanggil metode yang sesuai dari RecyclerView
. Namun, semuanya berubah ketika Google merilis Perpustakaan Dukungan versi 25.1.0, menambahkan DiffUtil
sana, yang memungkinkan Anda untuk secara ajaib mengubah daftar lama menjadi yang baru tanpa sepenuhnya membangun kembali RecyclerView
. Pada artikel ini, saya akan menghilangkan keajaiban DiffUtil
dan menjelaskan cara kerjanya.
Bagaimana cara kerja dengan DiffUtil?
Untuk bekerja dengan DiffUtil
Anda harus mengimplementasikan DiffUtil.Callback
, panggil metode calculateDiff(@NonNull Callback cb)
dan terapkan DiffResult
diterima ke RecyclerView
menggunakan metode dispatchUpdatesTo()
. Apa yang terjadi ketika metode calucalteDiff(@NonNull Callback cd)
dipanggil? Metode ini mengembalikan DiffResult
, yang berisi serangkaian operasi untuk mengubah daftar asli ke yang baru. Pembaruan diterapkan melalui panggilan ke notifyItemRangeInserted
, notifyItemRangeRemoved
, notifyItemMoved
dan notifyItemRangeChanged
. Tiga metode pertama mengubah struktur daftar, yaitu posisi elemen, tanpa mengubah elemen itu sendiri dan tidak memanggil onBindViewHolder()
pada mereka (dengan pengecualian elemen yang ditambahkan). Yang terakhir mengubah elemen itu sendiri dan memanggil onBindViewHolder()
untuk mengubah tampilan elemen.
DiffUtil
memeriksa dua daftar untuk perbedaan menggunakan algoritma Myers, yang hanya menentukan ada / tidaknya perubahan, tetapi tidak tahu bagaimana menemukan pergerakan elemen. Untuk melakukan ini, DiffUtil
berjalan melalui DiffUtil
dibuat oleh algoritma Myers '(lebih lanjut tentang ini nanti), dan kemudian mencari gerakan. DiffResult
dibentuk untuk
jika algoritma tidak memeriksa pergerakan elemen dan
, di mana P adalah jumlah elemen yang ditambahkan dan dihapus.
Algoritma Myers
Selanjutnya, penjelasan tentang algoritma Myers pada jari akan dipertimbangkan, tautan ke penjelasan matematis dari algoritma (serta artikel keren lainnya pada topik) akan berada di akhir artikel. Pertimbangkan dua urutan: BACAAC dan CBCBAB. Kita perlu menulis urutan transformasi pada urutan pertama, setelah itu kita mendapatkan urutan kedua. Kami menulis urutan dalam tabel sebagai berikut: daftar lama akan menunjukkan elemen pertama dari kolom, dan daftar baru akan menjadi elemen pertama dari baris.

Coret sel tempat elemen identik dari kedua sekuens berpotongan:

Tugas selanjutnya adalah pergi dari sudut kiri atas matriks ke sudut kanan bawah dengan jumlah langkah paling sedikit. Anda dapat bergerak sepanjang wajah horisontal dan vertikal. Jika Anda menekan titik di mana garis diagonal dimulai, maka Anda harus bergerak sepanjang itu, tetapi biaya langkah tersebut adalah 0. Dengan demikian, biaya langkah di sepanjang tepi adalah 1.

Dari titik (0; 0) kita bisa bergerak ke kanan dan ke bawah. Saat bergerak turun, Anda juga harus bergerak secara diagonal. Gerakan yang dilakukan dalam satu langkah disebut ular, dalam hal ini 2 ular yang diterima: (0; 0) -> (0; 1) dan (0; 0) -> (1; 2). Panah mengindikasikan akhir dari ular, yaitu jika setelah langkah vertikal / horizontal ada langkah wajib di sepanjang diagonal, maka panah akan berada di langkah di sepanjang diagonal. Konstruksi lengkap ular dari titik awal ke final ditunjukkan di bawah ini. Beberapa jalur pada video dihilangkan karena jelas bukan yang terpendek.

Akibatnya, kami mendapatkan beberapa jalur terpendek yang mungkin, beberapa di antaranya ditampilkan di bawah ini.


Bagaimana bisa meneruskan sebuah matriks dari paling kiri ke kanan membantu menentukan urutan tindakan (skrip) untuk mengubah satu urutan ke yang lain? Apa arti langkah horisontal, vertikal, dan diagonal? Langkah di sepanjang matriks di salah satu arah yang mungkin adalah tindakan pada baris lama:
- Langkah horisontal - hapus dari baris lama
- Langkah Vertikal - Tambahkan ke Baris Lama
- Langkah diagonal - tidak ada perubahan
Menggunakan jalur kedua sebagai contoh, kami membandingkan jalur dan skrip yang dihasilkan. Langkah pertama adalah vertikal, yang berarti kita menambahkan karakter "C" ke posisi 0 di baris lama.

Namun, ini bukan ular utuh. Selanjutnya, kita harus bergerak secara diagonal. Saat bergerak secara diagonal, elemen B tetap tidak berubah. Akibatnya, ular terdiri dari gerakan vertikal + gerakan diagonal.

Selanjutnya, ular itu secara horizontal - menghapus elemen A. dari garis lama

Video menunjukkan seluruh jalur dari awal hingga akhir dengan perubahan dalam string sumber hingga dikonversi ke yang terakhir.

Hasil dari algoritma Myers adalah sebuah skrip dengan serangkaian tindakan minimum yang harus dilakukan untuk mengubah satu urutan ke urutan lainnya. Dalam DiffUtil
algoritma Myers digunakan untuk mencari elemen yang berbeda yang ditentukan oleh metode areItemsTheSame()
. Selain membentuk daftar ular, ketika melewati daftar, algoritma Myers membuat daftar status elemen dari daftar lama dan baru. Semua data ini, serta flag detectMoves
dan panggilan balik yang diterapkan oleh pengguna, diteruskan ke konstruktor DiffResult(Callback callback, List<Snake> snakes, int[] oldItemStatuses, int[] newItemStatuses, boolean detectMoves)
.
Ketika saya sedang menulis artikel ini, saya dapat menggali apa yang sebenarnya terjadi di DiffResult
: algoritma berjalan melalui ular dan menetapkan bendera ke elemen (dalam daftar status), yang menentukan apa yang sebenarnya terjadi pada elemen. Menggunakan flag ini, ketika menerapkan perubahan pada RecyclerView
ditentukan metode mana yang menerapkan pembaruan untuk: notifyItemRangeInserted, notifyItemRangeRemoved, notifyItemMoved notifyItemRangeChanged
. Secara lebih rinci saya akan membicarakan hal ini lain kali.
Referensi