Jetpack Room 从入门到精通

简介: Jetpack Room 是 Android 官方推荐的持久化库,基于 SQLite 提供类型安全、编译时验证的数据库操作。通过 @Entity、@Dao、@Database 三大组件,简化增删改查,支持协程、LiveData、Flow,实现响应式数据访问,助力构建高效、稳定的本地数据存储方案。

@TOC

概述

Room 是 Google 推出的 Android 官方持久化库,它在 SQLite 的基础上提供了一个抽象层,极大地简化了数据库操作。它通过编译时的 SQL 验证和注解,让开发者能够更安全、更高效地使用 SQLite。

一、为什么选择 Room?

1.对比原生 SQLite

  • 减少样板代码:无需手动编写 SQLiteOpenHelper、ContentValues、Cursor 解析等繁琐代码。
  • 编译时 SQL 验证:在编译阶段检查 SQL 语句的正确性,避免运行时崩溃。
  • 与 LiveData/Flow 集成:查询结果可以直接返回 LiveData 或 Flow,实现数据变化自动通知 UI。
  • 支持 Kotlin 协程:DAO 方法可以声明为 suspend 函数,完美集成协程。
  • 迁移支持:提供便捷的数据库版本迁移机制。
  • 官方推荐:Jetpack 组件,与 Android 生态深度集成。

2.对比greenDao

对比维度 Room GreenDao
开发公司 Google官方(Jetpack架构组件) GreenRobot(第三方开源库)
支持平台 Android,深度集成LiveData/ViewModel Android,轻量级ORM,兼容性广
数据库类型 SQLite抽象层,类型安全,编译时SQL验证 基于SQLite,代码生成策略,性能优化
API设计 注解驱动(@Entity/@Dao),支持RxJava/Flow 代码生成模式,自动生成DAO类,API简洁
性能表现 插入1441ms/查询411ms(华为Mate10测试) 插入2771ms/查询750ms(同条件测试),批量操作更快
缓存机制 LiveData/Flowable自动缓存,实时UI更新 内存高效映射,支持异步操作
数据类型支持 强类型安全,支持自定义类型转换器 基本类型支持,需手动处理复杂类型
事务支持 编译时事务验证,集成Jetpack架构 基础事务支持,需手动管理
社区与文档 官方文档完善,更新频繁,生态成熟 社区活跃,但更新较慢,文档分散
代码生成 运行时通过注解处理器生成DAO实现 编译时生成DAO类,减少样板代码
加密支持 需自定义实现或第三方库 原生支持数据库加密
学习曲线 需掌握Jetpack架构,注解配置较复杂 简单易用,快速上手,配置灵活
典型场景 MVVM架构项目,需要实时数据同步 高性能需求场景,批量数据操作

二、核心组件

Room 有三个主要组件:

1. @Entity (实体类)

代表数据库中的一张表。
使用 @Entity 注解标记一个数据类。
类中的每个属性(字段)默认对应表中的一列。


@Entity(tableName = "users") // 指定表名
data class User(
    @PrimaryKey val uid: Int, // 主键
    @ColumnInfo(name = "first_name") val firstName: String?, // 指定列名
    @ColumnInfo(name = "last_name") val lastName: String?
)

2. @Dao (数据访问对象)

包含用于访问数据库的方法(增删改查)。
使用 @Dao 注解标记一个接口或抽象类。
DAO 是 Room 的核心,所有数据库操作都通过 DAO 完成。


@Dao
interface UserDao {
    @Query("SELECT * FROM users")
    fun getAll(): List<User>

    @Query("SELECT * FROM users WHERE uid IN (:userIds)")
    fun loadAllByIds(userIds: IntArray): List<User>

    @Query("SELECT * FROM users WHERE first_name LIKE :first AND " +
           "last_name LIKE :last LIMIT 1")
    fun findByName(first: String, last: String): User

    @Insert
    fun insertAll(vararg users: User)

    @Update
    fun update(user: User)

    @Delete
    fun delete(user: User)
}

3. @Database (数据库)

作为持久化数据的底层连接的主要访问点。
必须是一个抽象类,并继承自 RoomDatabase。
在注解中指定 entities(实体类)和 version(数据库版本)。


@Database(entities = [User::class], version = 1)
abstract class AppDatabase : RoomDatabase() {
    abstract fun userDao(): UserDao // 获取 DAO 实例

    companion object {
        @Volatile
        private var INSTANCE: AppDatabase? = null

        fun getDatabase(context: Context): AppDatabase {
            return INSTANCE ?: synchronized(this) {
                val instance = Room.databaseBuilder(
                    context.applicationContext,
                    AppDatabase::class.java,
                    "app_database" // 数据库文件名
                ).build()
                INSTANCE = instance
                instance
            }
        }
    }
}

三、基本操作

1. 添加依赖

在 app/build.gradle 文件中添加:


dependencies {
    def room_version = "2.6.1" // 请使用最新稳定版本

    implementation "androidx.room:room-runtime:$room_version"
    annotationProcessor "androidx.room:room-compiler:$room_version"
    // 如果使用 Kotlin 协程,还需要
    implementation "androidx.room:room-ktx:$room_version"
    // 如果使用 Kotlin,使用 kapt
    kapt "androidx.room:room-compiler:$room_version"
}

2. 创建实体 (User.kt)

深色版本
@Entity(tableName = "users")
data class User(
    @PrimaryKey(autoGenerate = true) // 自增主键
    val id: Long,
    val name: String,
    val email: String
)

3. 创建 DAO (UserDao.kt)


@Dao
interface UserDao {
    @Query("SELECT * FROM users")
    suspend fun getAllUsers(): List<User> // 使用 suspend 支持协程

    @Query("SELECT * FROM users WHERE id = :userId")
    suspend fun getUserById(userId: Long): User?

    @Insert
    suspend fun insertUser(user: User): Long // 返回新插入行的主键

    @Update
    suspend fun updateUser(user: User)

    @Delete
    suspend fun deleteUser(user: User)
}

4. 创建数据库 (AppDatabase.kt)


@Database(entities = [User::class], version = 1, exportSchema = false)
abstract class AppDatabase : RoomDatabase() {
    abstract fun userDao(): UserDao

    companion object {
        @Volatile
        private var INSTANCE: AppDatabase? = null

        fun getInstance(context: Context): AppDatabase {
            return INSTANCE ?: synchronized(this) {
                val instance = Room.databaseBuilder(
                    context.applicationContext,
                    AppDatabase::class.java,
                    "user_database"
                )
                // .addCallback(sRoomDatabaseCallback) // 可选:数据库创建/打开回调
                .build()
                INSTANCE = instance
                instance
            }
        }
    }
}

5. 在 Activity/Fragment 中使用

深色版本
class MainActivity : AppCompatActivity() {
    private lateinit var userDao: UserDao

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

        val db = AppDatabase.getInstance(this)
        userDao = db.userDao()

        // 在协程中执行数据库操作
        lifecycleScope.launch {
            // 插入
            val userId = userDao.insertUser(User(0, "Alice", "alice@example.com"))
            Log.d("DB", "Inserted user with id: $userId")

            // 查询
            val users = userDao.getAllUsers()
            Log.d("DB", "All users: $users")
        }
    }
}

四、进阶:精通之路

1. 复杂查询 (@Query)

  • 参数绑定:使用 :paramName 绑定方法参数。
  • 集合参数:使用 IN (:ids) 查询集合。
  • 模糊查询:LIKE '%' || :name || '%'。
  • 排序与分页:ORDER BY, LIMIT, OFFSET。
  • 聚合函数:COUNT, SUM, AVG, MAX, MIN。
    ```Kotlin

@Query("SELECT * FROM users WHERE name LIKE :nameQuery ORDER BY name LIMIT :limit OFFSET :offset")
suspend fun searchUsers(nameQuery: String, limit: Int, offset: Int): List

@Query("SELECT COUNT(*) FROM users")
suspend fun getUserCount(): Int

##  2. 返回自定义对象
查询结果可以映射到非实体类的数据类。

```Kotlin

data class UserNameAndEmail(
    val name: String,
    val email: String
)

@Dao
interface UserDao {
    @Query("SELECT name, email FROM users")
    suspend fun loadUserNamesAndEmails(): List<UserNameAndEmail>
}

3. 数据库关系

Room 支持一对一、一对多、多对多关系,但需要手动处理。

3.1 一对多

例如 User 有多个 Pet。

深色版本
@Entity
data class Pet(
    @PrimaryKey val petId: Long,
    val name: String,
    val userId: Long // 外键,关联 User.id
)

data class UserWithPets(
    @Embedded val user: User,
    @Relation(
        parentColumn = "id",
        entityColumn = "userId"
    )
    val pets: List<Pet>
)

@Dao
interface UserDao {
    @Transaction
    @Query("SELECT * FROM User")
    suspend fun getUsersWithPets(): List<UserWithPets>
}

3.2 多对多

需要一个中间表(Junction Table)。


@Entity(primaryKeys = ["userId", "bookId"])
data class UserBookCrossRef(
    val userId: Long,
    val bookId: Long
)

@Entity
data class Book(
    @PrimaryKey val bookId: Long,
    val title: String
)

data class UserWithBooks(
    @Embedded val user: User,
    @Relation(
        entity = Book::class,
        parentColumn = "id",
        entityColumn = "bookId",
        associateBy = Junction(UserBookCrossRef::class)
    )
    val books: List<Book>
)

4. 异步与响应式编程

返回 LiveData:数据变化时自动通知观察者。


@Query("SELECT * FROM users ORDER BY name")
fun loadUsers(): LiveData<List<User>> // 不再是 suspend

返回 Flow:更强大的响应式流,支持协程。


@Query("SELECT * FROM users ORDER BY name")
fun getUsersFlow(): Flow<List<User>>

在协程作用域中收集:


lifecycleScope.launch {
    userDao.getUsersFlow().collect { users ->
        // 更新 UI
    }
}

5. 数据库迁移

当数据库结构变化(如添加列、修改表)时,需要升级版本并提供迁移策略。


val MIGRATION_1_2 = object : Migration(1, 2) {
    override fun migrate(database: SupportSQLiteDatabase) {
        database.execSQL("ALTER TABLE users ADD COLUMN last_updated INTEGER NOT NULL DEFAULT 0")
    }
}

// 在构建数据库时添加
Room.databaseBuilder(context, AppDatabase::class.java, "database")
    .addMigrations(MIGRATION_1_2)
    .build()

6. 数据库创建/打开回调


private val sRoomDatabaseCallback = object : RoomDatabase.Callback() {
    override fun onOpen(db: SupportSQLiteDatabase) {
        super.onOpen(db)
        // 数据库打开时执行
    }

    override fun onCreate(db: SupportSQLiteDatabase) {
        super.onCreate(db)
        // 数据库创建时执行,可预填充数据
    }
}

7. 类型转换器 (@TypeConverter)

将复杂对象(如 Date, List, 自定义对象)存储为数据库支持的类型(如 Long, String)。

深色版本
class Converters {
    @TypeConverter
    fun fromTimestamp(value: Long?): Date? {
        return value?.let { Date(it) }
    }

    @TypeConverter
    fun dateToTimestamp(date: Date?): Long? {
        return date?.time
    }
}

// 在数据库类中注册
@Database(entities = [User::class], version = 1)
@TypeConverters(Converters::class)
abstract class AppDatabase : RoomDatabase() {
    // ...
}

五、使用建议与注意事项

1.使用建议

Room 通过注解和编译时代码生成,极大地简化了 Android 上的 SQLite 操作。从定义实体、DAO 到构建数据库,整个过程清晰、类型安全。结合协程、LiveData 和 Flow,可以构建出响应迅速、用户体验良好的应用。

精通 Room 的关键在于:

  • 熟练掌握 @Query 的各种用法。
  • 理解并正确处理数据库关系。
  • 灵活运用 LiveData 和 Flow 实现响应式 UI。
  • 掌握数据库迁移和类型转换器。
  • 遵循最佳实践,编写高效、可靠的数据库代码。

2.注意事项

  • 不要在主线程执行数据库操作:Room 会抛出 IllegalStateException。使用 suspend 函数配合协程,或返回 LiveData/Flow。

  • 使用单例模式:数据库实例应全局唯一,避免频繁创建和销毁。

  • 合理设计实体和关系:避免过度复杂的关系查询。
  • 谨慎处理迁移:测试迁移脚本,避免数据丢失。
  • 利用编译时检查:Room 会在编译时报错,及时修复 SQL 语法错误。
  • 考虑数据量:对于超大数据集,考虑分页加载。
  • 使用 @Transaction:确保多个数据库操作的原子性。
相关文章
|
24天前
|
XML Java 数据库连接
MyBatis Plus 核心功能与用法
MyBatis Plus 是基于 MyBatis 的增强工具,简化单表 CRUD 操作。通过继承 BaseMapper 即可实现增删改查,支持条件构造器、分页、逻辑删除、自动填充、乐观锁等高级功能,大幅提升开发效率,无需编写冗余 SQL 与 XML,助力快速构建 Spring Boot 应用。
141 0
|
传感器 JSON 物联网
什么是MQTT遗嘱消息?如何配置和处理遗嘱消息?
什么是MQTT遗嘱消息?如何配置和处理遗嘱消息?
1198 0
什么是MQTT遗嘱消息?如何配置和处理遗嘱消息?
|
1月前
|
安全 Java 编译器
Kotlin 高阶语法解析
本文深入解析 Kotlin 高级语法,涵盖协程、密封类、内联函数、扩展函数、类型系统、作用域函数、数据类解构、委托模式、高阶函数、DSL 构建、Flow 数据流等核心特性,结合实战示例详解其在 Android 开发中的应用,助你掌握现代 Kotlin 编程精髓。
115 4
|
21天前
|
Java Swift Android开发
Kotlin vs Swift:现代移动开发的“双子星”全面对比
Kotlin与Swift是现代移动开发的两大主流语言,分别主导Android与iOS生态。本文全面对比二者在语法、空安全、并发、性能及跨平台等方面的异同,深入解析其设计理念与适用场景,助你根据平台需求与技术战略做出最优选择。
110 0
|
21天前
|
缓存 调度 数据库
RxJava基础操作符和高级操作符
RxJava基于观察者模式,通过Observable、Observer、Subscription和Disposable四大核心实现响应式编程。支持冷热数据流,提供丰富操作符进行变换、过滤与组合,并借助Scheduler实现线程调度,Flowable解决背压问题,Subject用于状态共享,适用于异步事件处理与复杂数据流管理。
77 0
|
28天前
|
存储 移动开发 前端开发
CSS 中 `data-status` 的使用详解
`data-status` 是HTML5自定义属性,用于存储元素状态(如active、error),结合CSS属性选择器可动态应用样式,支持精确匹配、模糊匹配及伪元素内容插入,提升前端状态管理灵活性。
61 0
|
1月前
|
安全 Java Android开发
Android 开发核心技术深度解析
本文系统讲解Android开发核心技术,涵盖Java基础、四大组件、Jetpack、Kotlin语言、性能优化及安全发布等,助力开发者构建高质量应用。
226 0
|
7月前
|
人工智能 测试技术 API
Apifox对比Apipost:2025年推荐的API协作工具
Apifox与Apipost这两大国产API平台的全方位较量,助你在2025年做出最明智的选择。
|
5月前
|
API Windows
GetTickCount() 函数的作用和用法
GetTickCount() 函数是 Windows API 中的一个重要函数,用于获取自系统启动以来经过的毫秒数。这个函数通常用于计算时间间隔、性能分析和定时等场景。
214 0
|
消息中间件 安全 Linux
深入探索Linux操作系统的内核机制
本文旨在为读者提供一个关于Linux操作系统内核机制的全面解析。通过探讨Linux内核的设计哲学、核心组件、以及其如何高效地管理硬件资源和系统操作,本文揭示了Linux之所以成为众多开发者和组织首选操作系统的原因。不同于常规摘要,此处我们不涉及具体代码或技术细节,而是从宏观的角度审视Linux内核的架构和功能,为对Linux感兴趣的读者提供一个高层次的理解框架。

热门文章

最新文章