Room ไม่รองรับการเข้าถึงฐานข้อมูลในเทรดหลักเพื่อป้องกันไม่ให้คำค้นหาบล็อก UI ข้อจำกัดนี้หมายความว่าคุณต้องทำให้คำค้นหา DAO เป็นแบบอะซิงโครนัส ไลบรารี Room มีการผสานรวมกับเฟรมเวิร์กหลายรายการเพื่อรองรับการดำเนินการคำค้นหาแบบอะซิงโครนัส
คำค้นหา DAO แบ่งออกเป็น 3 หมวดหมู่ดังนี้
- คำค้นหา การเขียนแบบครั้งเดียว ที่แทรก อัปเดต หรือลบข้อมูลในฐานข้อมูล
- คำค้นหา การอ่านแบบครั้งเดียว ที่อ่านข้อมูลจากฐานข้อมูลเพียงครั้งเดียวและแสดงผลลัพธ์พร้อมสแนปชอตของฐานข้อมูล ณ เวลานั้น
- คำค้นหา การอ่านแบบสังเกตได้ ที่อ่านข้อมูลจากฐานข้อมูลทุกครั้งที่ตารางฐานข้อมูลพื้นฐานมีการเปลี่ยนแปลง และส่งค่าใหม่เพื่อแสดงการเปลี่ยนแปลงเหล่านั้น
ตัวเลือกภาษาและเฟรมเวิร์ก
Room รองรับการผสานรวมเพื่อการทำงานร่วมกันกับฟีเจอร์และไลบรารีภาษาที่เฉพาะเจาะจง ตารางต่อไปนี้แสดงประเภทการแสดงผลที่ใช้ได้ตามประเภทคำค้นหาและเฟรมเวิร์ก
| ประเภทคำค้นหา | ฟีเจอร์ภาษา Kotlin (เนทีฟ) | RxJava | กวาวา | Jetpack Lifecycle* |
|---|---|---|---|---|
| การเขียนแบบครั้งเดียว | โครูทีน (suspend) |
Single<T>, Maybe<T>,
Completable |
ListenableFuture<T> |
ไม่มี |
| การอ่านแบบครั้งเดียว | โครูทีน (suspend) |
Single<T>, Maybe<T> |
ListenableFuture<T> |
ไม่มี |
| การอ่านแบบสังเกตได้ | Flow<T> |
Flowable<T>, Publisher<T>,
Observable<T> |
ไม่มี | LiveData<T> |
คู่มือนี้แสดง 3 วิธีในการใช้การผสานรวมเหล่านี้เพื่อใช้คำค้นหาแบบอะซิงโครนัสใน DAO
Kotlin พร้อม Flow และโครูทีน
Kotlin มีฟีเจอร์ภาษาในตัวที่ช่วยให้คุณเขียนคำค้นหาแบบอะซิงโครนัสได้โดยไม่ต้องใช้เฟรมเวิร์กของบุคคลที่สาม
- Room รองรับ Flow ของ Kotlin โดยตรง เพื่อเขียนคำค้นหาแบบสังเกตได้
- Room กำหนดให้ใช้คีย์เวิร์ด
suspendเพื่อทำให้คำค้นหา DAO แบบครั้งเดียว เป็นแบบอะซิงโครนัสด้วย โครูทีนของ Kotlin
การรองรับโครูทีนและ Flow มีอยู่ในรันไทม์หลักของ Room โดยตรง จึงไม่จำเป็นต้องมีอาร์ติแฟกต์เพิ่มเติม
RxJava สำหรับ Kotlin และ Java
Room 3.0 รองรับประเภทการแสดงผล RxJava 3 หากต้องการใช้ประเภทการแสดงผล RxJava คุณต้องลงทะเบียนตัวแปลงประเภทการแสดงผล RxJava ในฐานข้อมูลหรือ DAO โดยทำดังนี้
- รวมอาร์ติแฟกต์
androidx.room3:room3-rxjava3ไว้ในการกำหนดค่าบิลด์ - ใส่คำอธิบายประกอบ
@Databaseหรือการประกาศ@Daoด้วย@DaoReturnTypeConverters(RxDaoReturnTypeConverters::class)
Room รองรับประเภทการแสดงผล RxJava 3 ต่อไปนี้
- คำค้นหาแบบครั้งเดียว:
Completable,Single<T>, และMaybe<T> - คำค้นหาแบบสังเกตได้:
Publisher<T>,Flowable<T>, และObservable<T>
LiveData และกวาวา
Room 3.0 รองรับประเภทการแสดงผล LiveData และ ListenableFuture ของกวาวาโดยใช้ตัวแปลง
- LiveData: รวมอาร์ติแฟกต์
androidx.room3:room3-livedataและ ใส่คำอธิบายประกอบฐานข้อมูลหรือ DAO ด้วย@DaoReturnTypeConverters(LiveDataDaoReturnTypeConverter::class) - กวาวา: รวมอาร์ติแฟกต์
androidx.room3:room3-guavaและใส่คำอธิบายประกอบ ฐานข้อมูลหรือ DAO ด้วย@DaoReturnTypeConverters(GuavaDaoReturnTypeConverter::class)
เขียนคำค้นหาแบบครั้งเดียวแบบอะซิงโครนัส
คำค้นหาแบบครั้งเดียวเป็นการดำเนินการฐานข้อมูลที่ทำงานเพียงครั้งเดียวและดึงสแนปชอตของข้อมูล ณ เวลาที่ดำเนินการ ตัวอย่างคำค้นหาแบบครั้งเดียวแบบอะซิงโครนัสมีดังนี้
@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> }
เขียนคำค้นหาแบบสังเกตได้
คำค้นหาแบบสังเกตได้เป็นการดำเนินการอ่านที่ส่งค่าใหม่ทุกครั้งที่ตารางที่อ้างอิงมีการเปลี่ยนแปลง ตัวอย่างเช่น คุณสามารถใช้ลักษณะการทำงานนี้เพื่ออัปเดตรายการที่แสดงอยู่เสมอเมื่อฐานข้อมูลมีการเปลี่ยนแปลง ตัวอย่างคำค้นหาแบบสังเกตได้มีดังนี้
@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>> }
ติดตามการทำให้ฐานข้อมูลไม่ถูกต้องด้วยตนเอง
เมื่อคุณต้องสร้างการดำเนินการฐานข้อมูลแบบสังเกตได้ด้วยตนเอง คุณสามารถใช้
createFlow API ของ InvalidationTracker ได้ API นี้ช่วยให้คุณสร้าง Flow ที่ติดตามการแก้ไขตารางที่เฉพาะเจาะจงและส่งการแจ้งเตือนทุกครั้งที่ตารางเหล่านั้นมีการเปลี่ยนแปลง
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) } }
โดยค่าเริ่มต้น Flow ที่แสดงผลจะส่งค่าเริ่มต้นที่มีตารางที่ลงทะเบียนทั้งหมดเพื่อเริ่มสตรีม คุณสามารถปิดใช้ลักษณะการทำงานนี้ได้โดยตั้งค่าพารามิเตอร์ emitInitialState เป็น false
ตัวแปลงประเภทการแสดงผล DAO ที่กำหนดเอง
สำหรับประเภทที่ Room หรือไลบรารีส่วนขยายไม่รองรับโดยตรง คุณสามารถกำหนดตัวแปลงประเภทการแสดงผล DAO ที่กำหนดเองเพื่อรองรับประเภทการแสดงผลเพิ่มเติมได้ หากต้องการแปลงผลลัพธ์ของฟังก์ชัน DAO เป็นประเภทที่กำหนดเอง
ให้ใส่คำอธิบายประกอบฟังก์ชันตัวแปลงด้วย @DaoReturnTypeConverter
ตัวอย่างเช่น คุณสามารถกำหนดตัวแปลงที่ใช้ androidx.tracing เพื่อเพิ่มส่วนการติดตามรอบๆ การดำเนินการคำค้นหาเพื่อตรวจสอบคำค้นหาที่ไวต่อประสิทธิภาพโดยการห่อการดำเนินการในประเภท TracedQuery ที่กำหนดเอง
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) } }
หากต้องการใช้ตัวแปลง ให้ใส่คำอธิบายประกอบฐานข้อมูลหรือ DAO ด้วย
@DaoReturnTypeConverters
@Dao @DaoReturnTypeConverters(TracingDaoReturnTypeConverter::class) interface MusicDao { @Query("SELECT * FROM Song") suspend fun getAllSongs(): TracedQuery<List<Song>> }
ควบคุมการเริ่มต้นตัวแปลงประเภทการแสดงผล DAO
โดยปกติแล้ว Room จะจัดการการสร้างอินสแตนซ์ของตัวแปลงประเภทการแสดงผล DAO
อย่างไรก็ตาม หากคุณต้องส่งการขึ้นต่อกันเพิ่มเติมไปยังคลาสตัวแปลง แอปของคุณต้องควบคุมการเริ่มต้นของคลาสตัวแปลงโดยตรง ในกรณีนี้ ให้ใส่คำอธิบายประกอบคลาสตัวแปลงด้วย @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) } }
จากนั้น นอกเหนือจากการประกาศคลาสตัวแปลงใน
@DaoReturnTypeConverters แล้ว ให้ใช้
RoomDatabase.Builder.addDaoReturnTypeConverter ฟังก์ชันเพื่อส่ง
อินสแตนซ์ของคลาสตัวแปลงไปยังบิลเดอร์ RoomDatabase
val db = Room.databaseBuilder<MyDatabase>(applicationContext, "database-name") .addDaoReturnTypeConverter(TracingDaoReturnTypeConverter(myLoggerInstance)) .build()
ข้อกำหนดของฟังก์ชันตัวแปลง
ฟังก์ชัน @DaoReturnTypeConverter ต้องเป็นไปตามข้อกำหนดหลายประการดังนี้
- ต้องมีพารามิเตอร์ฟังก์ชันเป็นอาร์กิวเมนต์สุดท้าย ซึ่งโดยปกติจะมีชื่อว่า
executeAndConvertพารามิเตอร์นี้เป็นแลมบ์ดาsuspendที่ Room สร้างขึ้นเพื่อดำเนินการคำค้นหาและแยกวิเคราะห์ผลลัพธ์- หากตัวแปลงต้องแปลงคำค้นหา เช่น การแบ่งหน้า แลมบ์ดาสามารถใช้พารามิเตอร์
RoomRawQueryได้
- หากตัวแปลงต้องแปลงคำค้นหา เช่น การแบ่งหน้า แลมบ์ดาสามารถใช้พารามิเตอร์
- สามารถยอมรับพารามิเตอร์ต่อไปนี้ก่อนแลมบ์ดาได้ (ไม่บังคับ)
db: RoomDatabase: เข้าถึงอินสแตนซ์ฐานข้อมูล ซึ่งมีประโยชน์สำหรับการรับขอบเขตโครูทีนหรือการดำเนินการเพิ่มเติมtableNames: Array<String>หรือList<String>: แสดงชื่อตารางที่คำค้นหาเข้าถึง ซึ่งมีประโยชน์สำหรับประเภทที่สังเกตได้rawQuery: RoomRawQuery: แสดงอินสแตนซ์รันไทม์ของคำค้นหาinTransaction: Boolean: บ่งบอกว่าคำค้นหากำลังดำเนินการภายในธุรกรรมหรือไม่