Untuk mencegah kueri memblokir UI, Room tidak mendukung akses database pada thread utama. Batasan ini berarti Anda harus membuat kueri DAO secara asinkron. Library Room menyertakan integrasi dengan beberapa framework untuk menyediakan eksekusi kueri asinkron.
Kueri DAO dibagi menjadi tiga kategori:
- Kueri tindakan tulis satu kali yang menyisipkan, memperbarui, atau menghapus data dalam database.
- Kueri tindakan baca satu kali yang membaca data dari database Anda hanya sekali dan menampilkan hasil beserta rekaman data dari database pada saat itu.
- Kueri tindakan baca yang dapat diamati yang membaca data dari database Anda setiap kali tabel database pokok berubah dan memunculkan nilai baru untuk mencerminkan perubahan tersebut.
Opsi bahasa dan framework
Room memberikan dukungan integrasi untuk interoperabilitas dengan library dan fitur bahasa tertentu. Tabel berikut menampilkan jenis nilai yang ditampilkan, yang dapat diterapkan berdasarkan jenis kueri dan framework:
| Jenis kueri | Fitur bahasa Kotlin (Native) | RxJava | Guava | Jetpack Lifecycle* |
|---|---|---|---|---|
| Tindakan tulis satu kali | Coroutine (suspend) |
Single<T>, Maybe<T>,
Completable |
ListenableFuture<T> |
T/A |
| Tindakan baca satu kali | Coroutine (suspend) |
Single<T>, Maybe<T> |
ListenableFuture<T> |
T/A |
| Tindakan baca yang dapat diamati | Flow<T> |
Flowable<T>, Publisher<T>,
Observable<T> |
T/A | LiveData<T> |
Panduan ini menunjukkan tiga cara menggunakan integrasi ini untuk mengimplementasikan kueri asinkron di DAO.
Kotlin dengan Flow dan coroutine
Kotlin menyediakan fitur bahasa bawaan yang memungkinkan Anda menulis kueri asinkron tanpa framework pihak ketiga:
- Room secara langsung mendukung Flow Kotlin untuk menulis kueri yang dapat diamati.
- Room memerlukan kata kunci
suspenduntuk membuat kueri DAO satu kali menjadi asinkron dengan coroutine Kotlin.
Dukungan Coroutine dan Flow dibangun langsung ke runtime Room inti, sehingga tidak diperlukan artefak tambahan.
RxJava untuk Kotlin dan Java
Room 3.0 mendukung jenis nilai yang ditampilkan RxJava 3. Untuk menggunakan jenis nilai yang ditampilkan RxJava, Anda harus mendaftarkan konverter jenis nilai yang ditampilkan RxJava di database atau DAO Anda:
- Sertakan artefak
androidx.room3:room3-rxjava3dalam konfigurasi build Anda. - Anotasikan deklarasi
@Databaseatau@DaoAnda dengan@DaoReturnTypeConverters(RxDaoReturnTypeConverters::class).
Room mendukung jenis nilai yang ditampilkan RxJava 3 berikut:
- Kueri one-shot:
Completable,Single<T>, danMaybe<T> - Kueri yang dapat diamati:
Publisher<T>,Flowable<T>, danObservable<T>
LiveData dan Guava
Room 3.0 mendukung jenis nilai yang ditampilkan LiveData dan Guava ListenableFuture menggunakan
konverter:
- LiveData: Sertakan artefak
androidx.room3:room3-livedatadan anotasi database atau DAO Anda dengan@DaoReturnTypeConverters(LiveDataDaoReturnTypeConverter::class). - Guava: Sertakan artefak
androidx.room3:room3-guavadan anotasi database atau DAO Anda dengan@DaoReturnTypeConverters(GuavaDaoReturnTypeConverter::class).
Menulis kueri satu kali asinkron
Kueri tindakan satu kali adalah operasi database yang hanya berjalan sekali dan mengambil rekaman data pada saat eksekusi. Berikut beberapa contoh kueri satu kali asinkron:
@Dao interface UserDao { @Query("SELECT * FROM user WHERE id = :id") suspend fun loadUserById(id: Int): User @Query("SELECT * from user WHERE region IN (:regions)") suspend fun loadUsersByRegion(regions: List<String>): List<User> }
Menulis kueri yang dapat diamati
Kueri yang dapat diamati adalah operasi baca yang memunculkan nilai baru setiap kali tabel yang dirujuk berubah. Misalnya, Anda dapat menggunakan perilaku ini untuk terus memperbarui daftar item yang ditampilkan saat database berubah. Berikut beberapa contoh kueri yang dapat diamati:
@Dao interface ObservableUserDao { @Query("SELECT * FROM user WHERE id = :id") fun loadUserById(id: Int): Flow<User> @Query("SELECT * from user WHERE region IN (:regions)") fun loadUsersByRegion(regions: List<String>): Flow<List<User>> }
Melacak pembatalan database secara manual
Saat perlu membuat operasi database yang dapat diamati secara manual, Anda dapat menggunakan API
createFlow dari InvalidationTracker. Dengan API ini, Anda dapat membuat Flow yang melacak modifikasi pada tabel tertentu dan mengirimkan notifikasi setiap kali tabel tersebut berubah.
fun getArtistTours(db: RoomDatabase, from: Date, to: Date): Flow<Map<Artist, TourState>> { return db.invalidationTracker.createFlow("Artist").map { _ -> val artists = artistsDao.getAllArtists() val tours = tourService.fetchStates(artists.map { it.id }) associateTours(artists, tours, from, to) } }
Secara default, Flow yang ditampilkan memancarkan nilai awal yang berisi semua
tabel terdaftar untuk memulai streaming. Anda dapat menonaktifkan perilaku ini dengan
menetapkan parameter emitInitialState ke false.
Konverter jenis nilai yang ditampilkan DAO kustom
Untuk jenis yang tidak didukung secara langsung oleh Room atau library ekstensinya, Anda dapat menentukan konverter jenis nilai yang ditampilkan DAO kustom untuk mendukung jenis nilai yang ditampilkan tambahan. Untuk mengubah hasil fungsi DAO menjadi jenis kustom Anda, anotasikan fungsi pengonversi dengan @DaoReturnTypeConverter.
Misalnya, Anda dapat menentukan pengonversi yang menggunakan androidx.tracing untuk menambahkan
bagian rekaman aktivitas di sekitar eksekusi kueri untuk memantau kueri yang sensitif terhadap performa
dengan membungkus eksekusi dalam jenis TracedQuery kustom:
class TracedQuery<T>(val result: T) object TracingDaoReturnTypeConverter { @DaoReturnTypeConverter([OperationType.READ]) suspend fun <T> convert( rawQuery: RoomRawQuery, executeAndConvert: suspend () -> T ): TracedQuery<T> { val result = trace("TracedQuery: ${rawQuery.sql}") { executeAndConvert() } return TracedQuery(result) } }
Untuk menggunakan pengonversi, anotasikan database atau DAO Anda dengan
@DaoReturnTypeConverters:
@Dao @DaoReturnTypeConverters(TracingDaoReturnTypeConverter::class) interface MusicDao { @Query("SELECT * FROM Song") suspend fun getAllSongs(): TracedQuery<List<Song>> }
Menginisialisasi pengonversi jenis nilai yang ditampilkan DAO kontrol
Biasanya, Room menangani pembuatan instance pengonversi jenis nilai yang ditampilkan DAO.
Namun, jika Anda harus meneruskan dependensi tambahan ke class konverter,
aplikasi Anda harus mengontrol inisialisasinya secara langsung. Jika demikian, anotasikan
class pengonversi dengan @ProvidedDaoReturnTypeConverter:
@ProvidedDaoReturnTypeConverter class TracingDaoReturnTypeConverter(val tracer: Tracer) { @DaoReturnTypeConverter([OperationType.READ]) suspend fun <T> convert( rawQuery: RoomRawQuery, executeAndConvert: suspend () -> T ): TracedQuery<T> { val result = tracer.trace("TracedQuery: ${rawQuery.sql}") { executeAndConvert() } return TracedQuery(result) } }
Kemudian, selain mendeklarasikan class pengonversi di
@DaoReturnTypeConverters, gunakan fungsi
RoomDatabase.Builder.addDaoReturnTypeConverter untuk meneruskan
instance class pengonversi ke builder RoomDatabase:
val db = Room.databaseBuilder<MyDatabase>(applicationContext, "database-name") .addDaoReturnTypeConverter(TracingDaoReturnTypeConverter(myLoggerInstance)) .build()
Persyaratan fungsi konverter
Fungsi @DaoReturnTypeConverter harus memenuhi beberapa persyaratan:
- Fungsi ini harus memiliki parameter fungsional sebagai argumen terakhirnya, biasanya bernama
executeAndConvert. Parameter ini adalah lambdasuspendyang dihasilkan Room untuk menjalankan kueri dan mengurai hasilnya.- Jika konverter perlu mengubah kueri, seperti Penomoran Halaman, lambda dapat menggunakan parameter
RoomRawQuery.
- Jika konverter perlu mengubah kueri, seperti Penomoran Halaman, lambda dapat menggunakan parameter
- Secara opsional, fungsi ini dapat menerima parameter berikut sebelum lambda:
db: RoomDatabase: Mengakses instance database, yang berguna untuk mendapatkan cakupan coroutine atau melakukan operasi tambahan.tableNames: Array<String>atauList<String>: Memberikan nama tabel yang diakses oleh kueri, yang berguna untuk jenis yang dapat diamati.rawQuery: RoomRawQuery: Menyediakan instance runtime kueri.inTransaction: Boolean: Menunjukkan apakah kueri sedang dieksekusi dalam transaksi.