欢迎来到 Advanced Room 系列,该系列涵盖了有关 Room Persistent Library 的所有详细信息。在本系列中,我们将从 Room 的基础知识开始,然后我们将学习如何使用它。我们将详细学习 Room 的组件,我们还将学习如何将 Room 与 LiveData 和其他第三方库(如 RxJava 和 Kotlin 协程)一起使用。
那么,让我们从 Room Persistent Library 简介开始。
这部分涵盖了 Room 持久性库的基础知识。阅读本文后,您可以开始在您的 Android 应用程序中使用 Room。
Room 是一个 Android 持久性库,它是 Google 的Android Jetpack项目的一部分。根据文档,Room 在 SQLite 之上提供了一个抽象层,以允许流畅的数据库访问,同时利用 SQLite 的全部功能。
处理大量结构化数据的应用程序可以从本地持久化数据中受益匪浅。最常见的用例是缓存相关的数据。这样,当设备无法访问网络时,用户仍然可以在离线时浏览该内容。在设备重新联机后,任何用户发起的内容更改都会同步到服务器。
在您的项目中添加Room
在模块的(app)build.gradle
文件中添加以下内容:
dependencies {
implementation "androidx.room:room-runtime:2.2.5"
kapt "androidx.room:room-compiler:2.2.5"
}
使用Room的好处
与 SQLiteOpenHelper 等其他替代解决方案相比,使用 Room 有多种优势:
- 查询的编译时验证。
- 减少样板代码。
- 易于理解和使用。
- 与 RxJava、LiveData 和 Kotlin 协程轻松集成。
Room的组成部分
Room中有3个主要组件:
- 数据库:包含数据库持有者,并充当与应用的持久关系数据的底层连接的主要访问点。
- 实体:表示数据库中的一个表。
- DAO:包含用于访问数据库的方法。
您的应用程序使用Room 数据库来获取与您的数据库关联的数据访问对象或DAO。然后,该应用程序使用每个DAO从数据库中获取实体,并将对这些实体的任何更改保存回数据库。最后,应用程序使用实体来获取和设置与数据库中的表列相对应的值。
数据库
如前所述,它包含数据库持有者,并充当与应用的持久关系数据的底层连接的主要访问点。被注解的类@Database
应满足以下条件:
- 是一个扩展的抽象类
RoomDatabase
。 - 在注释中包含与数据库关联的实体列表。
- 包含一个具有 0 个参数的抽象方法,并返回带有 @Dao 注释的类
- 在运行时,您可以
Database
通过调用Room.databaseBuilder()
或Room.inMemoryDatabaseBuilder()
@Database(entities = arrayOf(User::class), version = 1)
abstract class UserDatabase : RoomDatabase() {
abstract fun userDao(): UserDao
}
要获取数据库的实例,可以使用以下方法:
val db = Room.databaseBuilder(
applicationContext,
UserDatabase::class.java, "users-db"
).build()
注意: 如果您的应用程序在单个进程中运行,则在实例化
RoomDatabase
对象时应遵循单例设计模式。每个 实例都相当昂贵,您很少需要在单个进程中访问多个实例。RoomDatabase
实体
实体表示数据库中的表。此类使用@Entity 注释进行注释。此类中的数据成员表示表中的列。
@Entity
data class User(
@PrimaryKey val uid: Int,
@ColumnInfo(name = "first_name") val firstName: String?,
@ColumnInfo(name = "last_name") val lastName: String?
)
- 实体中的所有字段都必须是公共的或具有 getter 和 setter 方法。
- 实体类应该有一个空的构造函数(如果所有字段都可以访问)或一个参数化的构造函数,它接受所有字段。Room 也可以使用部分构造函数。
- 每个实体类必须至少有一个主键。您可以使用@PrimaryKey 注释
primaryKeys
为多个字段定义单个字段主键或@Entity 注释的属性。您还可以使用autoGenerate
@PrimaryKey 注释的属性来自动分配主键。
@Entity(primaryKeys = arrayOf("firstName", "lastName"))
- 默认情况下,Room 使用类名作为数据库表名。如果您希望表具有不同的名称,请设置注释的
tableName
属性。@Entity
同样,您可以使用@ColumnInfo
注解的 name 属性来定义列的名称。
@Entity(tableName = "users")
- 如果您不想保留任何字段,可以使用注释它们
@Ignore.
@Ignore val picture: Bitmap?
- 您可以使用
@Entity
注释的 indices 属性向实体添加索引。此外,您可以通过将注释的unique
属性设置为 来创建唯一索引。@Indextrue
@Entity(indices = arrayOf(Index(value = ["last_name", "address"])))@Entity(indices = arrayOf(Index(value = ["first_name", "last_name"],
unique = true)))
数据访问对象 (DAO)
DAO 提供了用于访问数据库的 API。这是一个带有@Dao 注解的接口。该接口中的所有方法都用于从数据库中获取数据或对数据库进行更改。这些方法使用@Query、@Insert、@Delete 等注解进行注解。
@Dao
interface UserDao {
@Query("SELECT * FROM user")
fun getAll(): List
@Query("SELECT * FROM user WHERE uid IN (:userIds)")
fun loadAllByIds(userIds: IntArray): List
@Insert
fun insertAll(vararg users: User)
@Delete
fun delete(user: User)
}
注意: 所有使用 UserDao 的查询都是在调用者线程上进行的。因此,您应该注意不要从 UI(主)线程调用任何方法。
类型转换器
有时,您可能需要在单个数据库列中保留自定义数据类型。您可以将类型转换器用于这些类型的用例。
class Converters {
@TypeConverter
fun fromTimestamp(value: Long?): Date? {
return value?.let { Date(it) }
}
@TypeConverter
fun dateToTimestamp(date: Date?): Long? {
return date?.time?.toLong()
}
}
接下来,您必须将@TypeConverters
注释添加到RoomDatabase
类中,以便 Room 可以使用您为其中的每个实体和DAO定义的转换器RoomDatabase.
@Database(entities = arrayOf(User::class), version = 1)
@TypeConverters(Converters::class)
abstract class UserDatabase : RoomDatabase() {
abstract fun userDao(): UserDao
}
这都是关于 Room 的基础知识。
作者:Deepanshu
链接:Introduction to Room Persistent Library in Android