Android Kotlin Fundamentals 06.1: Create a Room database

ה-codelab הזה הוא חלק מהקורס Android Kotlin Fundamentals. כדי להפיק את המרב מהקורס הזה, מומלץ לעבוד על ה-codelabs לפי הסדר. כל ה-codelab של הקורס מפורטים בדף הנחיתה של ה-codelab בנושא יסודות Kotlin ל-Android.

מבוא

ברוב האפליקציות יש נתונים שצריך לשמור, גם אחרי שהמשתמש סוגר את האפליקציה. לדוגמה, האפליקציה יכולה לאחסן פלייליסט, מלאי של פריטים במשחק, רשומות של הוצאות והכנסות, קטלוג של קבוצות כוכבים או נתוני שינה לאורך זמן. בדרך כלל משתמשים במסד נתונים כדי לאחסן נתונים קבועים.

Room היא ספריית מסדי נתונים שמהווה חלק מ-Jetpack. ‫Room מטפל בהרבה מהמשימות של הגדרת מסד נתונים וקביעת ההגדרות שלו, ומאפשר לאפליקציה שלכם ליצור אינטראקציה עם מסד הנתונים באמצעות קריאות רגילות לפונקציות. מתחת לפני השטח, Room היא שכבת הפשטה מעל מסד נתונים של SQLite. הטרמינולוגיה של Room ותחביר השאילתות בשאילתות מורכבות יותר מבוססים על מודל SQLite.

בתמונה שלמטה אפשר לראות איך מסד הנתונים Room משתלב בארכיטקטורה הכוללת שמומלצת בקורס הזה.

מה שכדאי לדעת

חשוב שתכירו את:

  • איך בונים ממשק משתמש (UI) בסיסי לאפליקציית Android
  • שימוש בפעילויות, בקטעים ובתצוגות.
  • ניווט בין פרגמנטים ושימוש ב-Safe Args (תוסף Gradle) כדי להעביר נתונים בין פרגמנטים.
  • הצגת מודלים, מפעלים של מודלים לתצוגה ו-LiveData והמשתמשים שלו. הנושאים האלה של רכיבי ארכיטקטורה מוסברים ב-codelab קודם בקורס הזה.
  • הבנה בסיסית של מסדי נתונים של SQL ושפת SQLite. אפשר לקרוא את המדריך המהיר ל-SQLite כדי לקבל סקירה כללית או לרענן את הידע.

מה תלמדו

  • איך יוצרים מסד נתונים של Room ואיך מקיימים איתו אינטראקציה כדי לשמור נתונים.
  • איך ליצור מחלקת נתונים שמגדירה טבלה במסד הנתונים.
  • איך משתמשים באובייקט לגישה לנתונים (DAO) כדי למפות פונקציות של Kotlin לשאילתות SQL.
  • איך בודקים אם מסד הנתונים פועל.

הפעולות שתבצעו:

  • יצירת מסד נתונים של Room עם ממשק לנתוני שינה ליליים.
  • בודקים את מסד הנתונים באמצעות הבדיקות שסופקו.

ב-codelab הזה, תבנו את החלק של מסד הנתונים באפליקציה שעוקבת אחרי איכות השינה. האפליקציה משתמשת במסד נתונים כדי לאחסן את נתוני השינה לאורך זמן.

לאפליקציה יש שני מסכים שמיוצגים על ידי קטעים, כמו שמוצג באיור שלמטה.

במסך הראשון, שמוצג בצד ימין, יש לחצנים להתחלת המעקב ולהפסקתו. במסך מוצגים כל נתוני השינה של המשתמש. הלחצן ניקוי מוחק באופן סופי את כל הנתונים שהאפליקציה אספה על המשתמש.

במסך השני, שמוצג בצד שמאל, בוחרים את דירוג איכות השינה. באפליקציה, הדירוג מיוצג בצורה מספרית. למטרות פיתוח, האפליקציה מציגה גם את סמלי הפנים וגם את הערכים המספריים שלהם.

מסלול המשתמש הוא כדלקמן:

  • המשתמש פותח את האפליקציה ומוצג לו מסך מעקב השינה.
  • המשתמש מקיש על הלחצן התחלה. השדה הזה מתעד את שעת ההתחלה ומציג אותה. הלחצן התחלה מושבת, והלחצן עצירה מופעל.
  • המשתמש מקיש על הלחצן הפסקה. השעה שבה תסיימו לישון תתועד ותיפתח לכם האפשרות לדרג את איכות השינה.
  • המשתמש בוחר בסמל של איכות השינה. המסך נסגר, ובמסך המעקב מוצגים שעת סיום השינה ואיכות השינה. הלחצן עצירה מושבת והלחצן התחלה מופעל. האפליקציה מוכנה לעוד לילה.
  • הלחצן ניקוי מופעל בכל פעם שיש נתונים במסד הנתונים. כשמשתמש מקיש על הלחצן ניקוי, כל הנתונים שלו נמחקים בלי אפשרות לשחזור – לא מוצגת הודעה עם השאלה 'האם אתה בטוח?'.

האפליקציה הזו משתמשת בארכיטקטורה פשוטה, כפי שמוצג בהמשך בהקשר של הארכיטקטורה המלאה. האפליקציה משתמשת רק ברכיבים הבאים:

  • בקר ממשק משתמש
  • הצגת הדגם ו-LiveData
  • מסד נתונים של Room

שלב 1: הורדה והפעלה של אפליקציית המתחילים

  1. מורידים את האפליקציה TrackMySleepQuality-Starter מ-GitHub.
  2. מבצעים Build ומריצים את האפליקציה. ממשק המשתמש של קטע SleepTrackerFragment מוצג באפליקציה, אבל לא מוצגים נתונים. הכפתורים לא מגיבים להקשה.

שלב 2: בודקים את אפליקציית המתחילים

  1. בודקים את קובצי Gradle:
  • קובץ Gradle של הפרויקט
    בקובץ build.gradle ברמת הפרויקט, שימו לב למשתנים שמציינים את גרסאות הספרייה. הגרסאות שבהן נעשה שימוש באפליקציית המתחילים פועלות היטב יחד, ופועלות היטב עם האפליקציה הזו. עד שתסיימו את ה-codelab הזה, יכול להיות ש-Android Studio יציע לכם לעדכן חלק מהגרסאות. אתם יכולים לבחור אם לעדכן או להישאר עם הגרסאות שמופיעות באפליקציה. אם נתקלתם בשגיאות קומפילציה "מוזרות", נסו להשתמש בשילוב של גרסאות הספריות שבהן נעשה שימוש באפליקציית הפתרון הסופי.
  • קובץ Gradle של המודול. שימו לב לרכיבים התלויים שסופקו לכל הספריות של Android Jetpack, כולל Room, ולרכיבים התלויים של קורוטינות.
  1. כדאי לעיין בחבילות ובממשק המשתמש. האפליקציה בנויה לפי פונקציונליות. החבילה מכילה קבצים של מצייני מיקום שבהם תוסיפו קוד במהלך סדרת ה-codelab הזו.
  • החבילה database, לכל הקוד שקשור למסד הנתונים Room.
  • החבילות sleepquality ו-sleeptracker מכילות את הפראגמנט, את מודל התצוגה ואת מפעל מודל התצוגה של כל מסך.
  1. כדאי לעיין בUtil.kt קובץ, שכולל פונקציות שיעזרו לכם להציג נתונים על איכות השינה. חלק מהקוד מופיע כהערה כי הוא מתייחס למודל תצוגה שיוצרים בהמשך.
  2. מעיינים בתיקיית androidTest (SleepDatabaseTest.kt). הבדיקה הזו תשמש לאימות מסד הנתונים.

ב-Android, הנתונים מיוצגים במחלקות נתונים, והגישה לנתונים והשינוי שלהם מתבצעים באמצעות קריאות לפונקציות. אבל בעולם של מסדי נתונים, צריך ישויות ושאילתות.

  • ישות מייצגת אובייקט או מושג, ואת המאפיינים שלו, שמאוחסנים במסד הנתונים. מחלקת ישויות מגדירה טבלה, וכל מופע של המחלקה הזו מייצג שורה בטבלה. כל מאפיין מגדיר עמודה. באפליקציה, הישות תכיל מידע על לילה של שינה.
  • שאילתה היא בקשה לנתונים או למידע מטבלה במסד נתונים או משילוב של טבלאות, או בקשה לבצע פעולה על הנתונים. שאילתות נפוצות הן שאילתות לקבלת ישויות, להוספת ישויות ולעדכון ישויות. לדוגמה, אפשר לשלוח שאילתה על כל הלילות שבהם ישנתם, ממוינים לפי שעת ההתחלה.

Room מבצע את כל העבודה הקשה בשבילכם, כדי להמיר מחלקות נתונים של Kotlin לישויות שאפשר לאחסן בטבלאות SQLite, ומפונקציות מוצהרות לשאילתות SQL.

צריך להגדיר כל ישות כמחלקת נתונים עם הערות, ואת האינטראקציות כממשק עם הערות, אובייקט לגישה לנתונים (DAO). ‫Room משתמש במחלקות האלה עם ההערות כדי ליצור טבלאות במסד הנתונים, ושאילתות שפועלות על מסד הנתונים.

שלב 1: יצירת הישות SleepNight

במשימה הזו מגדירים לילה אחד של שינה כסוג נתונים עם הערות.

כדי לתעד לילה אחד של שינה, צריך לתעד את שעת ההתחלה, שעת הסיום ודירוג האיכות.

בנוסף, צריך מזהה כדי לזהות באופן ייחודי את הלילה.

  1. בחבילה database, מאתרים את הקובץ SleepNight.kt ופותחים אותו.
  2. יוצרים את מחלקת הנתונים SleepNight עם פרמטרים למזהה, לזמן התחלה (באלפיות השנייה), לזמן סיום (באלפיות השנייה) ולדירוג מספרי של איכות השינה.
  • צריך לאתחל את sleepQuality, לכן מגדירים אותו לערך -1, שמציין שלא נאספו נתונים איכותיים.
  • צריך גם לאתחל את שעת הסיום. מגדירים את שעת ההתחלה כדי לציין שעדיין לא נרשמה שעת סיום.
data class SleepNight(
       var nightId: Long = 0L,
       val startTimeMilli: Long = System.currentTimeMillis(),
       var endTimeMilli: Long = startTimeMilli,
       var sleepQuality: Int = -1
)
  1. לפני הצהרת הכיתה, מוסיפים את ההערה @Entity לכיתת הנתונים. נותנים לטבלה את השם daily_sleep_quality_table. הארגומנט של tableName הוא אופציונלי, אבל מומלץ. אפשר לעיין בתיעוד כדי למצוא ארגומנטים נוספים.

    אם מוצגת בקשה, מייבאים את Entity ואת כל ההערות האחרות מהספרייה androidx.
@Entity(tableName = "daily_sleep_quality_table")
data class SleepNight(...)
  1. כדי לציין ש-nightId הוא המפתח הראשי, מוסיפים את ההערה @PrimaryKey למאפיין nightId. מגדירים את הפרמטר autoGenerate לערך true כדי ש-Room ייצור את המזהה לכל ישות. כך מובטח שהמזהה של כל לילה יהיה ייחודי.
@PrimaryKey(autoGenerate = true)
var nightId: Long = 0L,...
  1. מוסיפים הערות למאפיינים שנותרו באמצעות @ColumnInfo. להתאים אישית את שמות הנכסים באמצעות פרמטרים כמו שמוצג בהמשך.
import androidx.room.ColumnInfo
import androidx.room.Entity
import androidx.room.PrimaryKey

@Entity(tableName = "daily_sleep_quality_table")
data class SleepNight(
       @PrimaryKey(autoGenerate = true)
       var nightId: Long = 0L,

       @ColumnInfo(name = "start_time_milli")
       val startTimeMilli: Long = System.currentTimeMillis(),

       @ColumnInfo(name = "end_time_milli")
       var endTimeMilli: Long = startTimeMilli,

       @ColumnInfo(name = "quality_rating")
       var sleepQuality: Int = -1
)
  1. מריצים את הקוד כדי לוודא שאין בו שגיאות.

במשימה הזו תגדירו אובייקט לגישה לנתונים (DAO). ב-Android, ה-DAO מספק שיטות נוחות להוספה, למחיקה ולעדכון של מסד הנתונים.

כשמשתמשים בRoom מסד נתונים, מגדירים פונקציות של Kotlin בקוד ומפעילים אותן כדי לשלוח שאילתה למסד הנתונים. הפונקציות האלה של Kotlin ממופות לשאילתות SQL. מגדירים את המיפויים האלה ב-DAO באמצעות הערות, ו-Room יוצר את הקוד הנדרש.

אפשר לחשוב על DAO כהגדרה של ממשק מותאם אישית לגישה למסד הנתונים.

לפעולות נפוצות במסד נתונים, ספריית Room מספקת הערות נוחות, כמו @Insert,‏ @Delete ו-@Update. לכל שאר המקרים, יש את ההערה @Query. אפשר לכתוב כל שאילתה שנתמכת על ידי SQLite.

כבונוס נוסף, בזמן שאתם יוצרים את השאילתות ב-Android Studio, מהדר בודק את שאילתות ה-SQL שלכם כדי לוודא שאין בהן שגיאות תחביר.

כדי להשתמש במסד הנתונים של מעקב השינה, צריך:

  • מוסיפים לילות חדשים.
  • עדכון של לילה קיים כדי לעדכן את שעת הסיום ואת דירוג האיכות.
  • קבלת לילה ספציפי על סמך המפתח שלו.
  • Get all nights (קבלת כל הלילות), כדי שתוכלו להציג אותם.
  • קבלת הנתונים מהלילה האחרון.
  • מחיקה של כל הרשומות במסד הנתונים.

שלב 1: יצירת SleepDatabase DAO

  1. בחבילה database, פותחים את SleepDatabaseDao.kt.
  2. שימו לב שהערך interface SleepDatabaseDao מסומן בהערה @Dao. צריך להוסיף את מילת המפתח @Dao לכל אובייקט DAO.
@Dao
interface SleepDatabaseDao {}
  1. בתוך גוף הממשק, מוסיפים הערה @Insert. מתחת ל-@Insert, מוסיפים פונקציית insert() שמקבלת מופע של המחלקה Entity SleepNight כארגומנט.

    זה הכול. ‫Room ייצור את כל הקוד הדרוש להוספת SleepNight למסד הנתונים. כשמתקשרים אל insert() מקוד Kotlin, ‏ Room מריץ שאילתת SQL כדי להוסיף את הישות למסד הנתונים. (הערה: אפשר לתת לפונקציה כל שם שרוצים).
@Insert
fun insert(night: SleepNight)
  1. הוספת הערה @Update עם פונקציה update() לSleepNight אחד. הישויות שמתעדכנות הן אלה שיש להן מפתח זהה לזה שמועבר. אפשר לעדכן חלק מהמאפיינים האחרים של הישות או את כולם.
@Update
fun update(night: SleepNight)

אין הערה נוחה לפונקציונליות הנותרת, ולכן צריך להשתמש בהערה @Query ולספק שאילתות SQLite.

  1. מוסיפים הערה @Query עם פונקציה get() שמקבלת ארגומנט Long key ומחזירה ערך SleepNight שניתן לאכלוס בערך null. תוצג שגיאה לגבי פרמטר חסר.
@Query
fun get(key: Long): SleepNight?
  1. השאילתה מסופקת כפרמטר מחרוזת להערה. מוסיפים פרמטר ל-@Query. הופכים אותו לString שהוא שאילתת SQLite.
  • בוחרים את כל העמודות מתוך daily_sleep_quality_table
  • WHERE nightId תואם לארגומנט :key.

    שימו לב ל-:key. משתמשים בסימון הנקודתיים בשאילתה כדי להפנות לארגומנטים בפונקציה.
("SELECT * from daily_sleep_quality_table WHERE nightId = :key")
  1. מוסיפים עוד @Query עם פונקציית clear() ושאילתת SQLite כדי DELETE הכול מdaily_sleep_quality_table. השאילתה הזו לא מוחקת את הטבלה עצמה.

    ההערה @Delete מוחקת פריט אחד, ואפשר להשתמש ב-@Delete ולספק רשימה של לילות למחיקה. החיסרון הוא שצריך לאחזר את הטבלה או לדעת מה יש בה. ההערה @Delete מצוינת למחיקת רשומות ספציפיות, אבל היא לא יעילה למחיקת כל הרשומות מטבלה.
@Query("DELETE FROM daily_sleep_quality_table")
fun clear()
  1. הוספת @Query באמצעות פונקציית getTonight(). הופכים את הערך SleepNight שמוחזר על ידי getTonight() לערך שניתן להגדרה כ-null, כדי שהפונקציה תוכל לטפל במקרה שבו הטבלה ריקה. (הטבלה ריקה בהתחלה, ואחרי שהנתונים נמחקים).

    כדי לקבל את הערך 'הערב' ממסד הנתונים, צריך לכתוב שאילתת SQLite שמחזירה את הרכיב הראשון ברשימת התוצאות שממוינות לפי nightId בסדר יורד. משתמשים ב-LIMIT 1 כדי להחזיר רק רכיב אחד.
@Query("SELECT * FROM daily_sleep_quality_table ORDER BY nightId DESC LIMIT 1")
fun getTonight(): SleepNight?
  1. מוסיפים @Query עם פונקציית getAllNights():
  • השאילתה ב-SQLite צריכה להחזיר את כל העמודות מהטבלה daily_sleep_quality_table, בסדר יורד.
  • תבקש מ-getAllNights() להחזיר רשימה של ישויות SleepNight בתור LiveData. ‫Room דואג לעדכן את LiveData בשבילכם, כך שאתם צריכים לקבל את הנתונים רק פעם אחת.
  • יכול להיות שתצטרכו לייבא את LiveData מ-androidx.lifecycle.LiveData.
@Query("SELECT * FROM daily_sleep_quality_table ORDER BY nightId DESC")
fun getAllNights(): LiveData<List<SleepNight>>
  1. למרות שלא תראו שינויים גלויים, כדאי להריץ את האפליקציה כדי לוודא שאין בה שגיאות.

במשימה הזו תיצרו מסד נתונים Room שמשתמש ב-Entity וב-DAO שיצרתם במשימה הקודמת.

צריך ליצור מחלקה מופשטת של מאגר נתונים, עם ההערה @Database. למחלקת הנתונים הזו יש שיטה אחת שיוצרת מופע של מסד הנתונים אם הוא לא קיים, או מחזירה הפניה למסד נתונים קיים.

התהליך של יצירת מסד נתונים של Room הוא קצת מורכב, לכן הנה תיאור כללי של התהליך לפני שמתחילים לכתוב את הקוד:

  • יוצרים כיתת public abstract שextends RoomDatabase. המחלקה הזו משמשת כמאגר נתונים. הכיתה היא מופשטת, כי Room יוצרת את ההטמעה בשבילכם.
  • מוסיפים הערות לכיתה עם @Database. בארגומנטים, מצהירים על הישויות של מסד הנתונים ומגדירים את מספר הגרסה.
  • באובייקט companion, מגדירים שיטה או מאפיין מופשטים שמחזירים SleepDatabaseDao. ‫Room ייצור בשבילכם את גוף הטקסט.
  • צריך רק מופע אחד של מסד הנתונים Room לכל האפליקציה, ולכן צריך להגדיר את RoomDatabase כ-singleton.
  • משתמשים בכלי ליצירת מסד נתונים של Room כדי ליצור את מסד הנתונים רק אם הוא לא קיים. אחרת, מחזירים את מסד הנתונים הקיים.

שלב 1: יצירת מסד הנתונים

  1. בחבילה database, פותחים את SleepDatabase.kt.
  2. בקובץ, יוצרים מחלקה abstract בשם SleepDatabase שמרחיבה את RoomDatabase.

    מוסיפים למחלקה את ההערה @Database.
@Database()
abstract class SleepDatabase : RoomDatabase() {}
  1. תוצג שגיאה לגבי ישויות חסרות ופרמטרים של גרסה. ההערה @Database דורשת כמה ארגומנטים, כדי ש-Room יוכל לבנות את מסד הנתונים.
  • צריך לציין את SleepNight כפריט היחיד ברשימת entities.
  • מגדירים את version כ-1. בכל פעם שמשנים את הסכימה, צריך להגדיל את מספר הגרסה.
  • מגדירים את exportSchema ל-false כדי שלא יישמרו גיבויים של היסטוריית הגרסאות של הסכימה.
entities = [SleepNight::class], version = 1, exportSchema = false
  1. מסד הנתונים צריך לדעת על ה-DAO. בתוך גוף המחלקה, מגדירים ערך מופשט שמחזיר את SleepDatabaseDao. יכולים להיות לכם כמה ארגונים אוטונומיים מבוזרים.
abstract val sleepDatabaseDao: SleepDatabaseDao
  1. מתחת לזה, מגדירים אובייקט companion. אובייקט ה-companion מאפשר ללקוחות לגשת לשיטות ליצירה או לקבלת מסד הנתונים בלי ליצור מופע של המחלקה. מכיוון שהמטרה היחידה של המחלקה הזו היא לספק מסד נתונים, אין סיבה ליצור ממנה מופע.
 companion object {}
  1. בתוך אובייקט companion, מגדירים משתנה פרטי שניתן להקצאת ערך Null ‏INSTANCE למסד הנתונים ומאתחלים אותו ל-null. המשתנה INSTANCE ישמור הפניה למסד הנתונים, אחרי שייווצר מסד נתונים. כך אפשר להימנע מפתיחה חוזרת של חיבורים למסד הנתונים, פעולה יקרה.

הוספת הערות ל-INSTANCE באמצעות @Volatile. הערך של משתנה לא יציב אף פעם לא יישמר במטמון, וכל פעולות הכתיבה והקריאה יתבצעו בזיכרון הראשי וממנו. כך אפשר לוודא שהערך של INSTANCE תמיד עדכני וזהה לכל השרשורים של ההרצה. המשמעות היא ששינויים שבוצעו על ידי שרשור אחד ב-INSTANCE גלויים לכל השרשורים האחרים באופן מיידי, ולא נוצר מצב שבו, למשל, שני שרשורים מעדכנים כל אחד את אותו ישות במטמון, מה שיוצר בעיה.

@Volatile
private var INSTANCE: SleepDatabase? = null
  1. מתחת ל-INSTANCE, עדיין בתוך האובייקט companion, מגדירים שיטה getInstance() עם פרמטר Context שנדרש לבניית מסד הנתונים. החזרת סוג SleepDatabase. תוצג שגיאה כי הפונקציה getInstance() עדיין לא מחזירה ערך.
fun getInstance(context: Context): SleepDatabase {}
  1. בתוך getInstance(), מוסיפים בלוק synchronized{}. מעבירים את this כדי לקבל גישה להקשר.

    יכול להיות שכמה תהליכים יבקשו מופע של מסד נתונים בו-זמנית, וכתוצאה מכך ייווצרו שני מסדי נתונים במקום אחד. הבעיה הזו לא צפויה לקרות באפליקציית הדוגמה הזו, אבל היא יכולה לקרות באפליקציה מורכבת יותר. עטיפת הקוד כדי להעביר את מסד הנתונים אל synchronized מאפשרת רק לשרשור ביצוע אחד בכל פעם להיכנס לבלוק הקוד הזה, וכך מוודאים שמסד הנתונים מאותחל רק פעם אחת.
synchronized(this) {}
  1. בתוך הבלוק המסונכרן, מעתיקים את הערך הנוכחי של INSTANCE למשתנה מקומי instance. הסיבה לכך היא כדי לנצל את היתרונות של העברה חכמה, שזמינה רק למשתנים מקומיים.
var instance = INSTANCE
  1. בתוך הבלוק synchronized, return instance בסוף הבלוק synchronized. מתעלמים משגיאת אי ההתאמה של סוג ההחזרה, כי אחרי שמסיימים את הפעולה לא יוחזר ערך null.
return instance
  1. מעל ההצהרה return, מוסיפים הצהרה if כדי לבדוק אם instance הוא null, כלומר, אם עדיין אין מסד נתונים.
if (instance == null) {}
  1. אם הערך של instance הוא null, צריך להשתמש בכלי ליצירת מסדי נתונים כדי לקבל מסד נתונים. בגוף ההצהרה if, מפעילים את Room.databaseBuilder ומספקים את ההקשר שהועבר, את מחלקת מסד הנתונים ואת השם של מסד הנתונים, sleep_history_database. כדי להסיר את השגיאה, צריך להוסיף אסטרטגיית העברה וbuild() בשלבים הבאים.
instance = Room.databaseBuilder(
                           context.applicationContext,
                           SleepDatabase::class.java,
                           "sleep_history_database")
  1. מוסיפים לכלי הבנייה את אסטרטגיית ההעברה הנדרשת. משתמשים ב-.fallbackToDestructiveMigration().

    בדרך כלל, צריך לספק אובייקט העברה עם אסטרטגיית העברה למקרה של שינוי בסכימה. אובייקט העברה הוא אובייקט שמגדיר איך לוקחים את כל השורות עם הסכימה הישנה וממירים אותן לשורות בסכימה החדשה, כך שלא יאבדו נתונים. העברה היא מעבר להיקף של ה-Codelab הזה. פתרון פשוט הוא להרוס את מסד הנתונים ולבנות אותו מחדש, מה שאומר שהנתונים יאבדו.
.fallbackToDestructiveMigration()
  1. לבסוף, מתקשרים אל .build().
.build()
  1. מקצים את INSTANCE = instance כשלב האחרון בתוך ההצהרה if.
INSTANCE = instance
  1. הקוד הסופי אמור להיראות כך:
@Database(entities = [SleepNight::class], version = 1, exportSchema = false)
abstract class SleepDatabase : RoomDatabase() {

   abstract val sleepDatabaseDao: SleepDatabaseDao

   companion object {

       @Volatile
       private var INSTANCE: SleepDatabase? = null

       fun getInstance(context: Context): SleepDatabase {
           synchronized(this) {
               var instance = INSTANCE

               if (instance == null) {
                   instance = Room.databaseBuilder(
                           context.applicationContext,
                           SleepDatabase::class.java,
                           "sleep_history_database"
                   )
                           .fallbackToDestructiveMigration()
                           .build()
                   INSTANCE = instance
               }
               return instance
           }
       }
   }
}
  1. כותבים ומריצים את הקוד.

עכשיו יש לכם את כל אבני הבניין לעבודה עם מסד הנתונים Room. הקוד הזה עובר קומפילציה ופועל, אבל אין לכם דרך לדעת אם הוא באמת עובד. לכן, זה זמן טוב להוסיף כמה בדיקות בסיסיות.

שלב 2: בדיקת SleepDatabase

בשלב הזה מריצים את הבדיקות שסופקו כדי לוודא שהמסד נתונים פועל. כך תוכלו לוודא שהמסד פועל לפני שתבנו עליו. הבדיקות שסופקו הן בסיסיות. באפליקציה לייצור, תפעילו את כל הפונקציות והשאילתות בכל ה-DAO.

אפליקציית המתחילים מכילה תיקייה בשם androidTest. התיקייה androidTest מכילה בדיקות יחידות שכוללות מכשור של Android, כלומר הבדיקות צריכות את מסגרת Android, ולכן צריך להריץ את הבדיקות במכשיר פיזי או וירטואלי. כמובן שאפשר גם ליצור ולהריץ בדיקות יחידה (unit testing) טהורות שלא כוללות את מסגרת Android.

  1. ב-Android Studio, בתיקייה androidTest, פותחים את הקובץ SleepDatabaseTest.
  2. כדי לבטל את ההערה של הקוד, בוחרים את כל הקוד שהוספה לו הערה ולוחצים על מקש הקיצור Cmd+/ או Control+/.
  3. מעיינים בקובץ.

הנה סקירה מהירה של קוד הבדיקה, כי זה עוד קטע קוד שאפשר לעשות בו שימוש חוזר:

  • SleepDabaseTest היא כיתת ניסוי.
  • ההערה @RunWith מזהה את כלי ההרצה של הבדיקות, שהוא התוכנית שמגדירה ומבצעת את הבדיקות.
  • במהלך ההגדרה, הפונקציה שמסומנת ב-@Before מופעלת, והיא יוצרת SleepDatabase בזיכרון עם SleepDatabaseDao. 'בזיכרון' פירושו שמסד הנתונים הזה לא נשמר במערכת הקבצים ויימחק אחרי הרצת הבדיקות.
  • בנוסף, כשיוצרים את מסד הנתונים בזיכרון, הקוד קורא לשיטה ספציפית אחרת לבדיקה, allowMainThreadQueries. כברירת מחדל, אם מנסים להריץ שאילתות בשרשור הראשי, מוצגת שגיאה. השיטה הזו מאפשרת להריץ בדיקות בשרשור הראשי, ומומלץ לעשות זאת רק במהלך הבדיקות.
  • בשיטת בדיקה עם ההערה @Test, יוצרים, מוסיפים ומאחזרים SleepNight ומוודאים שהם זהים. אם משהו משתבש, צריך להפעיל חריגה. בבדיקה אמיתית, היו לכם כמה שיטות @Test .
  • כשהבדיקה מסתיימת, הפונקציה שמסומנת ב-@After מופעלת כדי לסגור את מסד הנתונים.
  1. לוחצים לחיצה ימנית על קובץ הבדיקה בחלונית Project ובוחרים באפשרות Run 'SleepDatabaseTest'.
  2. אחרי הרצת הבדיקות, מוודאים בחלונית SleepDatabaseTest שכל הבדיקות עברו.

מכיוון שכל הבדיקות עברו בהצלחה, אתם יודעים עכשיו כמה דברים:

  • מסד הנתונים נוצר בצורה תקינה.
  • אפשר להוסיף SleepNight למסד הנתונים.
  • אפשר לקבל בחזרה את SleepNight.
  • במאפיין SleepNight מופיע הערך הנכון של האיכות.

פרויקט Android Studio: ‏ TrackMySleepQualityRoomAndTesting

כשבודקים מסד נתונים, צריך להפעיל את כל השיטות שמוגדרות ב-DAO. כדי להשלים את הבדיקה, מוסיפים ומריצים בדיקות כדי להפעיל את שאר השיטות של DAO.

  • מגדירים את הטבלאות כסוגי נתונים שמסומנים בהערה @Entity. הגדרת מאפיינים עם ההערה @ColumnInfo כעמודות בטבלאות.
  • מגדירים אובייקט גישה לנתונים (DAO) כממשק עם ההערה @Dao. ה-DAO ממפה פונקציות של Kotlin לשאילתות במסד הנתונים.
  • משתמשים בהערות כדי להגדיר את הפונקציות @Insert,‏ @Delete ו-@Update.
  • משתמשים בהערה @Query עם מחרוזת שאילתה של SQLite כפרמטר לכל שאילתה אחרת.
  • יוצרים מחלקה מופשטת עם פונקציה getInstance() שמחזירה מסד נתונים.
  • משתמשים בבדיקות עם מכשור כדי לוודא שמסד הנתונים ו-DAO פועלים כמצופה. אפשר להשתמש בבדיקות שסופקו כתבנית.

קורס ב-Udacity:

מסמכי תיעוד למפתחי Android:

מסמכים ומאמרים נוספים:

בקטע הזה מפורטות אפשרויות למשימות ביתיות לתלמידים שעובדים על ה-Codelab הזה כחלק מקורס בהנחיית מדריך. המורה צריך:

  • אם צריך, מקצים שיעורי בית.
  • להסביר לתלמידים איך להגיש מטלות.
  • בודקים את שיעורי הבית.

אנשי ההוראה יכולים להשתמש בהצעות האלה כמה שרוצים, ומומלץ להם להקצות כל שיעורי בית אחרים שהם חושבים שמתאימים.

אם אתם עובדים על ה-codelab הזה לבד, אתם יכולים להשתמש במשימות האלה כדי לבדוק את הידע שלכם.

עונים על השאלות הבאות

שאלה 1

איך מציינים שמחלקה מייצגת ישות לאחסון במסד נתונים Room?

  • מרחיבים את הכיתה DatabaseEntity.
  • מוסיפים הערות לכיתה עם @Entity.
  • מוסיפים הערות לכיתה עם @Database.
  • הארכת השיעור ב-RoomEntity והוספת הערות לשיעור ב-@Room.

שאלה 2

ה-DAO (אובייקט גישה לנתונים) הוא ממשק ש-Room משתמש בו כדי למפות פונקציות של Kotlin לשאילתות במסד הנתונים.

איך מציינים שממשק מייצג DAO עבור מסד נתונים של Room?

  • הגדרת הממשק כך שיתרחב RoomDAO.
  • מגדירים את הממשק כך שירחיב את EntityDao, ואז מטמיעים את ה-method‏ DaoConnection().
  • הוספת הערות לממשק באמצעות @Dao.
  • הוספת הערות לממשק באמצעות @RoomConnection.

שאלה 3

אילו מההצהרות הבאות נכונות לגבי מסד הנתונים Room? צריך לבחור בכל האפשרויות הרלוונטיות.

  • אפשר להגדיר טבלאות למסד נתונים של Room כסוגי נתונים עם הערות.
  • אם מחזירים את LiveData משאילתה, Room ידאג לעדכן את LiveData אם הוא ישתנה.LiveData
  • לכל מסד נתונים Room צריך להיות DAO אחד בלבד.
  • כדי להגדיר מחלקה כRoom מסד נתונים, צריך להגדיר אותה כמחלקת משנה של RoomDatabase ולהוסיף לה את ההערה @Database.

שאלה 4

אילו מההערות הבאות אפשר להוסיף בממשק של @Dao? צריך לבחור בכל האפשרויות הרלוונטיות.

  • @Get
  • @Update
  • @Insert
  • @Query

שאלה 5

איך אפשר לוודא שהמסד נתונים פועל? יש לבחור בכל האפשרויות הרלוונטיות.

  • כתיבת בדיקות עם מכשור.
  • ממשיכים לכתוב ולהריץ את האפליקציה עד שהנתונים מוצגים.
  • מחליפים את הקריאות ל-methods בממשק DAO בקריאות ל-methods מקבילים במחלקה Entity.
  • מריצים את הפונקציה verifyDatabase() שסופקה על ידי הספרייה Room.

עוברים לשיעור הבא: 6.2 Coroutines and Room

קישורים ל-codelabs אחרים בקורס הזה מופיעים בדף הנחיתה של ה-codelabs בנושא יסודות Android Kotlin.