1. 准备工作
此 Codelab 将介绍如何自行构建一个名为 Forage 的新应用,并且会逐步引导您在 Android Studio 中完成 Forage 应用项目,包括项目设置和测试。
前提条件
- 此项目适合已学完《使用 Kotlin 进行 Android 开发的基础知识》课程第 5 单元的学生。
构建内容
- 通过实现一个实体、DAO、ViewModel 和数据库类,借助 Room 为现有应用增加数据持久性功能。
所需条件
- 一台安装了 Android Studio 的计算机。
2. 已完成的应用概览
完成后的 Forage 应用可让用户跟踪他们在自然界中搜寻到的物品,例如食物。在使用 Room 的情况下,此数据可在会话间持久保留。您要运用您掌握的 Room 知识以及对数据库执行读取、写入、更新和删除操作的知识,在 Forage 应用中实现数据持久性。下面介绍了完成后的应用及其功能。
用户首次启动该应用时,系统会显示一个空屏幕,其中包含一个 Recycler 视图(用于显示搜寻的物品)和右下角的一个悬浮按钮(用于添加新物品)。
添加新物品时,用户可以指定名称、找到物品的位置以及一些其他备注。此外还有一个复选框,用于表明食物目前是否应季。
物品添加后将显示在第一个屏幕的 Recycler 视图中。
点按物品后将转到一个详情屏幕,显示相应物品的名称、位置和备注。
悬浮按钮也会从加号变为修改图标。点按此按钮将转到另一个屏幕,供您修改名称、位置、备注和“应季”复选框。点按删除按钮会从数据库中移除该物品。
虽然此应用的界面部分已实现,但您的任务是运用您掌握的 Room 知识实现数据持久性,让应用可以对数据库执行读取、写入、更新和删除物品的操作。
3. 开始
下载项目代码
请注意,文件夹名称为 android-basics-kotlin-forage-app
。在 Android Studio 中打开项目时,请选择此文件夹。
如需获取此 Codelab 的代码并在 Android Studio 中打开它,请执行以下操作。
获取代码
- 点击提供的网址。此时,项目的 GitHub 页面会在浏览器中打开。
- 在项目的 GitHub 页面上,点击 Code 按钮,这时会出现一个对话框。
- 在对话框中,点击 Download ZIP 按钮,将项目保存到计算机上。等待下载完成。
- 在计算机上找到该文件(很可能在 Downloads 文件夹中)。
- 双击 ZIP 文件进行解压缩。系统将创建一个包含项目文件的新文件夹。
在 Android Studio 中打开项目
- 启动 Android Studio。
- 在 Welcome to Android Studio 窗口中,点击 Open an existing Android Studio project。
注意:如果 Android Studio 已经打开,请依次选择 File > New > Import Project 菜单选项。
- 在 Import Project 对话框中,前往解压缩的项目文件夹所在的位置(很可能在 Downloads 文件夹中)。
- 双击该项目文件夹。
- 等待 Android Studio 打开项目。
- 点击 Run 按钮 以构建并运行应用。请确保该应用按预期构建。
- 在 Project 工具窗口中浏览项目文件,了解应用的设置方式。
4. 设置项目以使用 Room
定义 Forableable 实体
该项目已有一个用于定义应用数据的 Forageable
类 (model.Forageable.kt)。此类具有以下几个属性:id
、name
、address
、inSeason
和 notes
。
data class Forageable(
val id: Long = 0,
val name: String,
val address: String,
val inSeason: Boolean,
val notes: String?
)
不过,若要使用此类来存储持久性数据,您需要将其转换为 Room 实体。
- 使用
@Entity
和表名称"forageable_database"
为此类添加注解。 - 将
id
属性设为主键。系统应该会自动生成主键。 - 将
inSeason
属性的列名称设为"in_season"
。
实现 DAO
您或许已猜到,如果要对您将从视图模型访问的数据库执行读写操作,您要在 ForageableDao
(data.ForageableDao.kt) 中定义用于执行读写操作的方法。由于 DAO 只是您定义的接口,因此您实际上并不需要编写任何代码来实现这些方法,而是应该使用 Room 注解,并根据需要指定 SQL 查询。
在 ForageableDao
接口中,您需要添加五个方法。
getForageables()
方法,用于返回数据库中所有行的Flow<List<Forageable>>
。getForageable(id: Long)
方法,用于返回与指定id
匹配的Flow<Forageable>
。insert(forageable: Forageable)
方法,用于将一个新的Forageable
插入到数据库中。update(forageable: Forageable)
方法,接受一个现有Forageable
作为参数并更新相应的行。delete(forageable: Forageable)
方法,接受一个Forageable
作为参数并从数据库中将其删除。
实现视图模型
ForageableViewModel
(ui.viewmodel.ForageableViewModel.kt) 已部分实现,但您需要添加用于访问 DAO 方法的功能,使其可实际读取和写入数据。请执行以下步骤来实现 ForageableViewModel
。
- 应将一个
ForageableDao
实例作为参数传入类构造函数。 - 创建一个类型为
LiveData<List<Forageable>>
的变量,该变量使用 DAO 获取完整的Forageable
实体列表,并将结果转换为LiveData
。 - 创建一个接受 ID(类型为
Long
)作为参数的方法,该方法通过对 DAO 调用getForageable()
方法返回LiveData<Forageable>
,并将结果转换为LiveData
。 - 在
addForageable()
方法中,使用viewModelScope
启动协程,并使用 DAO 将Forageable
实例插入到数据库中。 - 在
updateForageable()
方法中,使用 DAO 更新Forageable
实体。 - 在
deleteForageable()
方法中,使用 DAO 更新Forageable
实体。 - 创建一个
ViewModelFactory
,它可使用ForageableDao
构造函数参数创建一个ForageableViewModel
实例。
实现数据库类
ForageDatabase
(data.ForageDatabase.kt
) 类是将实体和 DAO 实际公开给 Room 的类。请按照说明实现 ForageDatabase
类。
- 实体:
Forageable
- 版本:
1
- exportSchema:
false
- 在
ForageDatabase
类中,添加一个抽象函数以返回ForageableDao
- 在
ForageDatabase
类中,使用名为INSTANCE
的私有变量和返回ForageDatabase
单例的getDatabase()
函数定义一个伴生对象。
- 在
BaseApplication
类中,创建一个database
属性,返回使用延迟初始化的ForageDatabase
实例。
5. 在 fragment 中持久保留并从中读取数据
设置实体、DAO、视图模型并定义数据库类以将其公开给 Room 后,您只需修改 fragment 便可访问视图模型。您需要在三个文件(应用中的每个屏幕对应一个文件)中进行更改。
Forageable 列表
forageable 列表只需要两项内容:对视图模型的引用以及对完整 forageable 列表的访问。请在 ui.ForageableListFragment.kt 中执行以下任务。
- 该类已有
viewModel
属性。但是,这样使用的并不是您在上一步中定义的工厂。您需要先重构此声明才能使用ForageableViewModelFactory
。
private val viewModel: ForageableViewModel by activityViewModels {
ForageableViewModelFactory(
(activity?.application as BaseApplication).database.foragableDao()
)
}
- 然后,在
onViewCreated()
中,观察viewModel
中的allForageables
属性,并酌情对适配器调用submitList()
以填充列表。
Forageable 详情屏幕
对 ui/ForageableDetailFragment.kt 中的详情列表执行的操作几乎相同。
- 转换
viewModel
属性以正确初始化ForageableViewModelFactory
。 - 在
onViewCreated()
中,对视图模型调用getForageable()
并传入id
以获取Forageable
实体。观察 LiveData 并将结果设为forageable
属性,然后调用bindForageable()
以更新界面。
添加和修改 forageable 屏幕
最后,您需要在 ui.AddForageableFragment.kt 中执行类似的操作。请注意,此屏幕还负责更新和删除实体。不过,视图模型中的这些方法已在正确的位置调用。您只需要在此文件中进行两项更改。
- 同样,重构
viewModel
属性以使用ForageableViewModelFactory
。 - 在
onViewCreated()
中的 if 语句块中设置删除按钮的可见性之前,对视图模型调用getForageable()
,传入id
,并将结果设为forageable
属性。
在 fragment 中需要执行的操作就这些。现在,您可以运行自己的应用,应该能看到所有持久性功能都在实际发挥作用。
6. 测试说明
运行测试
如需运行测试,您可以执行以下任一操作。
对于单个测试用例,打开测试用例类 PersistenceInstrumentationTests.kt
,并点击类声明左侧的绿色箭头。然后,从菜单中选择“Run”选项。这样会运行测试用例中的所有测试。
经常会有您只想运行单个测试的情况,例如,有一个测试失败,而其他测试都通过。您可以像运行整个测试用例一样运行单个测试。点击绿色箭头并选择 Run 选项。
如果您有多个测试用例,也可以运行整个测试套件。就像运行应用一样,您可以在 Run 菜单中找到此选项。
请注意,Android Studio 默认运行您运行的最后一个目标(应用目标、测试目标等),因此如果菜单仍然显示 Run > Run ‘app',您可以通过选择 Run > Run 运行测试目标。
然后从弹出式菜单中选择测试目标。