Room 是一个强大的 SQLite 对象映射库,旨在提供更健壮、更简洁、更符合现代开发模式的数据库访问方式。
核心价值: 消除大量样板代码,提供编译时 SQL 验证,强制结构化数据访问,并流畅集成 LiveData、Flow 和 RxJava 以实现响应式 UI。
Room 的使用遵循一个清晰的结构化流程:
添加依赖:
// build.gradle (Module)
dependencies {
def room_version = "2.6.1" // 使用最新稳定版本
implementation "androidx.room:room-runtime:$room_version"
kapt "androidx.room:room-compiler:$room_version" // Kotlin 使用 kapt
// 可选:Kotlin 扩展和协程支持
implementation "androidx.room:room-ktx:$room_version"
// 可选:RxJava2 支持
implementation "androidx.room:room-rxjava2:$room_version"
// 可选:RxJava3 支持
implementation "androidx.room:room-rxjava3:$room_version"
// 可选:测试支持
androidTestImplementation "androidx.room:room-testing:$room_version"
}
定义数据实体 (Entity):
@Entity
注解标注一个数据类。@PrimaryKey
定义主键(可以是 autoGenerate = true
实现自增)。@ColumnInfo(name = "column_name")
自定义列名(可选)。@Index
)、唯一约束 (@Index(unique = true)
) 等。@Entity(tableName = "users",
indices = [Index(value = ["last_name", "address"], unique = true)])
data class User(
@PrimaryKey(autoGenerate = true) val id: Int = 0,
@ColumnInfo(name = "first_name") val firstName: String,
@ColumnInfo(name = "last_name") val lastName: String,
val age: Int,
val address: String? // 可空类型对应数据库可为 NULL
)
定义数据访问对象 (DAO - Data Access Object):
@Dao
注解标注一个接口或抽象类。@Insert
:插入一个或多个实体。返回 Long
(插入行的 ID)或 Long[]
/List
。onConflict
参数定义冲突策略(如 OnConflictStrategy.REPLACE
)。@Update
:更新一个或多个实体。返回 Int
(受影响的行数)。@Delete
:删除一个或多个实体。返回 Int
(受影响的行数)。@Query("SQL_STATEMENT")
:执行自定义 SQL 查询。这是最强大的注解。
List
、LiveData
、Flow
、RxJava 类型 (Single
, Observable
等) 或简单类型 (Int
, String
等)。:paramName
在 SQL 中引用方法参数。@Dao
interface UserDao {
@Insert(onConflict = OnConflictStrategy.IGNORE)
suspend fun insert(user: User): Long // 协程支持
@Update
suspend fun update(user: User): Int
@Delete
suspend fun delete(user: User): Int
@Query("SELECT * FROM users ORDER BY last_name ASC")
fun getAllUsers(): Flow<List<User>> // 使用 Flow 实现响应式流
@Query("SELECT * FROM users WHERE id = :userId")
fun getUserById(userId: Int): LiveData<User> // 使用 LiveData 观察单个用户变化
@Query("SELECT * FROM users WHERE age > :minAge")
suspend fun getUsersOlderThan(minAge: Int): List<User> // 普通挂起函数
@Query("DELETE FROM users WHERE last_name = :lastName")
suspend fun deleteUsersByLastName(lastName: String): Int
}
定义数据库类 (Database):
RoomDatabase
的抽象类。@Database
注解标注,并指定:
entities
:包含该数据库中的所有实体类数组。version
:数据库版本号(整数)。每次修改数据库模式(表结构)时必须增加此版本号。exportSchema
:是否导出数据库模式信息到文件(默认为 true
,建议保留用于版本迁移)。@Dao
接口/抽象类的抽象方法(无参数)。@Database(entities = [User::class, Product::class], version = 2, exportSchema = true)
abstract class AppDatabase : RoomDatabase() {
abstract fun userDao(): UserDao
abstract fun productDao(): ProductDao
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,
"my_app_database.db" // 数据库文件名
)
.addCallback(roomCallback) // 可选:数据库创建/打开回调
.addMigrations(MIGRATION_1_2) // 版本迁移策略 (见下文)
// .fallbackToDestructiveMigration() // 危险:破坏性迁移(仅开发调试)
// .fallbackToDestructiveMigrationOnDowngrade() // 降级时破坏性迁移
.build()
INSTANCE = instance
instance
}
}
// 可选:数据库首次创建或打开时的回调(用于预填充数据等)
private val roomCallback = object : RoomDatabase.Callback() {
override fun onCreate(db: SupportSQLiteDatabase) {
super.onCreate(db)
// 在主线程执行!小心耗时操作。通常用协程在后台预填充。
}
override fun onOpen(db: SupportSQLiteDatabase) {
super.onOpen(db)
// 数据库每次打开时调用
}
}
// 定义从版本 1 到版本 2 的迁移策略
private val MIGRATION_1_2 = object : Migration(1, 2) {
override fun migrate(database: SupportSQLiteDatabase) {
// 执行必要的 SQL 语句来修改数据库模式
database.execSQL("ALTER TABLE users ADD COLUMN email TEXT")
}
}
}
}
在应用中使用数据库:
AppDatabase.getInstance(context)
获取数据库实例。Dao
(如 db.userDao()
)。Dao
的方法执行数据库操作。IllegalStateException
)。这是为了防止 UI 卡顿。必须在后台线程(如使用 Kotlin 协程
、RxJava
、LiveData
+ ViewModel
+ Repository
模式、ExecutorService
)中执行耗时操作。room-ktx
提供了对 Kotlin 协程的完美支持,@Dao
方法可以标记为 suspend
。LiveData
或 Flow
的查询方法会在数据变化时自动通知观察者,非常适合驱动 UI 更新。Room 会自动在后台线程执行查询并管理 LiveData
/Flow
的生命周期。class UserViewModel(application: Application) : AndroidViewModel(application) {
private val db = AppDatabase.getInstance(application)
private val userDao = db.userDao()
// 使用 Flow 暴露用户列表,Repository 模式更佳
val allUsers: Flow<List<User>> = userDao.getAllUsers()
fun insert(user: User) {
viewModelScope.launch(Dispatchers.IO) { // 在 IO 线程池执行
userDao.insert(user)
}
}
fun getUser(userId: Int): LiveData<User> = userDao.getUserById(userId)
}
数据库迁移 (Migration - 重要!):
@Database
注解中的 version
。Migration
策略告诉 Room 如何从旧版本升级到新版本。使用 addMigrations(...)
添加到数据库构建器中。Migration
对象重写 migrate(database: SupportSQLiteDatabase)
方法,在其中执行必要的 ALTER TABLE
, CREATE TABLE
, DROP TABLE
等 SQL 语句。.fallbackToDestructiveMigration()
或 .fallbackToDestructiveMigrationOnDowngrade()
。生产环境慎用!Room 适用于需要结构化、关系型、本地持久化存储的场景:
LiveData
/Flow
)。SQLiteOpenHelper
和 ContentProvider
: 提供更现代、更简洁、更安全的抽象层。不适合的场景:
SharedPreferences
或 DataStore
。Firestore
(云) 或本地 NoSQL 方案(虽然 Room 也能存 JSON,但查询不高效)。Room 的核心是一个编译时注解处理器,它在编译阶段生成实现代码,运行时库则提供执行环境。其设计哲学是**“抽象而不隐藏”**,开发者依然写 SQL,但获得了更好的安全性和便利性。
编译时处理 (Annotation Processing):
room-compiler
(KAPT/KSP) 扫描代码中的 @Entity
, @Dao
, @Database
, @Query
等注解。@Entity
生成对应的 *_Table
类(包含表名、列名、创建表 SQL 等元信息)。@Dao
接口/抽象类生成具体的实现类 (如 UserDao_Impl
)。这个实现类包含:
@Insert
, @Update
, @Delete
注解方法的实现:使用 EntityInsertionAdapter
, EntityUpdateAdapter
, EntityDeletionAdapter
等内部类处理绑定参数和执行 SQL。@Query
的核心: 对于每个 @Query
方法:
@Entity
定义)。*_Query
类(如 getUserById_Query
)。这个类:
:paramName
) 绑定到 SQLite 语句 (bind
方法) 的逻辑。Cursor
(SQLite 查询结果游标)行数据转换为 Java/Kotlin 对象 (Entity
或简单类型) 的逻辑 (convert
/map
方法)。@Database
类生成实现类 (如 AppDatabase_Impl
)。这个类:
AppDatabase
。userDao()
),返回生成的 UserDao_Impl
实例。createAllTables
) 和迁移相关的逻辑。SupportSQLiteOpenHelper
实例(由 Room.databaseBuilder
配置),这是实际打开和管理 SQLite 数据库的核心类。运行时库 (room-runtime
):
RoomDatabase
, Room
等核心类和构建器 (databaseBuilder
, inMemoryDatabaseBuilder
)。*_Impl
类间接使用 SupportSQLiteOpenHelper
(内部封装了 SQLiteOpenHelper
或直接使用 SQLite
API)来打开、关闭和操作实际的 SQLite 数据库文件。SupportSQLite*
): Room 定义了一套 SupportSQLiteDatabase
, SupportSQLiteStatement
等接口。这些接口由 room-runtime
提供的 FrameworkSQLite*
实现类具体实现(最终调用 Android Framework 的 SQLiteDatabase
, SQLiteStatement
)。这提供了抽象层,方便测试(可以用内存实现替换)。runInTransaction
),确保操作的原子性。LiveData
/Flow
集成: 对于返回 LiveData
或 Flow
的查询方法,Room 在内部使用 InvalidationTracker
机制。它注册一个观察者监听底层 SupportSQLiteDatabase
的变化通知(通过 SQLite 的 sqlite3_update_hook
或更现代的 SQLiteDatabase.OnCommitListener
等)。当检测到相关表发生修改(Insert/Update/Delete)时,它会自动触发 LiveData
更新或发射新的 Flow
值(在后台线程重新执行查询并传递新结果)。TypeConverter
): 如果 @Entity
包含 Room 不直接支持的类型(如 Date
, UUID
, 自定义枚举),你可以定义 @TypeConverter
类,Room 会在读写数据库时自动调用这些转换器进行类型映射。核心优势原理总结:
Entity
和 Dao
清晰地定义了数据模型和访问接口,符合良好的架构原则(如 Clean Architecture)。suspend
)、响应式流(LiveData
, Flow
)、RxJava,简化异步编程和 UI 更新。Dao
接口)使得单元测试业务逻辑时更容易 mock 数据库层。room-testing
提供测试辅助工具。总结: Room 通过编译时代码生成和运行时抽象封装,将原始 SQLite API 的强大功能与现代化开发所需的类型安全、简洁性、响应式支持和架构友好性完美结合,成为 Android 本地结构化数据存储的首选和标准解决方案。理解其流程、场景和原理,能帮助开发者更高效、更可靠地构建数据层。