Android Kotlin 基础知识:创建 Room 数据库

使用集合让一切井井有条 根据您的偏好保存内容并对其进行分类。

此 Codelab 是“Android Kotlin 基础知识”课程的一部分。如果您按顺序学习这些 Codelab,您将会充分发掘此课程的价值。“Android Kotlin 基础知识”Codelab 着陆页列出了所有课程 Codelab。

简介

大多数应用都有需要保存的数据,即使在用户关闭应用后仍要继续留存数据。例如,应用可能会存储歌曲播放列表、游戏道具目录、支出和收入记录、星座目录或个人睡眠数据历史记录。在大多数此类情况下,您都会使用数据库来存储这种持久性数据。

Room 是一个数据库,是 Android Jetpack 的一部分。在后台,Room librarySQLite 数据库之上的一个抽象层。SQLite 使用一种专门的语言 (SQL) 来执行数据库操作。Room 并不直接使用 SQLite,而是负责简化数据库设置和配置以及与数据库交互方面的琐碎工作。此外,它还让应用可以使用普通的函数调用与数据库进行交互。Room 还具有查询语法,可用于搜索数据。

下图展示了 Room 数据库如何融入本课程中推荐的总体架构。

f6a3960a0d1a7912.png

您应当已掌握的内容

您应熟悉以下内容:

  • 为 Android 应用构建基本界面
  • 使用 activity、fragment 和视图。
  • 在 fragment 之间导航,并使用 Safe Args(一个 Gradle 插件)在 fragment 之间传递数据。
  • 视图模型、视图模型工厂、LiveData 及其观察者。此课程中的其他 Codelab 介绍了这些架构组件主题。
  • 并发方面的基础知识。如需简要了解,请参阅“多线程和回调入门指南”。
  • SQL 数据库和 SQLite 语言方面的基础知识。如需大致了解或回顾,请参阅 SQLite 入门指南

学习内容

  • 如何创建 Room 数据库并与之交互以保留数据。
  • 如何创建用于在数据库中定义表的数据类。
  • 如何使用数据访问对象 (DAO) 将 Kotlin 函数映射到 SQL 查询。
  • 如何测试数据库是否在正常运行。

实践内容

  • 创建一个包含每晚睡眠数据接口的 Room 数据库。
  • 使用提供的测试来测试该数据库。

在此 Codelab 中,您将为一款用于跟踪睡眠质量的应用构建数据库部分。该应用使用数据库来存储一段时间的睡眠数据。

该应用有两个屏幕,以 fragment 表示,如下图所示。

e28eb795b6812ee4.png

左侧所示的第一个屏幕包含用于开始和停止跟踪的按钮。这个屏幕会显示用户的所有睡眠数据。CLEAR 按钮用于永久删除应用针对用户收集的所有数据。

右侧所示的第二个屏幕用于选择睡眠质量评分。在该应用中,评分用数字表示。出于开发目的,该应用同时显示人脸图标及其对应的数字。

用户的流程如下所示:

  • 用户打开该应用,并看到睡眠跟踪屏幕。
  • 用户点按 START 按钮。系统会记录开始时间并显示该时间。START 按钮会停用,而 STOP 按钮会启用。
  • 用户点按 STOP 按钮。系统会记录结束时间,并打开睡眠质量屏幕。
  • 用户选择一个睡眠质量图标。这个屏幕会关闭,跟踪屏幕会显示睡眠结束时间和睡眠质量。STOP 按钮会停用,而 START 按钮会启用。该应用已为下一晚运行做好准备。
  • 只要数据库中有数据,CLEAR 按钮就会处于启用状态。如果用户点按 CLEAR 按钮,系统会清空其所有数据,并且不予追偿,也就是说,系统不会显示“您确定吗?”这类消息。

该应用在完整架构的基础上采用简化的架构,如下所示。该应用仅使用以下组件:

  • 界面控制器
  • 视图模型及 LiveData
  • Room 数据库

2268f8ae35a8c715.png

第 1 步:下载并运行初始应用

  1. 从 GitHub 下载 TrackMySleepQuality-Starter 应用。
  2. 构建并运行应用。该应用会显示 SleepTrackerFragment fragment 的界面,但不会显示数据。按钮不会响应点按操作。

第 2 步:检查初始应用

  1. 查看 Gradle 文件:
  • 项目 Gradle 文件:在项目级 build.gradle 文件中,注意用于指定库版本的变量。起始应用中所用的版本能够很好地协同工作,并能与此应用完美配合。当您完成此 Codelab 时,Android Studio 可能会提示您更新部分版本。是更新还是继续使用应用中的版本,完全由您决定。如果遇到奇怪的编译错误,请尝试使用最终解决方案应用所用的库版本组合。
  • 模块 Gradle 文件:请注意针对所有 Android Jetpack 库(包括 Room)提供的依赖项,以及协程的依赖项。
  1. 在“Project”窗口中,注意不同的软件包。该应用的结构是按功能划分的。这些软件包包含将在其中添加代码的占位符文件。
  • database 软件包:包含与 Room 数据库相关的所有代码。
  • sleepqualitysleeptracker 软件包:包含每个屏幕的 fragment、视图模型和视图模型工厂。
  1. 查看 Util.kt 文件。此文件包含帮助显示睡眠质量数据的函数。一些代码已被注释掉,因为它们引用了您将在稍后创建的视图模型。
  2. 查看 androidTest 文件夹,注意 SleepDatabaseTest.kt 文件。您将使用此测试来验证数据库是否按预期运行。

在 Android 中,数据用数据类表示。这种数据通过函数调用进行访问并可能会进行修改。不过,在数据库环境中,您需要通过实体和查询来访问及修改数据。

  • 实体表示要在数据库中存储的对象或概念及其属性。在应用代码中,我们需要一个用于定义表的实体类,该类的每个实例都代表相应表中的一行。实体类以映射告知 Room 它打算如何呈现数据库中的信息并与之交互。在您的应用中,实体将保存有关一晚睡眠的信息。
  • 查询是从一个数据库表或多个表的组合中获取数据或信息的请求,或对数据执行操作的请求。常见的查询用于创建、读取、更新和删除实体。例如,您可以通过执行查询来读取应用记录的所有睡眠时段,并按开始时间排序。

在本地持久保留一些数据对提升应用的用户体验(与其他常见用例类似)大有裨益。通过缓存相关数据块,用户即使处于离线状态,也可以享受您的应用带来的乐趣。如果您的应用依赖于服务器,缓存支持用户在离线时修改在本地持久保留的内容。应用恢复网络连接后,这些已缓存更改可以在后台无缝同步到服务器。

Room 会完成所有艰苦的工作,包括获取可存储在 SQLite 表中的 Kotlin 数据类和实体,以及获取函数声明和 SQL 查询。

您必须将每个实体定义为带注解的数据类;并将与该实体的交互定义为带注解的接口,称为“数据访问对象”(DAO)。Room 使用这些带注解的类在数据库中创建表,以及创建针对数据库的查询。

c4a598be115aa77a.png

第 1 步:创建 SleepNight 实体

在此任务中,您将一晚睡眠定义为一个带注解的表示数据库实体的数据类。

您需要记录一晚睡眠的开始时间、结束时间和质量评分。

此外,您还需要一个 ID 来唯一标识那一晚。

  1. database 软件包中,找到并打开 SleepNight.kt 文件。
  2. 创建 SleepNight 数据类,参数包括 ID、开始时间(以毫秒为单位)、结束时间(以毫秒为单位),以及数字形式的睡眠质量评分。
  • 您必须初始化 sleepQuality,因此请将其设置为 -1,以表明未收集到质量数据。
  • 将开始时间初始化为一个已知有效的时间。建议选择以毫秒表示的当前时间。
  • 您还必须初始化结束时间。将其设置为开始时间,这表示尚未记录任何结束时间。
data class SleepNight(
       var nightId: Long = 0L,
       val startTimeMilli: Long = System.currentTimeMillis(),
       var endTimeMilli: Long = startTimeMilli,
       var sleepQuality: Int = -1
)
  1. 在类声明的上方,为该数据类添加 @Entity 注解。此注解有多个可能的参数。默认情况下(@Entity 没有参数),表名称与类名称相同。不过,我们要使用 daily_sleep_quality_table 这个有用的表名称。tableName 的此参数为可选参数,但强烈建议您使用。@Entity 还有其他几个参数,您可以参阅相关文档进行研究。

如果 Android Studio 提示,请从 androidx 库导入 Entity 和所有其他注解。

@Entity(tableName = "daily_sleep_quality_table")
data class SleepNight(...)
  1. 如需将 nightId 标识为主键,请为 nightId 属性添加 @PrimaryKey 注解。将参数 autoGenerate 设置为 true,让 Room 为每个实体生成 ID。这样做可以保证每晚的 ID 一定是唯一的。
@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. database 软件包中,打开 SleepDatabaseDao.kt
  2. 请注意,interface SleepDatabaseDao 带有 @Dao 注解。所有 DAO 都需要使用 @Dao 关键字进行注解。
@Dao
interface SleepDatabaseDao {}
  1. 在该接口的主体内添加 @Insert 注解。在 @Insert 下,添加一个 insert() 函数,该函数将 EntitySleepNight 的实例作为其参数。

大功告成。Room 将生成在数据库中插入 SleepNight 所需的全部代码。当您从 Kotlin 代码调用 insert() 时,Room 将执行 SQL 查询以将该实体插入到数据库中。(注意:您可以随意调用该函数。)

@Insert
fun insert(night: SleepNight)
  1. 添加 @Update 注解以及将一个 SleepNight 作为参数的 update() 函数。更新的实体是与所传入实体具有相同键的实体。您可以更新该实体的部分或全部其他属性。
@Update
fun update(night: SleepNight)

其余功能没有方便使用的注解,因此您必须使用 @Query 注解并提供 SQLite 查询。

  1. 添加一个 @Query 注解以及一个 get() 函数;该函数接受 Long key 参数,并返回可为 null 的 SleepNight。您会看到表示参数缺失的错误。
@Query
fun get(key: Long): SleepNight?
  1. 查询以字符串参数的形式提供给 @Query 注解。将 String 参数添加到 @Query(这是一个 SQLite 查询),用于检索特定 SleepNight 条目中的所有列。
  • 选择 daily_sleep_quality_table 中的所有列
  • WHERE 语句中的 nightId 匹配 :key 参数。

请注意 :key。在查询中使用英文冒号是为了引用该函数中的参数。

("SELECT * from daily_sleep_quality_table WHERE nightId = :key")
  1. 再添加一个 @Query,以及用于从 daily_sleep_quality_table 中删除 (DELETE) 所有信息的 clear() 函数和 SQLite 查询。此查询不会删除表本身。

@Delete 注解会删除一项内容,您可以使用 @Delete 并提供要删除的夜晚列表。这种方法的缺点是,您需要提取或了解表中的内容。@Delete 注解非常适合用于删除特定条目,但在清除表中的所有条目方面效率较低。

@Query("DELETE FROM daily_sleep_quality_table")
fun clear()
  1. 添加 @Query 注解和一个 getTonight() 函数。使 getTonight() 返回的 SleepNight 可为 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 实体的列表作为 LiveDataRoom 会为您保持更新此 LiveData,也就是说,您只需要显式获取一次数据。
  • 您可能需要从 androidx.lifecycle.LiveData 导入 LiveData
@Query("SELECT * FROM daily_sleep_quality_table ORDER BY nightId DESC")
fun getAllNights(): LiveData<List<SleepNight>>
  1. 尽管您不会看到任何明显的更改,但您仍应运行应用以确保其没有错误。

在此任务中,您将创建一个 Room 数据库,它将使用您在上一个任务中创建的 Entity 和 DAO。

您需要创建一个抽象数据库容器类,并为其添加 @Database 注解。这个类有一种方法,能够在数据库不存在时创建数据库的一个实例,或者返回对现有数据库的引用。

获取 Room 数据库的操作有点麻烦,因此在您开始编码之前,请按照以下常规流程操作:

  • 创建一个用于执行 extends RoomDatabase 操作的 public abstract 类。此类将充当数据库容器。它是抽象类,因为 Room 会为您创建实现。
  • 为该类添加 @Database 注解。在参数中,为数据库声明实体并设置版本号。
  • companion 对象内,定义一个返回 SleepDatabaseDao 的抽象方法或属性。Room 将为您生成主体。
  • 整个应用只需要 Room 数据库的一个实例,因此请将 RoomDatabase 设为单例。
  • 使用 Room 的数据库构建器,以仅在数据库不存在时创建数据库。否则,请返回现有数据库。

第 1 步:创建数据库

  1. database 软件包中,打开 SleepDatabase.kt
  2. 在该文件中,创建一个名为 SleepDatabaseabstract 类,用于扩展 RoomDatabase

为该类添加 @Database 注解。

@Database()
abstract class SleepDatabase : RoomDatabase() {}
  1. 您会看到表示实体和版本参数缺失的错误。@Database 注解需要包含几个参数,Room 才能构建数据库。
  • 提供 SleepNight,作为 entities 列表中的唯一项。
  • version 设置为 1每当您更改架构时,都必须增加版本号。
  • exportSchema 设置为 false,这样就不会保留架构版本记录的备份。
@Database(entities = [SleepNight::class], version = 1, exportSchema = false)
  1. 该数据库需要知悉 DAO。在类的主体内,声明一个返回 SleepDatabaseDao 的抽象值。您可能有多个 DAO。
abstract val sleepDatabaseDao: SleepDatabaseDao
  1. 在这个抽象值下方,定义一个 companion 对象。伴生对象允许客户端访问用于创建或获取数据库,而无需该类进行实例化的方法。由于该类的唯一用途是提供数据库,因此没有必要对它进行实例化。
 companion object {}
  1. companion 对象内,为数据库声明一个私有的可为 null 变量 INSTANCE,并将其初始化为 nullINSTANCE 变量将在数据库创建后保留对数据库的引用。这有助于避免重复建立与数据库的连接,因为其计算开销较高。

INSTANCE 添加 @Volatile 注解。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. 在 synchronized 代码块内,将 INSTANCE 的当前值复制到局部变量 instance 中。这是为了利用 Kotlin 的智能类型转换功能,该功能仅适用于局部变量。
var instance = INSTANCE
  1. synchronized 代码块内,在 synchronized 代码块的末尾执行 return instance 操作。忽略返回值类型不匹配错误;操作完成后,永远不会返回 null。
return instance
  1. return 语句上方添加 if 语句,用于检查 instance 是否为 null;如果为 null,即表示还没有数据库。
if (instance == null) {}
  1. 如果 instancenull,请使用数据库构建器获取数据库。在 if 语句的主体中,调用 Room.databaseBuilder,并提供您传入的上下文、数据库类,以及数据库的名称 sleep_history_database
instance = Room.databaseBuilder(
                           context.applicationContext,
                           SleepDatabase::class.java,
                           "sleep_history_database")

Android Studio 将生成类型不匹配错误。如需消除此错误,您必须在后续步骤中添加迁移策略和 build()

  1. 将所需的迁移策略添加到构建器中。使用 .fallbackToDestructiveMigration()

通常,您必须为迁移对象提供在架构发生更改时使用的迁移策略。迁移对象是发挥以下作用的对象:定义如何获取旧架构的所有行并将其转换为新架构中的行,使数据不会丢失。迁移不在此 Codelab 的范围内。一种简单的解决办法是销毁并重新构建数据库,这意味着数据会丢失。

.fallbackToDestructiveMigration()
  1. 最后,调用 .build()。这样做应该就会消除 Android Studio 错误。
.build()
  1. if 语句中,将 INSTANCE = instance 指定为最后一步。
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 的 androidTest 文件夹中,打开 SleepDatabaseTest 文件。
  2. 如需取消注释这些代码,请选择所有带注释的代码,然后按 Cmd+/Control+/ 键盘快捷键。
  3. 查看这个文件。

下面简要介绍了测试代码,因为它们又是一段可以重复使用的代码:

  • SleepDabaseTest 是一个测试类。
  • @RunWith 注解用于标识测试运行程序,即设置和执行测试的程序。
  • 在设置期间,系统会执行带有 @Before 注解的函数,这个函数会使用 SleepDatabaseDao 创建一个内存中 SleepDatabase。“内存中”表示此数据库不会保存到文件系统中,在测试运行完毕后会被删除。
  • 同样,在构建内存中数据库时,代码会调用另一个测试专用方法 allowMainThreadQueries。默认情况下,如果您尝试在主线程上运行查询,会遇到错误。此方法可让您在主线程上运行测试,这个操作只应在测试期间执行。
  • 在带有 @Test 注解的测试方法中,您可以创建、插入和检索 SleepNight,还可以断言它们是相同的。出现任何问题时会抛出异常。在真实的测试中,您需要采用多种 @Test 方法。
  • 测试完成后,系统会执行带有 @After 注解的函数,以关闭数据库。
  1. 右键点击 Project 窗格中的测试文件,然后选择 Run ‘SleepDatabaseTest'
  2. 运行测试后,在 SleepDatabaseTest 窗格中,验证是否所有测试均已通过。

f02d9e6e1f41aee9.png

因为所有测试均已通过,所以现在您了解了以下几方面的情况:

  • 数据库已正确创建。
  • 您可以在数据库中插入 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 函数映射到数据库查询。

如何指明某个接口代表 Room 数据库的 DAO?

  • 使该接口扩展 RoomDAO
  • 使该接口扩展 EntityDao,然后实现 DaoConnection() 方法。
  • 为该接口添加 @Dao 注解。
  • 为该接口添加 @RoomConnection 注解。

问题 3

以下关于 Room 数据库的说法中,哪些是正确的?请选择所有适用的选项。

  • 您可以将 Room 数据库的表定义为带注解的数据类。
  • 如果您从查询返回 LiveData,则 Room 会在 LiveData 发生变化时为您更新 LiveData
  • 每个 Room 数据库必须有且只能有一个 DAO。
  • 为将某个类标识为 Room 数据库,应该使其成为 RoomDatabase 的子类,并为其添加 @Database 注解。

问题 4

您可以在 @Dao 接口中使用以下哪些注解?请选择所有适用的选项。

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

问题 5

如何验证数据库是否在正常运行?请选择所有适用的选项。

  • 编写插桩测试。
  • 持续编写并运行应用,直到它显示数据。
  • 将对 DAO 接口中的方法的调用替换为对 Entity 类中的等效方法的调用。
  • 运行 Room 库提供的 verifyDatabase() 函数。

开始学习下一课:协程和 Room

如需本课程中其他 Codelab 的链接,请参阅“Android Kotlin 基础知识”Codelab 着陆页