Artikel ini menjelaskan secara terperinci tahapan pengembangan aplikasi mobile Meeting Room Helper: dari awal ide hingga rilis. Aplikasi ini ditulis dalam Kotlin dan dibangun di atas arsitektur MVVM yang disederhanakan, tanpa menggunakan pengikatan data. Bagian UI diperbarui menggunakan objek LiveData. Alasan penolakan pengikatan data dirinci dan dijelaskan. Arsitektur menggunakan sejumlah solusi menarik yang memungkinkan untuk secara logis membagi program menjadi file-file kecil, yang pada akhirnya menyederhanakan dukungan kode.


Deskripsi Proyek
3 tahun yang lalu, perusahaan kami datang dengan ide untuk mengembangkan proyek kecil untuk pemesanan instan ruang pertemuan. Sebagian besar manajer SDM dan Arcadia lebih suka menggunakan kalender Outlook untuk tujuan seperti itu, tetapi bagaimana dengan yang lain?
Saya akan memberikan 2 contoh dari kehidupan pengembang
- Setiap tim secara berkala memiliki keinginan spontan untuk mengadakan reli cepat selama 5-10 menit. Keinginan ini dapat menyalip pengembang di sudut kantor mana pun, dan agar tidak mengganggu kolega di sekitar mereka, mereka (pengembang dan bukan hanya) mulai mencari percakapan gratis. Bermigrasi dari kamar ke kamar (di kantor kami, ruang pertemuan diatur secara berurutan), kolega “memeriksa dengan cermat” kamar mana yang saat ini gratis. Akibatnya, mereka mengalihkan perhatian rekan kerja di dalam. Orang-orang seperti itu selalu dan akan selalu ada, bahkan jika eksekusi harus ditembak dalam piagam perusahaan untuk gangguan reli. Siapa yang mengerti, dia akan mengerti.
- Dan di sini ada kasus lain. Anda baru saja meninggalkan ruang makan dan menuju ke diri Anda sendiri, tetapi di sini kolega Anda (atau manajer) dari departemen lain mencegat Anda. Dia ingin memberi tahu Anda sesuatu yang mendesak, dan untuk keperluan ini Anda perlu ruang rapat. Menurut peraturan, pertama-tama Anda harus memesan kamar (dari ponsel atau komputer) dan baru kemudian menempatinya. Baik jika Anda memiliki ponsel dengan Outlook seluler. Dan jika tidak? Kembali ke komputer, lalu kembali ke ruang rapat? Untuk memaksa setiap karyawan meletakkan Outlook Express di telepon dan memastikan semua orang membawa telepon? Ini bukan metode kami.
Itulah sebabnya 2,5 tahun yang lalu masing-masing ruang pertemuan dilengkapi dengan tabletnya sendiri:

Untuk proyek ini, kolega saya mengembangkan versi pertama aplikasi: Ruang Rapat Pembantu Kecil (di
sini Anda dapat membacanya ). MRLH diizinkan memesan reservasi, membatalkan, dan memperbarui reservasi, menunjukkan status percakapan yang tersisa. Mengenali identitas karyawan (menggunakan layanan cloud Microsoft Face API dan analis internal kami) telah menjadi “trik” inovatif. Aplikasi itu ternyata solid dan melayani perusahaan dengan setia selama 2,5 tahun.
Tetapi waktu berlalu ... Gagasan baru muncul. Saya ingin sesuatu yang baru, jadi kami memutuskan untuk menulis ulang aplikasi.
Kerangka Acuan
Seperti yang sering terjadi - tetapi, sayangnya, tidak selalu - pengembangan dimulai dengan persiapan spesifikasi teknis. Pertama-tama, kami memanggil orang-orang yang paling sering menggunakan tablet untuk pemesanan. Kebetulan bahwa sebagian besar dari mereka kecanduan SDM dan manajer yang sebelumnya menggunakan Outlook secara eksklusif. Dari mereka, kami menerima umpan balik berikut (dari persyaratan segera jelas apa yang diminta SDM dan apa yang diminta manajer):
- Anda harus menambahkan kemampuan untuk memesan ruang rapat apa pun dari tablet apa pun (sebelumnya, setiap tablet hanya mengizinkan Anda memesan kamar);
- alangkah baiknya untuk melihat jadwal rapat umum untuk pertemuan sepanjang hari (idealnya, untuk hari apa pun);
- seluruh siklus pengembangan harus dilakukan dalam waktu singkat (selama 6-7 minggu).
Semuanya jelas dengan keinginan pelanggan, tetapi bagaimana dengan persyaratan teknis dan masa depan? Tambahkan beberapa persyaratan untuk proyek dari guild pengembang:
- Sistem harus bekerja baik dengan tablet yang ada, dan dengan yang baru;
- skalabilitas sistem - dari 50 percakapan ke atas (ini harus cukup dengan margin bagi sebagian besar pelanggan jika sistem mulai mereplikasi);
- mempertahankan fungsionalitas sebelumnya (versi pertama aplikasi menggunakan Java API untuk berkomunikasi dengan layanan Outlook, dan kami berencana untuk menggantinya dengan Microsoft Graph API khusus, jadi penting untuk tidak kehilangan fungsionalitas);
- meminimalkan konsumsi energi (tablet didukung oleh baterai eksternal, karena pusat bisnis tidak mengizinkan pengeboran dindingnya untuk memasang kabel kami);
- desain UX / UI baru, yang secara ergonomis mencerminkan semua inovasi.
Total 8 poin. Persyaratannya cukup adil. Selain itu, kami menetapkan aturan pengembangan umum:
- gunakan hanya teknologi canggih (ini akan memungkinkan tim untuk berkembang sebagai spesialis dan tidak mandek di satu tempat, sementara menyederhanakan dukungan proyek di masa mendatang);
- ikuti praktik terbaik, tetapi jangan membabi buta menerima begitu saja, seperti aturan utama dari setiap profesional (dan pengembang yang berjuang untuk ini) adalah untuk mengevaluasi semuanya secara kritis;
- Menulis kode yang bersih dan rapi (mungkin ini yang paling sulit ketika Anda mencoba menggabungkan inovasi dan waktu pengembangan yang ketat).
Sebuah awal telah dibuat. Itu, seperti biasa, antusias! Mari kita lihat apa yang terjadi selanjutnya.
Desain
Desain aplikasi yang dikembangkan oleh desainer UX:


Ini adalah layar utama. Ini akan ditampilkan sebagian besar waktu. Semua informasi yang diperlukan berlokasi secara ergonomis di sini:
- nama kamar dan nomornya;
- status saat ini;
- waktu sampai pertemuan berikutnya (atau sampai akhir);
- status kamar yang tersisa di bagian bawah layar.
Harap dicatat: dial hanya menampilkan 12 jam, seperti sistem dikonfigurasikan sesuai dengan kebutuhan perusahaan (tablet Arcadia bekerja dari jam 8 pagi sampai jam 8 malam, nyalakan dan matikan secara otomatis)


Untuk memesan kamar, cukup hubungi jendela pemesanan dan tentukan durasi rapat umum. Langkah-langkah untuk memesan kamar yang tersisa serupa, mereka hanya mulai dengan mengklik ikon kamar.


Jika Anda ingin menjadwalkan rapat untuk waktu tertentu, buka tab berikutnya, ke daftar rapat yang akan berlangsung hari ini di ruang rapat, dan klik waktu luang. Selanjutnya, semuanya seperti dalam kasus pertama.
Pohon transisi yang lengkap harus terlihat seperti ini:


Mari kita coba implementasikan secara kompeten.
Tumpukan teknologi
Teknik pengembangan berkembang agak cepat dan berubah. Selama 2 tahun berikutnya, Java adalah bahasa pengembangan Android resmi. Semua orang menulis di Java dan menggunakan data binding. Sekarang, menurut saya, kami bergerak menuju pemrograman reaktif dan Kotlin. Java adalah bahasa yang hebat, tetapi memiliki beberapa ketidaksempurnaan dibandingkan dengan apa yang ditawarkan Kotlin dan AndroidX. Kotlin dan AndroidX dapat mengurangi penggunaan data yang mengikat hingga minimum, jika tidak sepenuhnya mengecualikannya. Di bawah ini saya akan mencoba menjelaskan sudut pandang saya.
Kotlin
Saya pikir banyak pengembang Android telah beralih ke Kotlin, dan karena itu setuju dengan saya bahwa menulis proyek Android baru pada 2019 dalam bahasa apa pun selain Kotlin seperti berperang di laut. Tentu saja Anda bisa berdebat, tetapi bagaimana dengan Flutter dan Dart? Bagaimana dengan C ++, C #, dan bahkan Cordova? Yang akan saya jawab: pilihan selalu ada di tangan Anda.
Pada 480 SM raja Persia Xerxes memerintahkan tentaranya untuk menyeberangi laut sebagai hukuman karena menghancurkan sebagian pasukannya selama badai, dan lima abad kemudian, kaisar Romawi Caligula menyatakan perang terhadap Poseidon. Masalah selera. Untuk 9 dari 10, Kotlin baik, tetapi 10 mungkin buruk. Itu semua tergantung pada Anda, pada keinginan dan aspirasi Anda.
Kotlin adalah pilihanku. Bahasanya sederhana dan indah. Menulisnya mudah dan menyenangkan, dan yang paling penting, tidak perlu menulis terlalu banyak: kelas data, objek, setter dan pengambil opsional, ekspresi lambda sederhana dan fungsi ekstensi. Ini hanya sebagian kecil dari apa yang ditawarkan bahasa ini. Jika Anda belum beralih ke Kotlin - silakan pergi! Di bagian latihan, saya akan menunjukkan beberapa kelebihan bahasa (ini bukan tawaran iklan).
Model-View-ViewModel
MVVM saat ini adalah arsitektur aplikasi yang direkomendasikan dari Google. Selama pengembangan, kami akan mematuhi pola khusus ini, namun, kami tidak akan mengamatinya sepenuhnya, karena MVVM merekomendasikan untuk menggunakan pengikatan data, tetapi kami menolaknya.
Pro dari MVVM- Diferensiasi logika bisnis dan UI. Dalam implementasi MVVM yang benar, seharusnya tidak ada satu pun impor android di ViewModel, kecuali objek LiveData dari paket AndroidX atau Jetpack. Penggunaan yang benar secara otomatis meninggalkan semua pekerjaan UI di dalam fragmen dan aktivitas. Bukankah itu hebat?
- Tingkat enkapsulasi dipompa. Akan lebih mudah untuk bekerja sebagai tim: sekarang Anda dapat bekerja bersama di satu layar dan tidak saling mengganggu. Sementara satu pengembang bekerja dengan layar, yang lain dapat membangun ViewModel, dan yang ketiga dapat menulis kueri di Repositori.
- MVVM memiliki efek positif pada penulisan unit test. Item ini mengikuti dari yang sebelumnya. Jika semua kelas dan metode dirangkum dari bekerja dengan UI, mereka dapat dengan mudah diuji.
- Solusi alami dengan rotasi layar. Tidak peduli seberapa aneh kedengarannya, tetapi fitur ini diperoleh secara otomatis, dengan transisi ke MVVM (karena data disimpan dalam ViewModel). Jika Anda memeriksa aplikasi yang cukup populer (VK, Telegram, Sberbank-Online dan Aviasales), ternyata separuh dari mereka tidak dapat memutar layar. Yang menyebabkan saya terkejut dan salah paham sebagai pengguna aplikasi ini.
Mengapa MVVM berbahaya?- Kebocoran memori. Kesalahan berbahaya ini terjadi jika Anda melanggar hukum menggunakan LiveData dan pengamat. Kami akan memeriksa kesalahan ini secara rinci di bagian latihan.
- ViewModel yang luas. Jika Anda mencoba memasukkan semua logika bisnis ke dalam ViewModel, Anda akan mendapatkan kode yang tidak dapat dibaca. Jalan keluar dari situasi ini mungkin memecah ViewModel menjadi hierarki, atau menggunakan Penyaji. Itulah tepatnya yang saya lakukan.
Aturan untuk bekerja dengan MVVMMari kita mulai dengan kesalahan yang paling banyak dan pergi ke kesalahan yang kurang:
- badan permintaan tidak boleh dalam ViewModel (hanya di Repositori);
- Objek LiveData didefinisikan dalam ViewModel, karena mereka tidak melemparkan diri ke dalam Repositori, karena permintaan di Repositori diproses menggunakan Rx-Java (atau coroutines);
- semua fungsi pemrosesan harus dipindahkan ke kelas dan file pihak ketiga ("Penyaji"), agar tidak mengacaukan ViewModel dan tidak mengalihkan perhatian dari esensi.
Livedata
LiveData adalah kelas pemegang data yang dapat diamati. Tidak seperti yang biasa diamati, LiveData sadar akan siklus hidup, yang berarti ia menghormati siklus hidup komponen aplikasi lain, seperti aktivitas, fragmen, atau layanan. Kesadaran ini memastikan LiveData hanya memperbarui pengamat komponen aplikasi yang dalam keadaan siklus hidup aktif.
Sumber: developer.android.com/topic/libraries/architecture/livedataKesimpulan sederhana dapat diambil dari definisi: LiveData adalah alat pemrograman reaktif yang andal. Kami akan menggunakannya untuk memperbarui bagian UI tanpa ikatan data. Kenapa begitu
Struktur file XML tidak memungkinkan distribusi data secara ringkas yang diperoleh dari <data> ... </data>. Jika semuanya jelas dengan file kecil, lalu bagaimana dengan file besar? Apa yang harus dilakukan dengan layar yang kompleks, beberapa termasuk dan melewati beberapa bidang? Gunakan model di mana-mana? Dapatkan ikatan bidang yang kaku? Dan jika bidang tersebut harus diformat, panggil metode dari paket Java? Ini membuat kode tanpa harapan dan sepenuhnya spageti. Sama sekali tidak seperti yang dijanjikan MVVM.
Menolak pengikatan data akan membuat perubahan pada bagian UI transparan. Semua pembaruan akan terjadi langsung di dalam pengamat. Karena Karena kode Kolin ringkas dan jelas, kami tidak akan mendapatkan masalah dengan pengamat yang membengkak. Menulis dan memelihara kode akan menjadi lebih mudah. File XML hanya akan digunakan untuk desain - tidak ada properti di dalamnya.
Pengikatan data adalah alat yang ampuh. Ini bagus untuk menyelesaikan beberapa masalah, dan selaras dengan Java, tetapi dengan Kotlin ... Dengan Kotlin, dalam kebanyakan kasus, pengikatan data hanya bersifat rudimenter. Pengikatan data hanya memperumit kode dan tidak memberikan keunggulan kompetitif.
Di Jawa, Anda punya pilihan: menggunakan pengikatan data, atau menulis banyak kode jelek. Di Kotlin, Anda dapat mengakses elemen tampilan secara langsung, mem-bypass findViewById (), serta propertinya. Sebagai contoh:
Sebuah pertanyaan logis muncul: mengapa repot-repot dengan model berkebun di dalam file XML, menggunakan metode Java dalam file XML, membebani logika bagian XML jika semua ini bisa dihindari?
Coroutines bukannya Thread () dan Rx-Java
Coroutine sangat ringan dan mudah digunakan. Mereka ideal untuk tugas asinkron yang paling sederhana: memproses hasil permintaan, memperbarui UI, dll.
Coroutine dapat secara efektif mengganti Thread () dan Rx-Java dalam kasus di mana kinerja tinggi tidak diperlukan, karena mereka membayar ringan dengan kecepatan. Rx-Java, tidak diragukan lagi, lebih fungsional, namun untuk tugas-tugas sederhana semua asetnya tidak diperlukan.
Microsoft dan yang lainnya
Untuk bekerja dengan layanan Outlook, Microsoft Graph API akan digunakan. Dengan izin yang sesuai, Anda dapat memperoleh semua informasi yang diperlukan tentang karyawan, ruang, dan acara (rapat). Untuk pengenalan wajah, layanan cloud Microsoft Face API akan digunakan.
Melihat sedikit ke depan, saya akan mengatakan bahwa untuk memecahkan masalah skalabilitas, Firebase cloud storage digunakan. Ini akan dibahas di bawah.
Arsitektur
Masalah skalabilitas
Sangat sulit untuk membuat sistem terukur sepenuhnya atau sebagian. Ini sangat sulit dilakukan jika versi pertama aplikasi tidak dapat diskalakan, dan yang kedua harus menjadi. Aplikasi v1 mengirim permintaan ke semua kamar sekaligus. Masing-masing tablet secara teratur mengirim permintaan ke server untuk memperbarui semua data. Pada saat yang sama, perangkat tidak melakukan sinkronisasi satu sama lain, karena proyek tidak memiliki server sendiri.
Tentu saja, jika kita mengikuti jalur yang sama dan mengirim permintaan N dari masing-masing tablet N, maka pada titik tertentu kita akan membatalkan Microsoft Graph API atau mendapatkan pembekuan sistem kami.
Adalah logis untuk menggunakan solusi client-server di mana server polling grafik, mengumpulkan data dan, berdasarkan permintaan, memberikan informasi ke tablet, tetapi di sini kita bertemu dengan kenyataan. Tim proyek terdiri dari 2 orang (pengembang dan perancang Android). Mereka harus memenuhi tenggat waktu 7 minggu dan backend tidak disediakan, karena penskalaan adalah persyaratan dari pengembang. Tetapi ini tidak berarti bahwa ide itu harus ditinggalkan?
Mungkin satu-satunya solusi yang tepat dalam situasi ini adalah penggunaan penyimpanan cloud. Firebase akan mengganti server dan bertindak sebagai penyangga. Kemudian ternyata yang berikut ini:
setiap tablet hanya menyurvei alamatnya dari Microsoft Graph API, dan, jika perlu, menyinkronkan data dalam penyimpanan cloud, dari tempat itu dapat dibaca oleh perangkat lain.Keuntungan dari implementasi ini akan menjadi respons yang cepat, karena Firebase bekerja dalam mode real-time. Kami akan mengurangi jumlah permintaan yang dikirim ke server N kali, yang berarti perangkat akan bekerja dengan baterai sedikit lebih lama. Dari sudut pandang keuangan, proyek tidak naik harga, karena Untuk proyek ini, versi gratis Firebase sudah cukup dengan beberapa cadangan: penyimpanan 1 GB, 10 ribu otorisasi per bulan dan 100 koneksi sekaligus. Kerugiannya bisa termasuk ketergantungan pada kerangka kerja pihak ketiga, tetapi Firebase menginspirasi kepercayaan pada kami, karena Ini adalah produk stabil yang dikelola dan dikembangkan oleh Google.
Gagasan umum sistem baru adalah sebagai berikut: N tablet dan platform cloud untuk sinkronisasi data waktu-nyata. Mari kita mulai merancang aplikasi itu sendiri.
LiveData di Repositori
Tampaknya saya baru-baru ini menetapkan aturan bentuk yang baik dan segera melanggar salah satunya. Tidak seperti penggunaan LiveData yang direkomendasikan di dalam ViewModel, dalam proyek ini objek LiveData diinisialisasi ke repositori, dan semua repositori dinyatakan sebagai singleton. Kenapa begitu
Solusi serupa dikaitkan dengan mode aplikasi. Tablet buka dari jam 8 pagi sampai 8 malam. Selama ini, hanya Helper Ruang Rapat yang diluncurkan pada mereka. Akibatnya, banyak objek dapat dan harus berumur panjang (itulah sebabnya semua repositori dirancang sebagai singleton).
Selama bekerja, konten UI secara teratur beralih, yang pada gilirannya memerlukan pembuatan dan rekreasi objek ViewModel. Ternyata jika Anda menggunakan LiveData di dalam ViewModel, maka untuk setiap fragmen yang dibuat, ViewModel sendiri akan dibuat dengan sekumpulan objek LiveData yang ditentukan. Jika 2 fragmen serupa ditampilkan secara bersamaan di layar, dengan ViewModel dan Base-ViewModel yang sama, maka selama inisialisasi akan ada duplikasi objek LiveData dari Base-ViewModel. Di masa depan, duplikat ini akan memakan ruang memori sampai mereka dihancurkan oleh "pengumpul sampah." Karena Jika kita sudah memiliki repositori dalam bentuk singleton dan kami ingin meminimalkan biaya pembuatan kembali layar, akan lebih bijaksana untuk mentransfer objek LiveData di dalam repositori singleton, sehingga memfasilitasi objek ViewModel dan mempercepat aplikasi.
Tentu saja, ini tidak berarti bahwa Anda perlu mentransfer semua LiveData dari ViewModel ke repositori, tetapi Anda harus lebih serius mendekati masalah ini dan membuat pilihan Anda secara sadar. Kerugian dari pendekatan ini adalah peningkatan jumlah objek berumur panjang, karena semua repositori didefinisikan sebagai singleton dan masing-masing menyimpan objek LiveData. Tetapi dalam kasus tertentu, Pembantu Ruang Rapat bukan minus, karena aplikasi berjalan tanpa henti sepanjang hari, tanpa mengalihkan konteks ke aplikasi lain.
Arsitektur yang dihasilkan

- Semua permintaan dieksekusi dalam repositori. Semua repositori (di Helper Ruang Pertemuan ada 11) yang dirancang sebagai singleton. Mereka dibagi berdasarkan jenis objek yang dikembalikan dan disembunyikan di balik fasad.
- Logika bisnis berada di ViewModel. Berkat penggunaan "Penyaji", ukuran total semua ViewModel (ada 6 dalam proyek) ternyata kurang dari 120 baris.
- Aktivitas dan fragmen hanya terlibat dalam mengubah bagian UI, menggunakan pengamat dan LiveData yang dikembalikan dari ViewModel.
- Fungsi untuk memproses dan menghasilkan data disimpan di "presenter". Fungsi izin yang digunakan secara aktif dari Kotlin untuk pemrosesan data.
Logika latar belakang telah dipindahkan ke Intent-Service:
- Event-Update-Service. Layanan yang bertanggung jawab untuk menyinkronkan data kamar saat ini di Firebase dan Graph API.
- Layanan Pengguna-Mengenali. Hanya berjalan di master tablet. Bertanggung jawab untuk menambah staf baru ke sistem. Memeriksa daftar orang yang sudah terlatih dengan daftar dari Active Directory. Jika orang baru muncul, layanan menambahkan mereka ke Face API dan melatih kembali jaringan saraf. Setelah operasi selesai, dimatikan. Itu dimulai ketika aplikasi dimulai.
- Layanan Notifikasi-Online memberi tahu tablet lain bahwa tablet ini berfungsi, mis. Baterai eksternal tidak habis. Ia bekerja melalui Firebase.
Hasilnya adalah arsitektur yang agak fleksibel dan benar dari sudut pandang distribusi tanggung jawab yang memenuhi semua persyaratan pembangunan modern. Jika di masa depan kita meninggalkan Microsoft Graph API, Firebase, atau modul lainnya, mereka dapat dengan mudah diganti dengan yang baru tanpa mengganggu aplikasi lainnya. Kehadiran sistem "presenter" yang luas memungkinkan untuk mengambil semua fungsi pemrosesan data di luar inti. Akibatnya, arsitekturnya menjadi sangat jernih, yang merupakan nilai tambah besar. Masalah ViewModel yang terlalu besar telah sepenuhnya hilang.
Di bawah ini saya akan memberikan contoh bundel yang biasa digunakan dalam aplikasi yang dikembangkan.
Berlatih. Tonton Pembaruan
Bergantung pada kondisi ruang pertemuan, dial menunjukkan salah satu kondisi berikut:


Selain itu, lengkungan sementara unjuk rasa terletak di sepanjang garis dial, dan pusat menghitung mundur sampai akhir pertemuan atau sampai awal reli berikutnya. Semua ini dilakukan oleh perpustakaan kanvas yang kami kembangkan. Jika kisi pertemuan telah berubah, kita harus memperbarui data di perpustakaan.
Karena LiveData diumumkan dalam Repositori, paling logis untuk memulainya.
Repositori
FirebaseRoomRepository - kelas yang bertanggung jawab untuk mengirim dan memproses permintaan di Firebase terkait dengan model Room.
Untuk menunjukkan, kode inisialisasi firebase pendengar sedikit disederhanakan (fungsi menyambung kembali dihapus). Mari kita lihat poin dari apa yang terjadi di sini:
- repositori dirancang sebagai singleton (di Kotlin, cukup untuk mengganti kata kunci kelas dengan objek);
- inisialisasi objek LiveData;
- ValueEventListener dideklarasikan sebagai variabel untuk menghindari penciptaan kembali kelas anonim jika terjadi penyambungan kembali (ingat, saya menyederhanakan inisialisasi dengan menghapus penyambungan kembali jika terputus);
- inisialisasi ValueEventListener (jika data dalam Firebase berubah, pendengar akan segera menjalankan dan memperbarui data dalam objek LiveData);
- Pembaruan untuk objek LiveData.
Fungsi itu sendiri dipindahkan ke file FirebaseRoomRepositoryPresenter yang terpisah dan didekorasi sebagai fungsi ekstensi.
fun MutableLiveData<List<Room>>.updateOtherRooms(rooms: MutableList<Room>) { this.postValue(rooms.filter { !it.isOwnRoom() }) }
Contoh fungsi ekstensi dari FirebaseRoomRepositoryPresenterJuga, untuk pemahaman umum tentang gambar, saya akan memberikan daftar objek Room.
- Kelas data. Pengubah ini secara otomatis menghasilkan dan mengganti metode toString (), HashCode (), dan equal (). Tidak perlu lagi mendefinisikan ulang sendiri.
- Daftar Acara dari objek Kamar. Daftar inilah yang diperlukan untuk memperbarui data di perpustakaan dial.
Semua kelas Repositori disembunyikan di belakang kelas fasad.
object Repository {
- Di atas Anda dapat melihat daftar semua kelas repositori bekas dan fasad tingkat kedua. Ini menyederhanakan pemahaman umum kode dan menunjukkan daftar semua kelas repositori yang terhubung.
- Daftar metode yang mengembalikan referensi ke objek LiveData dari FirebaseRoomRepository. Setter dan getter Kotlin adalah opsional, jadi Anda tidak perlu menuliskannya secara tidak perlu.
Organisasi semacam itu memungkinkan Anda untuk memenuhi 20 hingga 30 permintaan dalam satu repositori root. Jika aplikasi Anda memiliki lebih banyak permintaan, Anda harus membagi fasad root menjadi 2 atau lebih.
ViewModel
BaseViewModel adalah ViewModel dasar dari mana semua ViewModels diwarisi. Ini termasuk satu objek currentRoom tunggal, digunakan secara universal.
- Marker terbuka berarti bahwa Anda dapat mewarisi dari kelas. Secara default di Kotlin, semua kelas dan metode adalah final, mis. kelas tidak dapat diwarisi, dan metode tidak dapat didefinisikan ulang. Ini untuk melindungi dari perubahan versi yang tidak kompatibel yang tidak disengaja. Saya akan memberi contoh.
Anda sedang mengembangkan versi baru perpustakaan. Pada titik tertentu, karena satu dan lain alasan, Anda memutuskan untuk mengubah nama kelas atau mengubah tanda tangan beberapa metode. Dengan mengubahnya, Anda secara tidak sengaja membuat ketidakcocokan versi. Ups ... Jika Anda mungkin tahu bahwa metode ini dapat ditimpa oleh seseorang, dan kelasnya diwarisi, Anda mungkin akan lebih akurat dan tidak akan menembak kaki Anda. Untuk melakukan ini, di Kotlin, secara default, semuanya dinyatakan final, dan untuk pembatalan ada pengubah "terbuka".
- Metode getCurrentRoom () mengembalikan tautan ke objek LiveData dari ruang saat ini dari Repositori, yang, pada gilirannya, diambil dari FirebaseRoomRepository. Ketika metode ini dipanggil, objek Kamar akan kembali berisi semua informasi tentang ruangan, termasuk daftar acara.
Untuk mengonversi data dari satu format ke format lainnya, kami akan menggunakan transformasi. Untuk melakukan ini, buat
MainFragmentViewModel dan mewarisinya dari
BaseViewModel .
MainFragmentViewModel adalah kelas
turunan dari BaseViewModel. ViewModel ini hanya digunakan di MainFragment.
- Perhatikan kurangnya pengubah terbuka. Ini berarti tidak ada yang mewarisi dari kelas.
- currentRoomEvents - objek yang diperoleh menggunakan transformasi. Segera setelah objek perubahan ruang saat ini, transformasi dilakukan dan objek currentRoomEvents diperbarui.
- MediatorLiveData. Hasilnya identik dengan transformasi (ditunjukkan untuk referensi).
Opsi pertama digunakan untuk mengonversi data dari satu jenis ke jenis lainnya, yang kami butuhkan, dan opsi kedua diperlukan untuk menjalankan beberapa logika bisnis. Namun, konversi data tidak terjadi. Ingat bahwa impor android di ViewModel tidak valid. Karenanya, saya memulai permintaan tambahan dari sini atau memulai kembali layanan sebagaimana diperlukan.
Pemberitahuan penting! Agar transformasi atau mediator berfungsi, seseorang harus berlangganan dari fragmen atau aktivitas. Kalau tidak, kode tidak akan dieksekusi, karena tidak ada yang akan mengharapkan hasil (ini adalah objek pengamat).
Mainfragment
Langkah terakhir dalam mengonversi data menjadi hasil. MainFragment mencakup pustaka panggil dan Lihat-Pager di bagian bawah layar.
class MainFragment : BaseFragment() {
- Inisialisasi dari MainFragmentViewModel. Pengubah lateinit menunjukkan bahwa kami berjanji untuk menginisialisasi objek ini nanti, sebelum kami menggunakannya. Kotlin mencoba melindungi pemrogram dari penulisan kode yang salah, jadi kita harus segera mengatakan bahwa objek tersebut bisa nol, atau menaruh lateinit. Dalam hal ini, ViewModel harus diinisialisasi oleh objek.
- Pengamat-pendengar untuk memperbarui nomor.
- Menginisialisasi ViewModel. Harap dicatat bahwa ini terjadi segera setelah fragmen melekat pada aktivitas.
- Setelah aktivitas dibuat, kami berlangganan perubahan ke objek currentRoomEvents. Harap dicatat bahwa saya tidak berlangganan ke siklus hidup fragmen (ini), tetapi ke objek viewLifecycleOwner. Faktanya adalah bahwa di perpustakaan dukungan 28.0.0 dan AndroidX 1.0.0 bug terdeteksi ketika pengamat "berhenti berlangganan". Untuk mengatasi masalah ini, tambalan dalam bentuk viewLifecycleOwner dirilis, dan Google menyarankan untuk berlangganan. Ini memperbaiki masalah pengamat zombie ketika fragmen itu mati dan pengamat terus bekerja. Jika Anda masih menggunakan ini, pastikan untuk menggantinya dengan viewLifecycleOwner.
Jadi, saya ingin menunjukkan kesederhanaan dan keindahan MVVM dan LiveData tanpa menggunakan data binding. Harap dicatat bahwa dalam proyek ini saya melanggar aturan yang diterima secara umum dengan menempatkan LiveData di Repositori karena kekhasan proyek. Namun, jika kami memindahkannya ke ViewModel, keseluruhan gambar akan tetap tidak berubah.
Sebagai ceri pada kue, saya telah menyiapkan untuk Anda sebuah video pendek dengan demonstrasi (nama-nama dioleskan sesuai dengan persyaratan keamanan, saya minta maaf):

Ringkasan
Sebagai hasil dari aplikasi bekerja di bulan pertama, beberapa bug terungkap pada tampilan cross rallies (Outlook memungkinkan Anda untuk membuat beberapa acara pada saat yang bersamaan, sementara sistem kami tidak). Sekarang sistem telah bekerja selama 3 bulan. Kesalahan atau kegagalan tidak diamati.
PS Terima kasih
jericho_code untuk komentarnya. Di Kotlin, Anda dapat dan harus menginisialisasi Daftar <> dalam model menggunakan blankList (), maka objek tambahan tidak dibuat.
var events: List<Event.Short> = emptyList()