หลักพื้นฐานของ Android Kotlin 06.1: สร้างฐานข้อมูล Room

Codelab นี้เป็นส่วนหนึ่งของหลักสูตรหลักพื้นฐานของ Android Kotlin คุณจะได้รับประโยชน์สูงสุดจากหลักสูตรนี้หากทำตาม Codelab ตามลำดับ Codelab ของหลักสูตรทั้งหมดแสดงอยู่ในหน้า Landing Page ของ Codelab หลักพื้นฐานของ Android Kotlin

บทนำ

แอปส่วนใหญ่มีข้อมูลที่ต้องเก็บไว้แม้ว่าผู้ใช้จะปิดแอปแล้วก็ตาม เช่น แอปอาจจัดเก็บเพลย์ลิสต์ รายการไอเทมในเกม บันทึกค่าใช้จ่ายและรายได้ แคตตาล็อกกลุ่มดาว หรือข้อมูลการนอนหลับเมื่อเวลาผ่านไป โดยปกติแล้ว คุณจะใช้ฐานข้อมูลเพื่อจัดเก็บข้อมูลถาวร

Room เป็นไลบรารีฐานข้อมูลที่เป็นส่วนหนึ่งของ Jetpack ของ Android Room จะช่วยจัดการงานต่างๆ ในการตั้งค่าและกำหนดค่าฐานข้อมูล และช่วยให้แอปโต้ตอบกับฐานข้อมูลได้โดยใช้การเรียกฟังก์ชันธรรมดา Room เป็นเลเยอร์การแยกข้อมูลที่อยู่เหนือฐานข้อมูล SQLite Roomและไวยากรณ์การค้นหาสำหรับการค้นหาที่ซับซ้อนยิ่งขึ้นจะใช้รูปแบบ SQLite

รูปภาพด้านล่างแสดงให้เห็นว่าฐานข้อมูล Room เหมาะสมกับสถาปัตยกรรมโดยรวมที่แนะนำในหลักสูตรนี้อย่างไร

สิ่งที่คุณควรทราบอยู่แล้ว

คุณควรคุ้นเคยกับสิ่งต่อไปนี้

  • สร้างอินเทอร์เฟซผู้ใช้ (UI) พื้นฐานสำหรับแอป Android
  • การใช้กิจกรรม Fragment และ View
  • การไปยังส่วนย่อยต่างๆ และการใช้ Safe Args (ปลั๊กอิน Gradle) เพื่อส่งข้อมูลระหว่างส่วนย่อย
  • ดูโมเดล โรงงาน View-Model และ LiveData รวมถึงผู้สังเกตการณ์ หัวข้อเกี่ยวกับคอมโพเนนต์สถาปัตยกรรมเหล่านี้จะอยู่ใน Codelab ก่อนหน้านี้ในหลักสูตรนี้
  • ความเข้าใจพื้นฐานเกี่ยวกับฐานข้อมูล SQL และภาษา SQLite ดูภาพรวมหรือการทบทวนอย่างรวดเร็วได้ที่ข้อมูลเบื้องต้นเกี่ยวกับ SQLite

สิ่งที่คุณจะได้เรียนรู้

  • วิธีสร้างและโต้ตอบกับฐานข้อมูล Room เพื่อคงข้อมูลไว้
  • วิธีสร้างคลาสข้อมูลที่กำหนดตารางในฐานข้อมูล
  • วิธีใช้ Data Access Object (DAO) เพื่อแมปฟังก์ชัน Kotlin กับการค้นหา SQL
  • วิธีทดสอบว่าฐานข้อมูลทํางานหรือไม่

สิ่งที่คุณต้องดำเนินการ

  • สร้างฐานข้อมูล Room ที่มีอินเทอร์เฟซสำหรับข้อมูลการนอนหลับในแต่ละคืน
  • ทดสอบฐานข้อมูลโดยใช้การทดสอบที่ให้ไว้

ใน Codelab นี้ คุณจะได้สร้างส่วนฐานข้อมูลของแอปที่ติดตามคุณภาพการนอนหลับ แอปใช้ฐานข้อมูลเพื่อจัดเก็บข้อมูลการนอนหลับเมื่อเวลาผ่านไป

แอปมี 2 หน้าจอซึ่งแสดงด้วย Fragment ดังที่แสดงในรูปภาพด้านล่าง

หน้าจอแรกที่แสดงทางด้านซ้ายมีปุ่มสำหรับเริ่มและหยุดการติดตาม หน้าจอจะแสดงข้อมูลการนอนหลับทั้งหมดของผู้ใช้ ปุ่มล้างจะลบข้อมูลทั้งหมดที่แอปเก็บรวบรวมไว้สำหรับผู้ใช้ออกอย่างถาวร

หน้าจอที่ 2 ซึ่งแสดงทางด้านขวาใช้สำหรับเลือกคะแนนคุณภาพการนอนหลับ ในแอป การจัดประเภทจะแสดงเป็นตัวเลข แอปจะแสดงทั้งไอคอนใบหน้าและค่าเทียบเท่าที่เป็นตัวเลขเพื่อวัตถุประสงค์ในการพัฒนา

โฟลว์ของผู้ใช้มีดังนี้

  • ผู้ใช้เปิดแอปและเห็นหน้าจอการติดตามการนอนหลับ
  • ผู้ใช้แตะปุ่มเริ่ม ซึ่งจะบันทึกเวลาเริ่มต้นและแสดงเวลาดังกล่าว ปุ่มเริ่มจะปิดใช้ และปุ่มหยุดจะเปิดใช้
  • ผู้ใช้แตะปุ่มหยุด ซึ่งจะบันทึกเวลาสิ้นสุดและเปิดหน้าจอคุณภาพการนอนหลับ
  • ผู้ใช้เลือกไอคอนคุณภาพการนอนหลับ หน้าจอจะปิดลง และหน้าจอการติดตามจะแสดงเวลาสิ้นสุดการนอนหลับและคุณภาพการนอนหลับ ปุ่มหยุดจะปิดใช้และปุ่มเริ่มจะเปิดใช้ แอปพร้อมสำหรับคืนถัดไปแล้ว
  • ปุ่มล้างจะเปิดใช้เมื่อใดก็ตามที่มีข้อมูลในฐานข้อมูล เมื่อผู้ใช้แตะปุ่มล้าง ระบบจะลบข้อมูลทั้งหมดของผู้ใช้โดยไม่มีการกู้คืนใดๆ และไม่มีข้อความ "คุณแน่ใจไหม"

แอปนี้ใช้สถาปัตยกรรมที่เรียบง่าย ดังที่แสดงด้านล่างในบริบทของสถาปัตยกรรมแบบเต็ม แอปใช้เฉพาะคอมโพเนนต์ต่อไปนี้

  • ตัวควบคุม UI
  • ดูโมเดลและ LiveData
  • ฐานข้อมูล Room

ขั้นตอนที่ 1: ดาวน์โหลดและเรียกใช้แอปเริ่มต้น

  1. ดาวน์โหลดแอป TrackMySleepQuality-Starter จาก GitHub
  2. สร้างและเรียกใช้แอป แอปจะแสดง UI สำหรับ SleepTrackerFragment Fragment แต่ไม่มีข้อมูล ปุ่มไม่ตอบสนองต่อการแตะ

ขั้นตอนที่ 2: ตรวจสอบแอปเริ่มต้น

  1. ดูไฟล์ Gradle ดังนี้
  • ไฟล์ Gradle ของโปรเจ็กต์
    ในไฟล์ build.gradle ระดับโปรเจ็กต์ ให้สังเกตตัวแปรที่ระบุเวอร์ชันของไลบรารี เวอร์ชันที่ใช้ในแอปเริ่มต้นทำงานร่วมกันได้ดีและทำงานได้ดีกับแอปนี้ เมื่อคุณทำ Codelab นี้เสร็จแล้ว Android Studio อาจแจ้งให้คุณอัปเดตเวอร์ชันบางรายการ คุณเลือกได้ว่าจะอัปเดตหรือใช้เวอร์ชันที่มีอยู่ในแอปต่อไป หากพบข้อผิดพลาดในการคอมไพล์ที่ "แปลก" ให้ลองใช้ชุดค่าผสมของเวอร์ชันไลบรารีที่แอปโซลูชันสุดท้ายใช้
  • ไฟล์ Gradle ของโมดูล โปรดสังเกตทรัพยากร Dependency ที่ระบุไว้สำหรับไลบรารี Android Jetpack ทั้งหมด รวมถึง Room และทรัพยากร Dependency สำหรับโครูทีน
  1. ดูแพ็กเกจและ UI แอปมีโครงสร้างตามฟังก์ชันการทำงาน แพ็กเกจประกอบด้วยไฟล์ตัวยึดตำแหน่งที่คุณจะเพิ่มโค้ดตลอดทั้งชุดของ Codelab นี้
  • แพ็กเกจ database สำหรับโค้ดทั้งหมดที่เกี่ยวข้องกับฐานข้อมูล Room
  • แพ็กเกจ sleepquality และ sleeptracker มี Fragment, View Model และ View Model Factory สำหรับแต่ละหน้าจอ
  1. ดูUtil.ktไฟล์ซึ่งมีฟังก์ชันที่จะช่วยแสดงข้อมูลคุณภาพการนอนหลับ โค้ดบางส่วนถูกแสดงความคิดเห็นเนื่องจากอ้างอิงถึง ViewModel ที่คุณสร้างในภายหลัง
  2. ดูโฟลเดอร์ androidTest (SleepDatabaseTest.kt) คุณจะใช้การทดสอบนี้เพื่อยืนยันว่าฐานข้อมูลทำงานได้ตามที่ต้องการ

ใน Android ข้อมูลจะแสดงในคลาสข้อมูล และจะเข้าถึงและแก้ไขข้อมูลได้โดยใช้การเรียกฟังก์ชัน อย่างไรก็ตาม ในโลกของฐานข้อมูล คุณต้องมีเอนทิตีและคําค้นหา

  • เอนทิตีแสดงถึงออบเจ็กต์หรือแนวคิด รวมถึงพร็อพเพอร์ตี้ของออบเจ็กต์หรือแนวคิดนั้นๆ เพื่อจัดเก็บไว้ในฐานข้อมูล คลาสเอนทิตีจะกำหนดตาราง และแต่ละอินสแตนซ์ของคลาสนั้นจะแสดงแถวในตาราง แต่ละพร็อพเพอร์ตี้จะกำหนดคอลัมน์ ในแอปของคุณ เอนทิตีจะเก็บข้อมูลเกี่ยวกับการนอนหลับในคืนหนึ่งๆ
  • การค้นหาคือคำขอข้อมูลจากตารางฐานข้อมูลหรือชุดตาราง หรือคำขอให้ดำเนินการกับข้อมูล คำค้นหาที่พบบ่อยคือคำค้นหาสำหรับการรับ การแทรก และการอัปเดตเอนทิตี เช่น คุณสามารถค้นหาคืนที่นอนหลับทั้งหมดที่บันทึกไว้ โดยจัดเรียงตามเวลาเริ่มต้น

Room จะทำงานหนักทั้งหมดให้คุณเพื่อเปลี่ยนจากคลาสข้อมูล Kotlin เป็นเอนทิตีที่จัดเก็บไว้ในตาราง SQLite ได้ และเปลี่ยนจากประกาศฟังก์ชันเป็นการค้นหา SQL

คุณต้องกำหนดเอนทิตีแต่ละรายการเป็นคลาสข้อมูลที่อธิบายประกอบ และการโต้ตอบเป็นอินเทอร์เฟซที่อธิบายประกอบ ซึ่งก็คือออบเจ็กต์การเข้าถึงข้อมูล (DAO) Room ใช้คลาสที่มีคำอธิบายประกอบเหล่านี้เพื่อสร้างตารางในฐานข้อมูล และการค้นหาที่ทำงานในฐานข้อมูล

ขั้นตอนที่ 1: สร้างเอนทิตี SleepNight

ในงานนี้ คุณจะกำหนดการนอนหลับ 1 คืนเป็นคลาสข้อมูลที่อธิบายประกอบ

สำหรับการนอนหลับ 1 คืน คุณต้องบันทึกเวลาเริ่มต้น เวลาสิ้นสุด และการให้คะแนนคุณภาพ

และคุณต้องมีรหัสเพื่อระบุตัวตนของคืนนั้นๆ โดยไม่ซ้ำกัน

  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 เป็นคีย์หลัก ให้ใส่คำอธิบายประกอบพร็อพเพอร์ตี้ nightId ด้วย @PrimaryKey ตั้งค่าพารามิเตอร์ 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

สำหรับฐานข้อมูลเครื่องมือติดตามการนอนหลับของคืนที่นอนหลับ คุณต้องทำสิ่งต่อไปนี้ได้

  • แทรกคืนใหม่
  • อัปเดตการแข่งขันที่กำลังดำเนินการเพื่ออัปเดตเวลาสิ้นสุดและการจัดประเภทคุณภาพ
  • รับคืนที่เฉพาะเจาะจงตามคีย์
  • รับทุกคืนเพื่อให้คุณแสดงได้
  • ดูคืนล่าสุด
  • ลบรายการทั้งหมดในฐานข้อมูล

ขั้นตอนที่ 1: สร้าง SleepDatabase DAO

  1. เปิด SleepDatabaseDao.kt ในdatabase
  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 ที่อนุญาตให้เป็นค่าว่าง คุณจะเห็นข้อผิดพลาดสำหรับพารามิเตอร์ที่ขาดหายไป
@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 จะลบรายการ 1 รายการ และคุณสามารถใช้ @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. เปิด SleepDatabase.kt ในdatabase
  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 คุณมี DAO ได้หลายรายการ
abstract val sleepDatabaseDao: SleepDatabaseDao
  1. จากนั้นกำหนดcompanionออบเจ็กต์ ออบเจ็กต์คู่ช่วยให้ไคลเอ็นต์เข้าถึงเมธอดสำหรับการสร้างหรือรับฐานข้อมูลได้โดยไม่ต้องสร้างอินสแตนซ์ของคลาส เนื่องจากคลาสนี้มีจุดประสงค์เพียงอย่างเดียวคือการจัดเตรียมฐานข้อมูล จึงไม่มีเหตุผลที่จะต้องสร้างอินสแตนซ์ของคลาสนี้
 companion object {}
  1. ภายในออบเจ็กต์ companion ให้ประกาศตัวแปรส่วนตัวที่อนุญาตให้เป็น Null INSTANCE สำหรับฐานข้อมูลและเริ่มต้นเป็น null ตัวแปร INSTANCE จะเก็บข้อมูลอ้างอิงไปยังฐานข้อมูลเมื่อมีการสร้างฐานข้อมูล ซึ่งจะช่วยให้คุณไม่ต้องเปิดการเชื่อมต่อกับฐานข้อมูลซ้ำๆ ซึ่งมีค่าใช้จ่ายสูง

ใส่คำอธิบายประกอบ INSTANCE ด้วย @Volatile ระบบจะไม่แคชค่าของตัวแปรที่เปลี่ยนแปลงได้ และการเขียนและการอ่านทั้งหมดจะดำเนินการในหน่วยความจำหลัก ซึ่งจะช่วยให้มั่นใจได้ว่าค่าของ INSTANCE จะเป็นข้อมูลล่าสุดและเหมือนกันเสมอสำหรับทุกเธรดการดำเนินการ ซึ่งหมายความว่าการเปลี่ยนแปลงที่เธรดหนึ่งทำกับ INSTANCE จะปรากฏต่อเธรดอื่นๆ ทั้งหมดทันที และคุณจะไม่เจอกรณีที่เธรด 2 เธรดอัปเดตเอนทิตีเดียวกันในแคช ซึ่งจะทำให้เกิดปัญหา

@Volatile
private var INSTANCE: SleepDatabase? = null
  1. ที่ด้านล่าง INSTANCE ซึ่งยังอยู่ภายในออบเจ็กต์ companion ให้กำหนดgetInstance()เมธอดที่มีพารามิเตอร์ Context ที่เครื่องมือสร้างฐานข้อมูลจะต้องใช้ แสดงผลประเภท SleepDatabase คุณจะเห็นข้อผิดพลาดเนื่องจาก getInstance() ยังไม่แสดงผลอะไร
fun getInstance(context: Context): SleepDatabase {}
  1. เพิ่มบล็อก synchronized{} ใน getInstance() ส่ง this เพื่อให้คุณเข้าถึงบริบทได้

    ชุดข้อความหลายชุดอาจขออินสแตนซ์ฐานข้อมูลพร้อมกัน ซึ่งจะทำให้มีฐานข้อมูล 2 รายการแทนที่จะเป็น 1 รายการ ปัญหานี้ไม่น่าจะเกิดขึ้นในแอปตัวอย่างนี้ แต่ก็อาจเกิดขึ้นได้ในแอปที่ซับซ้อนกว่า การห่อโค้ดเพื่อให้ได้ฐานข้อมูลใน 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 ดังนั้นคุณจึงต้องเรียกใช้การทดสอบบนอุปกรณ์จริงหรืออุปกรณ์เสมือน แน่นอนว่าคุณยังสร้างและเรียกใช้การทดสอบหน่วยอย่างเดียวที่ไม่เกี่ยวข้องกับเฟรมเวิร์ก Android ได้ด้วย

  1. ใน Android Studio ให้เปิดไฟล์ SleepDatabaseTest ในโฟลเดอร์ androidTest
  2. หากต้องการยกเลิกการแสดงความคิดเห็นในโค้ด ให้เลือกโค้ดที่แสดงความคิดเห็นทั้งหมด แล้วกดแป้นพิมพ์ลัด Cmd+/ หรือ Control+/
  3. ดูไฟล์

ต่อไปนี้คือภาพรวมโดยย่อของโค้ดการทดสอบ เนื่องจากเป็นโค้ดอีกส่วนหนึ่งที่คุณนำกลับมาใช้ใหม่ได้

  • SleepDabaseTest เป็นชั้นเรียนทดสอบ
  • คำอธิบายประกอบ @RunWith ระบุตัวเรียกใช้การทดสอบ ซึ่งเป็นโปรแกรมที่ตั้งค่าและเรียกใช้การทดสอบ
  • ในระหว่างการตั้งค่า ฟังก์ชันที่อธิบายประกอบด้วย @Before จะได้รับการดำเนินการ และจะสร้าง SleepDatabase ในหน่วยความจำด้วย SleepDatabaseDao "ในหน่วยความจำ" หมายความว่าระบบจะไม่บันทึกฐานข้อมูลนี้ในระบบไฟล์ และจะลบฐานข้อมูลหลังจากที่เรียกใช้การทดสอบ
  • นอกจากนี้ เมื่อสร้างฐานข้อมูลในหน่วยความจำ โค้ดจะเรียกใช้เมธอดอื่นที่เฉพาะเจาะจงสำหรับการทดสอบ allowMainThreadQueries โดยค่าเริ่มต้น คุณจะได้รับข้อผิดพลาดหากพยายามเรียกใช้การค้นหาในเทรดหลัก วิธีนี้ช่วยให้คุณเรียกใช้การทดสอบในเทรดหลักได้ ซึ่งคุณควรทำเฉพาะในระหว่างการทดสอบเท่านั้น
  • ในเมธอดทดสอบที่มีคำอธิบายประกอบ @Test คุณจะสร้าง แทรก และเรียกข้อมูล SleepNight แล้วยืนยันว่าข้อมูลเหล่านั้นเหมือนกัน หากเกิดข้อผิดพลาด ให้ส่งข้อยกเว้น ในการทดสอบจริง คุณจะมี@Test หลายวิธี
  • เมื่อการทดสอบเสร็จสิ้น ฟังก์ชันที่อธิบายประกอบด้วย @After จะทํางานเพื่อปิดฐานข้อมูล
  1. คลิกขวาที่ไฟล์ทดสอบในบานหน้าต่างโปรเจ็กต์ แล้วเลือกเรียกใช้ "SleepDatabaseTest"
  2. หลังจากทดสอบแล้ว ให้ตรวจสอบในแผง SleepDatabaseTest ว่าการทดสอบทั้งหมดผ่านแล้ว

เนื่องจากการทดสอบทั้งหมดผ่าน คุณจึงทราบสิ่งต่างๆ ดังนี้

  • ระบบจะสร้างฐานข้อมูลอย่างถูกต้อง
  • คุณสามารถแทรก SleepNight ลงในฐานข้อมูลได้
  • คุณจะได้รับ SleepNight คืน
  • SleepNight มีค่าที่ถูกต้องสำหรับคุณภาพ

โปรเจ็กต์ Android Studio: TrackMySleepQualityRoomAndTesting

เมื่อทดสอบฐานข้อมูล คุณต้องเรียกใช้เมธอดทั้งหมดที่กำหนดไว้ใน DAO หากต้องการทำการทดสอบให้เสร็จสมบูรณ์ ให้เพิ่มและเรียกใช้การทดสอบเพื่อทดสอบเมธอด DAO อื่นๆ

  • กำหนดตารางเป็นคลาสข้อมูลที่มีคำอธิบายประกอบ @Entity กำหนดพร็อพเพอร์ตี้ที่มีคำอธิบายประกอบด้วย @ColumnInfo เป็นคอลัมน์ในตาราง
  • กำหนดออบเจ็กต์การเข้าถึงข้อมูล (DAO) เป็นอินเทอร์เฟซที่มีคำอธิบายประกอบ @Dao DAO จะแมปฟังก์ชัน Kotlin กับการค้นหาฐานข้อมูล
  • ใช้คำอธิบายประกอบเพื่อกำหนดฟังก์ชัน @Insert, @Delete และ @Update
  • ใช้@Queryคำอธิบายประกอบที่มีสตริงการค้นหา SQLite เป็นพารามิเตอร์สำหรับการค้นหาอื่นๆ
  • สร้างคลาส Abstract ที่มีฟังก์ชัน getInstance() ซึ่งแสดงผลฐานข้อมูล
  • ใช้การทดสอบที่วัดผลเพื่อทดสอบว่าฐานข้อมูลและ DAO ทำงานตามที่คาดไว้ คุณใช้การทดสอบที่ให้ไว้เป็นเทมเพลตได้

หลักสูตร Udacity:

เอกสารประกอบสำหรับนักพัฒนาแอป Android:

เอกสารและบทความอื่นๆ

ส่วนนี้แสดงรายการการบ้านที่เป็นไปได้สำหรับนักเรียน/นักศึกษาที่กำลังทำ Codelab นี้เป็นส่วนหนึ่งของหลักสูตรที่สอนโดยผู้สอน ผู้สอนมีหน้าที่ดำเนินการต่อไปนี้

  • มอบหมายการบ้านหากจำเป็น
  • สื่อสารกับนักเรียนเกี่ยวกับวิธีส่งงานที่ได้รับมอบหมาย
  • ให้คะแนนงานการบ้าน

ผู้สอนสามารถใช้คำแนะนำเหล่านี้ได้มากน้อยตามที่ต้องการ และควรมีอิสระในการมอบหมายการบ้านอื่นๆ ที่เห็นว่าเหมาะสม

หากคุณกำลังทำ Codelab นี้ด้วยตนเอง โปรดใช้แบบฝึกหัดเหล่านี้เพื่อทดสอบความรู้ของคุณ

ตอบคำถามต่อไปนี้

คำถามที่ 1

คุณจะระบุว่าคลาสแสดงถึงเอนทิตีที่จะจัดเก็บในRoomฐานข้อมูลได้อย่างไร

  • ขยายเวลาชั้นเรียน DatabaseEntity
  • ใส่คำอธิบายประกอบในชั้นเรียนด้วย @Entity
  • ใส่คำอธิบายประกอบในชั้นเรียนด้วย @Database
  • ขยายคลาส RoomEntity และใส่คำอธิบายประกอบในคลาสด้วย @Room

คำถามที่ 2

DAO (Data Access Object) คืออินเทอร์เฟซที่ Room ใช้เพื่อแมปฟังก์ชัน Kotlin กับการค้นหาฐานข้อมูล

คุณจะระบุว่าอินเทอร์เฟซแสดงถึง DAO สำหรับฐานข้อมูล Room ได้อย่างไร

  • ขยายอินเทอร์เฟซ RoomDAO
  • ทำให้อินเทอร์เฟซขยาย EntityDao จากนั้นใช้เมธอด DaoConnection()
  • ใส่คำอธิบายประกอบในอินเทอร์เฟซด้วย @Dao
  • ใส่คำอธิบายประกอบในอินเทอร์เฟซด้วย @RoomConnection

คำถามที่ 3

ข้อความใดต่อไปนี้เป็นจริงเกี่ยวกับRoomฐานข้อมูล เลือกได้มากกว่า 1 ข้อ

  • คุณกำหนดตารางสำหรับฐานข้อมูล Room เป็นคลาสข้อมูลที่อธิบายประกอบได้
  • หากคุณส่งคืน LiveData จากการค้นหา Room จะอัปเดต LiveData ให้คุณหาก LiveData มีการเปลี่ยนแปลง
  • ฐานข้อมูล Room แต่ละฐานข้อมูลต้องมี DAO เพียง 1 รายการเท่านั้น
  • หากต้องการระบุคลาสเป็นRoomฐานข้อมูล ให้สร้างเป็นคลาสย่อยของ RoomDatabase และใส่คำอธิบายประกอบด้วย @Database

คำถามที่ 4

คุณใช้คำอธิบายประกอบใดต่อไปนี้ในอินเทอร์เฟซ @Dao ได้ เลือกได้มากกว่า 1 ข้อ

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

คำถามที่ 5

คุณจะยืนยันได้อย่างไรว่าฐานข้อมูลทำงานได้ เลือกได้มากกว่า 1 ข้อ

  • เขียนการทดสอบแบบมีเครื่องมือ
  • เขียนและเรียกใช้แอปต่อไปจนกว่าจะแสดงข้อมูล
  • แทนที่การเรียกเมธอดในอินเทอร์เฟซ DAO ด้วยการเรียกเมธอดที่เทียบเท่าในคลาส Entity
  • เรียกใช้ฟังก์ชัน verifyDatabase() ที่ไลบรารี Room มีให้

เริ่มบทเรียนถัดไป: 6.2 Coroutine และ Room

ดูลิงก์ไปยัง Codelab อื่นๆ ในหลักสูตรนี้ได้ที่หน้า Landing Page ของ Codelab หลักพื้นฐานของ Android Kotlin