Android Kotlin Fundamentals 06.1: Tạo cơ sở dữ liệu của Phòng

Lớp học lập trình này nằm trong khóa học về Khái niệm cơ bản về Android Kotlin. Bạn sẽ nhận được nhiều giá trị nhất từ khóa học này nếu bạn làm việc qua các lớp học lập trình theo trình tự. Tất cả các lớp học lập trình trong khóa học đều có trên trang đích của các lớp học lập trình cơ bản về Android Kotlin.

Giới thiệu

Hầu hết các ứng dụng đều có dữ liệu cần được giữ lại, ngay cả sau khi người dùng đóng ứng dụng. Ví dụ: ứng dụng có thể lưu trữ danh sách phát, kho hàng vật phẩm, hồ sơ chi phí và thu nhập, danh mục chòm sao hoặc dữ liệu về giấc ngủ theo thời gian. Thông thường, bạn sẽ dùng cơ sở dữ liệu để lưu trữ dữ liệu ổn định.

Room là một thư viện cơ sở dữ liệu thuộc Jetpack của Android. Room xử lý nhiều việc nhà khi thiết lập và định cấu hình cơ sở dữ liệu, đồng thời giúp ứng dụng của bạn tương tác với cơ sở dữ liệu bằng những lệnh gọi hàm thông thường. Về cơ bản, Room là một lớp trừu tượng nằm ở đầu cơ sở dữ liệu SQLite. RoomThuật ngữ và cú pháp truy vấn cho những cụm từ tìm kiếm phức tạp hơn, hãy làm theo mô hình SQLite.

Hình ảnh dưới đây cho thấy cách cơ sở dữ liệu Room phù hợp với cấu trúc tổng thể được đề xuất trong khóa học này.

Kiến thức bạn cần có

Bạn cần thông thạo:

  • Xây dựng giao diện người dùng (UI) cơ bản cho ứng dụng Android
  • Sử dụng hoạt động, mảnh và chế độ xem.
  • Di chuyển giữa các mảnh và sử dụng Safe Args (trình bổ trợ Gradle) để chuyển dữ liệu giữa các mảnh.
  • Xem các mô hình, nhà máy sản xuất mô hình xem, LiveData và các đối tượng tiếp nhận dữ liệu. Các chủ đề về Thành phần cấu trúc này được đề cập trong một lớp học lập trình trước đây trong khóa học này.
  • Kiến thức cơ bản về cơ sở dữ liệu SQL và ngôn ngữ SQLite. Hãy xem SQLite Primer để xem tổng quan nhanh hoặc xem lại.

Kiến thức bạn sẽ học được

  • Cách tạo và tương tác với cơ sở dữ liệu của Room để duy trì dữ liệu.
  • Cách tạo lớp dữ liệu xác định một bảng trong cơ sở dữ liệu.
  • Cách sử dụng đối tượng truy cập dữ liệu (DAO) để ánh xạ các hàm Kotlin đến truy vấn SQL.
  • Cách kiểm tra xem cơ sở dữ liệu của bạn có đang hoạt động hay không.

Bạn sẽ thực hiện

  • Tạo cơ sở dữ liệu Room có giao diện cho dữ liệu về giấc ngủ vào mỗi đêm.
  • Kiểm tra cơ sở dữ liệu bằng cách sử dụng các quy trình kiểm tra được cung cấp.

Trong lớp học lập trình này, bạn xây dựng phần cơ sở dữ liệu của một ứng dụng theo dõi chất lượng giấc ngủ. Ứng dụng sẽ dùng cơ sở dữ liệu để lưu trữ dữ liệu về giấc ngủ.

Ứng dụng có hai màn hình, được biểu thị bằng các mảnh, như minh họa trong hình bên dưới.

Màn hình đầu tiên (hiển thị ở bên trái) có các nút để bắt đầu và dừng theo dõi. Màn hình hiển thị tất cả dữ liệu giấc ngủ của người dùng. Nút Xóa xóa vĩnh viễn tất cả dữ liệu mà ứng dụng đã thu thập cho người dùng.

Màn hình thứ hai hiển thị ở bên phải, cho biết mức chọn điểm xếp hạng chất lượng giấc ngủ. Trong ứng dụng, điểm xếp hạng được biểu thị bằng số. Để phát triển, ứng dụng sẽ hiển thị cả biểu tượng khuôn mặt và giá trị tương đương bằng số.

Luồng người dùng như sau:

  • Người dùng mở ứng dụng và hiển thị màn hình theo dõi giấc ngủ.
  • Người dùng nhấn vào nút Bắt đầu. Thao tác này sẽ ghi lại thời gian bắt đầu và hiển thị thời gian đó. Nút Bắt đầu đã tắt và nút Dừng.
  • Người dùng nhấn vào nút Dừng. Thao tác này sẽ ghi lại thời gian kết thúc và mở màn hình chất lượng giấc ngủ.
  • Người dùng chọn một biểu tượng chất lượng giấc ngủ. Màn hình sẽ đóng lại và màn hình theo dõi hiển thị thời gian ngủ và chất lượng giấc ngủ. Nút Dừng bị tắt và nút Bắt đầu đang bật. Ứng dụng đã sẵn sàng cho một đêm khác.
  • Nút Xóa được bật bất cứ khi nào có dữ liệu trong cơ sở dữ liệu. Khi người dùng nhấn vào nút Xóa, tất cả dữ liệu của họ sẽ bị xóa mà không cần truy cập lại – không có "Bạn có chắc chắn{5}quot; tin nhắn không.

Ứng dụng này dùng một cấu trúc đơn giản như trong bối cảnh của cấu trúc đầy đủ. Ứng dụng này chỉ sử dụng các thành phần sau:

  • Bộ điều khiển giao diện người dùng
  • Xem mô hình và LiveData
  • Cơ sở dữ liệu Phòng

Bước 1: Tải xuống và chạy ứng dụng dành cho người mới bắt đầu

  1. Tải ứng dụng TrackMySleepquality-Starter xuống từ GitHub.
  2. Tạo và chạy ứng dụng. Ứng dụng này hiển thị giao diện người dùng cho mảnh SleepTrackerFragment nhưng không hiển thị dữ liệu. Các nút không phản hồi khi nhấn.

Bước 2: Kiểm tra ứng dụng dành cho người mới bắt đầu

  1. Hãy xem tệp Gradle:
  • Tệp Gradle dự án
    Trong tệp build.gradle cấp dự án, hãy chú ý đến các biến chỉ định phiên bản thư viện. Các phiên bản được dùng trong ứng dụng dành cho người mới bắt đầu hoạt động hiệu quả với nhau và hoạt động tốt với ứng dụng này. Khi bạn hoàn tất lớp học lập trình này, Android Studio có thể nhắc bạn cập nhật một số phiên bản. Bạn có quyền quyết định xem bạn muốn cập nhật hay giữ lại các phiên bản trong ứng dụng. Nếu bạn gặp lỗi "strange" lỗi biên dịch, hãy thử sử dụng kết hợp các phiên bản thư viện mà ứng dụng giải pháp cuối cùng sử dụng.
  • Tệp Gradle mô-đun. Lưu ý đến các phần phụ thuộc được cung cấp cho mọi thư viện Android Jetpack, bao gồm cả Room, và các phần phụ thuộc cho coroutine.
  1. Hãy xem các gói và giao diện người dùng. Ứng dụng được sắp xếp theo chức năng. Gói này chứa các tệp giữ chỗ mà bạn sẽ thêm mã trong suốt các lớp học lập trình này.
  • Gói database, cho tất cả các mã liên quan đến cơ sở dữ liệu của Room.
  • Các gói sleepqualitysleeptracker chứa mảnh, mô hình xem và trạng thái ban đầu của mô hình chế độ xem cho mỗi màn hình.
  1. Hãy xem Util.kt tệp, có chứa hàm giúp hiển thị dữ liệu về chất lượng giấc ngủ. Một số mã được nhận xét do mã này tham chiếu đến một mô hình chế độ xem mà bạn tạo sau này.
  2. Hãy xem thư mục Kiểm tra android (SleepDatabaseTest.kt). Bạn sẽ dùng thử nghiệm này để xác minh rằng cơ sở dữ liệu hoạt động như dự kiến.

Trong Android, dữ liệu được trình bày trong lớp dữ liệu, đồng thời dữ liệu được truy cập và sửa đổi bằng các lệnh gọi hàm. Tuy nhiên, trong thế giới cơ sở dữ liệu, bạn cần có thực thểtruy vấn.

  • Thực thể đại diện cho một đối tượng hoặc khái niệm và các thuộc tính của đối tượng đó để lưu trữ trong cơ sở dữ liệu. Một lớp thực thể xác định bảng và mỗi thực thể của lớp đó đại diện cho một hàng trong bảng. Mỗi thuộc tính xác định một cột. Trong ứng dụng của bạn, đối tượng này sẽ lưu giữ thông tin về đêm ngủ.
  • Truy vấn là một yêu cầu về dữ liệu hoặc thông tin từ bảng cơ sở dữ liệu, hoặc kết hợp các bảng hoặc yêu cầu thực hiện một hành động đối với dữ liệu. Truy vấn phổ biến là lấy, chèn và cập nhật các thực thể. Ví dụ: bạn có thể truy vấn tất cả các đêm ngủ được ghi lại, được sắp xếp theo thời gian bắt đầu.

Room sẽ trợ giúp bạn trong việc chuyển các lớp dữ liệu Kotlin sang các thực thể có thể được lưu trữ trong bảng SQLite, và từ việc khai báo hàm cho đến các truy vấn SQL.

Bạn phải xác định mỗi thực thể là một lớp dữ liệu được chú thích và các hoạt động tương tác là một giao diện được chú thích, một đối tượng truy cập dữ liệu (DAO). Room sử dụng các lớp có chú thích này để tạo bảng trong cơ sở dữ liệu và truy vấn hoạt động trên cơ sở dữ liệu.

Bước 1: Tạo thực thể Sleepnight

Trong nhiệm vụ này, bạn xác định một đêm ngủ là lớp dữ liệu có chú thích.

Đối với giấc ngủ một đêm, bạn cần ghi lại thời gian bắt đầu, thời gian kết thúc và điểm xếp hạng chất lượng.

Và bạn cần một mã nhận dạng để xác định duy nhất đêm.

  1. Trong gói database, hãy tìm và mở tệp SleepNight.kt.
  2. Tạo lớp dữ liệu SleepNight có các thông số cho một mã nhận dạng, thời gian bắt đầu (tính bằng mili giây), thời gian kết thúc (tính bằng mili giây) và điểm xếp hạng chất lượng giấc ngủ bằng số.
  • Bạn phải khởi tạo sleepQuality, vì vậy, hãy đặt giá trị thành -1, cho biết rằng chưa thu thập dữ liệu chất lượng nào.
  • Bạn cũng phải khởi tạo thời gian kết thúc. Đặt thời gian bắt đầu để báo hiệu rằng chưa có thời gian kết thúc nào được ghi lại.
data class SleepNight(
       var nightId: Long = 0L,
       val startTimeMilli: Long = System.currentTimeMillis(),
       var endTimeMilli: Long = startTimeMilli,
       var sleepQuality: Int = -1
)
  1. Trước khi khai báo lớp, hãy chú thích lớp dữ liệu bằng @Entity. Đặt tên cho bảng là daily_sleep_quality_table. Đối số cho tableName là không bắt buộc, nhưng nên dùng. Bạn có thể tra cứu các đối số khác trong tài liệu.

    Nếu được nhắc, hãy nhập Entity và tất cả chú thích khác từ thư viện androidx.
@Entity(tableName = "daily_sleep_quality_table")
data class SleepNight(...)
  1. Để khai báo nightId làm khoá chính, hãy chú giải thuộc tính nightId bằng @PrimaryKey. Hãy thiết lập tham số autoGenerate thành true để Room tạo giá trị nhận dạng cho mỗi thực thể. Điều này đảm bảo rằng mã nhận dạng cho mỗi đêm là duy nhất.
@PrimaryKey(autoGenerate = true)
var nightId: Long = 0L,...
  1. Chú giải các thuộc tính còn lại bằng @ColumnInfo. Tuỳ chỉnh tên thuộc tính bằng cách sử dụng các tham số như dưới đây.
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. Tạo và chạy mã của bạn để đảm bảo mã không có lỗi.

Trong nhiệm vụ này, bạn xác định một đối tượng truy cập dữ liệu (DAO). Trên Android, DAO cung cấp các phương thức thuận tiện để chèn, xóa và cập nhật cơ sở dữ liệu.

Khi sử dụng cơ sở dữ liệu của Room, bạn truy vấn cơ sở dữ liệu bằng cách xác định và gọi các hàm Kotlin trong mã của mình. Các hàm Kotlin này liên kết với các truy vấn SQL. Bạn xác định các mối liên kết này trong DAO bằng chú thích và Room tạo mã cần thiết.

Hãy xem DAO là một giao diện tùy chỉnh để truy cập vào cơ sở dữ liệu của bạn.

Đối với các thao tác cơ sở dữ liệu phổ biến, thư viện Room cung cấp các chú giải thuận tiện như @Insert, @Delete@Update. Chú giải @Query dành cho các thao tác khác. Bạn có thể viết bất kỳ truy vấn nào được SQLite hỗ trợ.

Ngoài ra, khi bạn tạo truy vấn trong Android Studio, trình biên dịch sẽ kiểm tra các truy vấn SQL của bạn để tìm lỗi cú pháp.

Đối với cơ sở dữ liệu trình theo dõi giấc ngủ về đêm, bạn cần có thể làm như sau:

  • Chèn đêm mới.
  • Cập nhật đêm hiện có để cập nhật thời gian kết thúc và điểm xếp hạng chất lượng.
  • Nhận một đêm cụ thể dựa trên khóa của phòng.
  • Xem tất cả các đêm để có thể giới thiệu các chương trình này.
  • Xem đêm gần đây nhất.
  • Xoá tất cả các mục trong cơ sở dữ liệu.

Bước 1: Tạo DAIDatabase DAO

  1. Trong gói database, hãy mở SleepDatabaseDao.kt.
  2. Lưu ý rằng interface SleepDatabaseDao được chú thích bằng @Dao. Bạn cần chú thích tất cả DAO bằng từ khóa @Dao.
@Dao
interface SleepDatabaseDao {}
  1. Bên trong phần nội dung của giao diện, hãy thêm chú giải @Insert. Bên dưới @Insert, hãy thêm một hàm insert() lấy một thực thể của lớp Entity SleepNight làm đối số của lớp đó.

    Chính xác. Room sẽ tạo tất cả mã cần thiết để chèn SleepNight vào cơ sở dữ liệu. Khi bạn gọi insert() từ mã Kotlin, Room sẽ thực thi một truy vấn SQL để chèn thực thể đó vào cơ sở dữ liệu. (Lưu ý: Bạn có thể gọi hàm bất kỳ thứ gì bạn muốn.)
@Insert
fun insert(night: SleepNight)
  1. Thêm chú giải @Update có hàm update() cho một SleepNight. Thực thể được cập nhật là thực thể có cùng khóa với thực thể đã chuyển vào. Bạn có thể cập nhật một số hoặc tất cả thuộc tính khác của thực thể.
@Update
fun update(night: SleepNight)

Không có chú giải tiện lợi cho chức năng còn lại. Vì vậy, bạn phải sử dụng chú giải @Query và cung cấp các truy vấn SQLite.

  1. Thêm chú thích @Query bằng hàm get() lấy đối số Long key và trả về SleepNight có thể có giá trị null. Bạn sẽ thấy lỗi thông số bị thiếu.
@Query
fun get(key: Long): SleepNight?
  1. Truy vấn được cung cấp dưới dạng một tham số chuỗi cho chú thích. Thêm một tham số vào @Query. Đặt thành String thành một truy vấn SQLite.
  • Chọn tất cả các cột qua daily_sleep_quality_table
  • WHERE nightId khớp với đối số: key.

    Hãy chú ý đến :key. Bạn sử dụng ký hiệu dấu hai chấm trong truy vấn để tham chiếu các đối số trong hàm.
("SELECT * from daily_sleep_quality_table WHERE nightId = :key")
  1. Thêm một @Query khác có hàm clear() và truy vấn SQLite vào DELETE mọi thứ từ daily_sleep_quality_table. Truy vấn này không xóa bảng.

    Chú thích @Delete xóa một mục và bạn có thể sử dụng @Delete cũng như cung cấp danh sách các đêm cần xóa. Hạn chế của việc này là bạn cần phải tìm nạp hoặc biết rõ những gì trong bảng. Chú thích @Delete rất phù hợp để xóa các mục cụ thể nhưng không hiệu quả để xóa tất cả các mục khỏi bảng.
@Query("DELETE FROM daily_sleep_quality_table")
fun clear()
  1. Thêm một @Query bằng hàm getTonight(). Đặt SleepNight do getTonight() trả về có thể có giá trị null để hàm có thể xử lý trường hợp bảng trống. (Bảng trống ở đầu và sau khi dữ liệu bị xóa.)

    Để lấy "tonight" từ cơ sở dữ liệu, hãy viết truy vấn SQLite trả về phần tử đầu tiên của danh sách kết quả được sắp xếp theo nightId theo thứ tự giảm dần. Hãy dùng LIMIT 1 để chỉ trả về một phần tử.
@Query("SELECT * FROM daily_sleep_quality_table ORDER BY nightId DESC LIMIT 1")
fun getTonight(): SleepNight?
  1. Thêm @Query có hàm getAllNights():
  • Yêu cầu truy vấn SQLite trả về mọi cột từ daily_sleep_quality_table, theo thứ tự giảm dần.
  • Yêu cầu getAllNights() trả về danh sách các thực thể SleepNight dưới dạng LiveData. Room giúp LiveData này luôn cập nhật, nghĩa là bạn chỉ cần xác định rõ ràng dữ liệu một lần.
  • Có thể bạn cần nhập LiveData từ androidx.lifecycle.LiveData.
@Query("SELECT * FROM daily_sleep_quality_table ORDER BY nightId DESC")
fun getAllNights(): LiveData<List<SleepNight>>
  1. Dù bạn sẽ không nhận thấy thay đổi nào, nhưng hãy chạy ứng dụng của bạn để đảm bảo ứng dụng không có lỗi.

Trong việc cần làm này, bạn tạo một cơ sở dữ liệu Room sử dụng Entity và DAO mà bạn đã tạo trong việc cần làm trước đó.

Bạn cần tạo một lớp lưu giữ cơ sở dữ liệu trừu tượng, được chú thích bằng @Database. Lớp này có một phương thức tạo thực thể của cơ sở dữ liệu nếu cơ sở dữ liệu không tồn tại hoặc trả về thông tin tham chiếu đến cơ sở dữ liệu hiện có.

Việc thu thập cơ sở dữ liệu Room khá phức tạp, vì vậy, bạn nên thực hiện quy trình chung này trước khi bắt đầu sử dụng mã:

  • Tạo một lớp public abstract để extends RoomDatabase. Lớp này đóng vai trò là chủ sở hữu cơ sở dữ liệu. Lớp này là trừu tượng vì Room tạo ra cách triển khai cho bạn.
  • Chú giải lớp này bằng @Database. Trong các đối số, hãy khai báo thực thể cho cơ sở dữ liệu và đặt số phiên bản.
  • Bên trong một đối tượng companion, hãy xác định một phương thức hoặc thuộc tính trừu tượng trả về SleepDatabaseDao. Room sẽ tạo nội dung cho bạn.
  • Bạn chỉ cần một phiên bản của cơ sở dữ liệu Room cho toàn bộ ứng dụng, do đó, hãy đặt RoomDatabase thành một singleton.
  • Dùng trình tạo cơ sở dữ liệu của Room để tạo cơ sở dữ liệu chỉ khi cơ sở dữ liệu không tồn tại. Nếu không, hãy trả về cơ sở dữ liệu hiện có.

Bước 1: Tạo cơ sở dữ liệu

  1. Trong gói database, hãy mở SleepDatabase.kt.
  2. Trong tệp, hãy tạo một lớp abstract có tên là SleepDatabase, mở rộng đó là RoomDatabase.

    Hãy chú thích lớp đó bằng @Database.
@Database()
abstract class SleepDatabase : RoomDatabase() {}
  1. Bạn sẽ thấy lỗi một số thực thể và thông số phiên bản bị thiếu. Chú giải @Database yêu cầu một số đối số để Room có thể xây dựng cơ sở dữ liệu.
  • Hãy cung cấp SleepNight làm mục duy nhất có danh sách entities.
  • Đặt version làm 1. Bất cứ khi nào thay đổi giản đồ, bạn sẽ phải tăng số phiên bản.
  • Thiết lập exportSchema thành false để không giữ lại các bản sao lưu nhật ký phiên bản giản đồ.
entities = [SleepNight::class], version = 1, exportSchema = false
  1. Cơ sở dữ liệu cần biết về DAO. Bên trong phần nội dung của lớp, hãy khai báo một giá trị trừu tượng trả về SleepDatabaseDao. Bạn có thể có nhiều DAO.
abstract val sleepDatabaseDao: SleepDatabaseDao
  1. Ở bên dưới, hãy xác định một đối tượng companion. Đối tượng companion cho phép khách hàng truy cập vào các phương thức để tạo hoặc lấy cơ sở dữ liệu mà không cần tạo thực thể lớp. Vì mục đích duy nhất của lớp này là cung cấp cơ sở dữ liệu, nên không có lý do gì để tạo thực thể của cơ sở dữ liệu này.
 companion object {}
  1. Bên trong đối tượng companion, hãy khai báo INSTANCE (biến riêng tư có thể mang giá trị rỗng) cho cơ sở dữ liệu và khởi tạo biến đó đến null. Biến INSTANCE sẽ lưu giữ thông tin tham chiếu đến cơ sở dữ liệu sau khi thông tin được tạo. Điều này giúp bạn tránh được việc liên tục mở kết nối tới cơ sở dữ liệu. Việc này sẽ tốn kém.

Chú giải INSTANCE bằng @Volatile. Giá trị của biến volatile sẽ không bao giờ được lưu vào bộ nhớ đệm và tất cả lượt ghi và đọc sẽ xuất phát và trở về từ bộ nhớ chính. Điều này giúp đảm bảo giá trị của INSTANCE luôn được cập nhật và giống nhau đối với tất cả luồng thực thi. Điều này có nghĩa là những thay đổi do một luồng đối với INSTANCE hiển thị ngay lập tức với tất cả các luồng khác, và bạn sẽ không gặp phải một tình huống nào đó, giả sử hai luồng cập nhật cùng một thực thể trong bộ nhớ đệm, điều này sẽ gây ra vấn đề.

@Volatile
private var INSTANCE: SleepDatabase? = null
  1. Bên dưới INSTANCE, vẫn nằm trong đối tượng companion, hãy xác định một phương thức getInstance() có thông số Context mà trình tạo cơ sở dữ liệu sẽ cần. Trả về một loại SleepDatabase. Bạn sẽ thấy lỗi vì getInstance() chưa trả về giá trị nào.
fun getInstance(context: Context): SleepDatabase {}
  1. Bên trong getInstance(), hãy thêm một khối synchronized{}. Chuyển vào this để có thể truy cập vào ngữ cảnh.

    Nhiều chuỗi có khả năng yêu cầu một thực thể của cơ sở dữ liệu cùng lúc, dẫn đến hai cơ sở dữ liệu thay vì một. Sự cố này không có khả năng xảy ra trong ứng dụng mẫu này nhưng có thể xảy ra đối với ứng dụng phức tạp hơn. Nếu bạn gói mã để đưa cơ sở dữ liệu vào synchronized, thì tức là chỉ một luồng thực thi mỗi lúc có thể nhập khối mã này, điều này giúp đảm bảo cơ sở dữ liệu chỉ được khởi tạo một lần.
synchronized(this) {}
  1. Bên trong khối đã đồng bộ hóa, hãy sao chép giá trị hiện tại của INSTANCE vào biến cục bộ instance. Điều này là để tận dụng tính năng truyền thông minh, vốn chỉ có sẵn cho các biến cục bộ.
var instance = INSTANCE
  1. Bên trong khối synchronized, return instance ở cuối khối synchronized. Bỏ qua lỗi về loại trả lại không khớp; bạn sẽ không bao giờ trả lại null sau khi hoàn tất.
return instance
  1. Phía trên câu lệnh return, hãy thêm một câu lệnh if để kiểm tra xem instance có rỗng hay không, tức là chưa có cơ sở dữ liệu.
if (instance == null) {}
  1. Nếu instancenull, hãy dùng trình tạo cơ sở dữ liệu để lấy cơ sở dữ liệu. Trong phần nội dung của câu lệnh if, hãy gọi Room.databaseBuilder và cung cấp ngữ cảnh mà bạn đã chuyển vào, lớp cơ sở dữ liệu và tên cho cơ sở dữ liệu, sleep_history_database. Để xóa lỗi này, bạn phải thêm một chiến lược di chuyển và build() trong các bước sau.
instance = Room.databaseBuilder(
                           context.applicationContext,
                           SleepDatabase::class.java,
                           "sleep_history_database")
  1. Thêm chiến lược di chuyển cần có vào hàm tạo. Dùng .fallbackToDestructiveMigration().

    Thông thường, bạn sẽ phải cung cấp đối tượng di chuyển cho một chiến lược di chuyển khi giản đồ này thay đổi. Đối tượng di chuyển là một đối tượng xác định cách bạn lấy tất cả các hàng có giản đồ cũ và chuyển đổi chúng thành các hàng trong giản đồ mới để không bị mất dữ liệu. Di chuyển nằm ngoài phạm vi của lớp học lập trình này. Giải pháp đơn giản là huỷ bỏ và tạo lại cơ sở dữ liệu, tức là dữ liệu sẽ bị mất.
.fallbackToDestructiveMigration()
  1. Cuối cùng, hãy gọi .build().
.build()
  1. Chỉ định INSTANCE = instance là bước cuối cùng trong câu lệnh if.
INSTANCE = instance
  1. Mã hoàn thiện của bạn sẽ có dạng như sau:
@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. Tạo và chạy mã.

Bây giờ, bạn đã có tất cả các khối xây dựng để làm việc với cơ sở dữ liệu Room. Mã này sẽ biên dịch và chạy, nhưng bạn không có cách nào để biết mã có thực sự hoạt động hay không. Vì vậy, đây là thời điểm thích hợp để thêm một số thử nghiệm cơ bản.

Bước 2: Kiểm tra SleepDatabase

Trong bước này, bạn chạy các thử nghiệm được cung cấp để xác minh rằng cơ sở dữ liệu của bạn có hoạt động. Điều này giúp đảm bảo rằng cơ sở dữ liệu hoạt động trước khi bạn tạo. Các thử nghiệm được cung cấp là cơ bản. Đối với ứng dụng chính thức, bạn sẽ thực hiện tất cả các hàm và truy vấn trong tất cả DAO.

Ứng dụng khởi động chứa thư mục androidTest. Thư mục androidTest này chứa các thử nghiệm đơn vị liên quan đến khả năng đo lường Android. Đây là một cách thú vị để nói rằng các thử nghiệm này cần có khung Android, vì vậy, bạn cần chạy các thử nghiệm trên thiết bị thực hoặc ảo. Tất nhiên, bạn cũng có thể tạo và chạy các thử nghiệm đơn vị thuần túy không liên quan đến khung Android.

  1. Trong Android Studio, trong thư mục androidTest, hãy mở tệp SleepDatabaseTest.
  2. Để hủy nhận xét mã, hãy chọn tất cả mã nhận xét và nhấn phím tắt Cmd+/ hoặc Control+/.
  3. Hãy xem tệp.

Dưới đây là thông tin nhanh về mã thử nghiệm, bởi vì mã này là một đoạn mã khác mà bạn có thể sử dụng lại:

  • SleepDabaseTest là lớp học thử nghiệm.
  • Chú thích @RunWith sẽ xác định người chạy thử nghiệm, là chương trình được thiết lập và thực thi các thử nghiệm.
  • Trong quá trình thiết lập, hàm được chú thích bằng @Before sẽ được thực thi và tạo một SleepDatabase trong bộ nhớ với SleepDatabaseDao. "In-bộ nhớ" có nghĩa là cơ sở dữ liệu này không được lưu trên hệ thống tệp và sẽ bị xoá sau khi thử nghiệm chạy.
  • Ngoài ra, khi xây dựng cơ sở dữ liệu trong bộ nhớ, mã gọi một phương thức kiểm tra khác, allowMainThreadQueries. Theo mặc định, bạn sẽ gặp lỗi nếu cố gắng chạy truy vấn trên chuỗi chính. Phương pháp này cho phép bạn chạy thử nghiệm trên luồng chính. Bạn chỉ nên chạy thử nghiệm này trong quá trình thử nghiệm.
  • Trong phương thức thử nghiệm được chú thích bằng @Test, bạn tạo, chèn và truy xuất SleepNight, đồng thời xác nhận rằng các thuộc tính này giống nhau. Nếu có lỗi xảy ra, hãy gửi một ngoại lệ. Trong một thử nghiệm thực tế, bạn sẽ có nhiều phương thức @Test .
  • Khi kiểm tra xong, hàm được chú thích bằng @After sẽ thực thi để đóng cơ sở dữ liệu.
  1. Nhấp chuột phải vào tệp thử nghiệm trong ngăn Project rồi chọn Run \39;SleepDatabaseTest#39;.
  2. Sau khi thử nghiệm chạy xong, hãy xác minh trong ngăn SleepDatabaseTest rằng tất cả các thử nghiệm đã vượt qua.

Vì tất cả các thử nghiệm đã vượt qua, giờ đây bạn biết một số điều:

  • Cơ sở dữ liệu được tạo chính xác.
  • Bạn có thể chèn một SleepNight vào cơ sở dữ liệu.
  • Bạn có thể khôi phục SleepNight.
  • SleepNight có giá trị chính xác cho chất lượng.

Dự án Android Studio: TrackMySleepqualityRoomAndTest

Khi thử nghiệm cơ sở dữ liệu, bạn cần thực hiện tất cả các phương thức được xác định trong DAO. Để hoàn tất thử nghiệm, hãy thêm và thực thi thử nghiệm để thực hiện các phương thức DAO khác.

  • Khai báo các bảng dưới dạng lớp dữ liệu được chú giải bằng @Entity. Khai báo các thuộc tính được chú giải bằng @ColumnInfo dưới dạng cột trong bảng.
  • Khai báo một đối tượng truy cập dữ liệu (DAO) làm giao diện, kèm theo chú giải bằng @Dao. DAO ánh xạ các hàm Kotlin đến các truy vấn cơ sở dữ liệu.
  • Sử dụng chú giải để xác định các hàm @Insert, @Delete@Update.
  • Sử dụng chú giải @Query có chuỗi truy vấn SQLite làm tham số cho mọi truy vấn khác.
  • Tạo một lớp trừu tượng có hàm getInstance() trả về cơ sở dữ liệu.
  • Sử dụng các thử nghiệm đo lường để kiểm tra xem cơ sở dữ liệu và DAO của bạn có đang hoạt động như mong đợi hay không. Bạn có thể sử dụng các thử nghiệm được cung cấp dưới dạng mẫu.

Khóa học từ Udacity:

Tài liệu dành cho nhà phát triển Android:

Tài liệu và bài viết khác:

Phần này liệt kê các bài tập về nhà có thể được giao cho học viên đang làm việc qua lớp học lập trình này trong khóa học do người hướng dẫn tổ chức. Người hướng dẫn có thể làm những việc sau:

  • Giao bài tập về nhà nếu được yêu cầu.
  • Trao đổi với học viên cách nộp bài tập về nhà.
  • Chấm điểm bài tập về nhà.

Người hướng dẫn có thể sử dụng những đề xuất này ít hay nhiều tùy ý. Do đó, họ có thể thoải mái giao bất kỳ bài tập về nhà nào khác mà họ cảm thấy phù hợp.

Nếu bạn đang tự mình làm việc qua lớp học lập trình này, hãy thoải mái sử dụng các bài tập về nhà này để kiểm tra kiến thức của bạn.

Trả lời những câu hỏi này

Câu hỏi 1

Làm cách nào để cho biết rằng một lớp đại diện cho một thực thể lưu trữ trong cơ sở dữ liệu của Room?

  • Mở rộng DatabaseEntity trong lớp.
  • Chú giải lớp này bằng @Entity.
  • Chú giải lớp này bằng @Database.
  • Mở rộng RoomEntity của lớp này rồi chú giải lớp này bằng @Room.

Câu hỏi 2

DAO (đối tượng truy cập dữ liệu) là giao diện mà Room sử dụng để ánh xạ các hàm Kotlin với các truy vấn cơ sở dữ liệu.

Làm cách nào để cho biết rằng một giao diện đại diện cho DAO cho cơ sở dữ liệu? Room

  • Mở rộng RoomDAO trong giao diện.
  • Mở rộng EntityDao trong giao diện, sau đó triển khai phương thức DaoConnection().
  • Chú giải giao diện này bằng @Dao.
  • Chú giải giao diện này bằng @RoomConnection.

Câu hỏi 3

Câu nào sau đây về cơ sở dữ liệu Room là chính xác? Hãy chọn tất cả phương án phù hợp.

  • Bạn có thể xác định bảng cho cơ sở dữ liệu Room dưới dạng lớp dữ liệu có chú thích.
  • Nếu bạn trả về LiveData từ một truy vấn, thì Room sẽ cập nhật LiveData cho bạn nếu LiveData thay đổi.
  • Mỗi cơ sở dữ liệu Room phải có một và chỉ có một DAO.
  • Để xác định một lớp là cơ sở dữ liệu của Room, hãy đặt lớp đó là lớp con của RoomDatabase và chú thích lớp đó bằng @Database.

Câu hỏi 4

Bạn có thể sử dụng chú giải nào sau đây trong giao diện @Dao? Hãy chọn tất cả phương án phù hợp.

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

Câu hỏi 5

Làm thế nào để xác minh rằng cơ sở dữ liệu của bạn đang hoạt động? Hãy chọn mọi câu phù hợp.

  • Viết các thử nghiệm đo lường.
  • Tiếp tục viết và chạy ứng dụng cho đến khi ứng dụng hiển thị dữ liệu.
  • Thay thế các lệnh gọi đến phương thức trong giao diện DAO bằng cách gọi các phương thức tương đương trong lớp Entity.
  • Chạy hàm verifyDatabase() do thư viện Room cung cấp.

Bắt đầu bài học tiếp theo: 6.2 Coroutine và Phòng

Để biết đường liên kết đến các lớp học lập trình khác trong khóa học này, hãy xem trang đích của các lớp học lập trình cơ bản về Android Kotlin.