כדי למנוע משאילתות לחסום את ממשק המשתמש, Room לא תומך בגישה למסד נתונים בשרשור הראשי. ההגבלה הזו מחייבת אתכם להפוך את השאילתות שלכם ב-DAO לאסינכרוניות. ספריית Room כוללת שילובים עם כמה מסגרות (frameworks) כדי לספק ביצוע שאילתות אסינכרוני.
שאילתות DAO מתחלקות לשלוש קטגוריות:
- שאילתות One-shot write שמוסיפות, מעדכנות או מוחקות נתונים במסד הנתונים.
- שאילתות One-shot read קוראות נתונים ממסד הנתונים רק פעם אחת ומחזירות תוצאה עם תמונת המצב של מסד הנתונים באותו זמן.
- שאילתות Observable read שקוראות נתונים מהמסד בכל פעם שמתבצע שינוי בטבלאות הבסיסיות של המסד, ומפיקות ערכים חדשים כדי לשקף את השינויים האלה.
אפשרויות שפה ו-framework
Room מספקת תמיכה באינטגרציה לצורך פעולה הדדית עם תכונות וספריות ספציפיות של שפות. בטבלה הבאה מוצגים סוגי ההחזרה הרלוונטיים על סמך סוג השאילתה והמסגרת:
| סוג השאילתה | תכונות של שפת Kotlin (Native) | 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> |
במדריך הזה מוצגות שלוש דרכים להשתמש בשילובים האלה כדי להטמיע שאילתות אסינכרוניות ב-DAO.
Kotlin עם Flow ושגרות המשך (coroutines)
Kotlin מספקת תכונות שפה מובנות שמאפשרות לכם לכתוב שאילתות אסינכרוניות בלי מסגרות צד שלישי:
- ספריית Room תומכת ישירות ב-Flow של Kotlin כדי לכתוב שאילתות שניתן לצפות בהן.
- כדי להפוך את השאילתות שלכם ב-DAO ללא חוסמות באמצעות Kotlin coroutines, צריך להשתמש במילת המפתח
suspendב-Room.
התמיכה ב-coroutines וב-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:
- שאילתות עם דוגמה אחת (one-shot):
Completable,Single<T>, וMaybe<T> - Observable queries:
Publisher<T>,Flowable<T>, andObservable<T>
LiveData ו-Guava
Room 3.0 תומך בסוגי החזרה של LiveData ו-Guava ListenableFuture באמצעות ממירים:
- LiveData: כוללים את ארטיפקט
androidx.room3:room3-livedataומבצעים הערות במסד הנתונים או ב-DAO באמצעות@DaoReturnTypeConverters(LiveDataDaoReturnTypeConverter::class). - Guava: כוללים את ארטיפקט
androidx.room3:room3-guavaומבצעים הערה (annotation) של מסד הנתונים או של 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>> }
מעקב ידני אחר ביטול התוקף של מסד הנתונים
כשצריך ליצור פעולות מסד נתונים שניתנות לצפייה באופן ידני, אפשר להשתמש ב-API createFlow של 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. הפרמטר הזה הואsuspendlambda ש-Room יוצרת כדי להריץ את השאילתה ולנתח את התוצאה.- אם הממיר צריך לשנות את השאילתה, למשל להוסיף לה מספור דפים, פונקציית הלמבדה יכולה לקבל פרמטר
RoomRawQuery.
- אם הממיר צריך לשנות את השאילתה, למשל להוסיף לה מספור דפים, פונקציית הלמבדה יכולה לקבל פרמטר
- אפשר להוסיף לפני פונקציית ה-lambda את הפרמטרים הבאים:
-
db: RoomDatabase: ניגש למופע של מסד הנתונים, וזה שימושי כדי לקבל את היקף הקורוטינה או לבצע פעולות נוספות. -
tableNames: Array<String>אוList<String>: השדות האלה מספקים את שמות הטבלאות שהשאילתה ניגשת אליהן, וזה שימושי לסוגים שניתנים לצפייה. -
rawQuery: RoomRawQuery: מספק את מופע זמן הריצה של השאילתה. -
inTransaction: Boolean: מציין אם השאילתה מופעלת בתוך טרנזקציה.
-