2. 搭建 Room 项目环境:添加依赖、配置 KSP/Gradle、初始化数据库、定义实体与 DAO 基础结构

2.1 为什么需要独立搭建环境?

Room 是 Android Jetpack 的持久化库,它在 SQLite 之上提供了编译时校验、自动生成 DAO 实现、与 LiveData/Flow 天然集成等能力。搭建时如果依赖版本不一致、注解处理器(KSP 或 kapt)配置错误,会导致编译失败或运行时 DAO 方法找不到。本章定出标准化搭建步骤:添加依赖 → 配置 KSP → 编写实体 → 创建 DAO → 构建 Database 类 → 初始化实例。

2.2 Gradle 依赖与 KSP 配置

Room 从 2.4.0 起推荐使用 KSP(Kotlin Symbol Processing)替代 kapt,编译速度提升 2~3 倍。以下在 build.gradle.kts(Module 级)中配置:

plugins {
    id("com.android.application")
    id("org.jetbrains.kotlin.android")
    id("com.google.devtools.ksp") version "1.9.22-1.0.17"  // 与 Kotlin 版本匹配
}

android {
    namespace = "com.example.roomcache"
    compileSdk = 34
    defaultConfig {
        minSdk = 26
        targetSdk = 34
    }
}

dependencies {
    val roomVersion = "2.6.1"
    implementation("androidx.room:room-runtime:$roomVersion")
    implementation("androidx.room:room-ktx:$roomVersion")
    ksp("androidx.room:room-compiler:$roomVersion")
    // optional: Room test helpers
    testImplementation("androidx.room:room-testing:$roomVersion")
}

使用 Kotlin DSL 时注意:ksp 配置块需要与 com.google.devtools.ksp 插件配合。如果项目已有 Java 代码,可以保留 kapt,但建议统一使用 KSP。

2.3 定义实体 Entity

实体对应数据库表,用 @Entity 注解标记。增量更新缓存场景中,常用字段包含 id(主键)、data(JSON 数据)、timestamp(版本时间戳):

import androidx.room.Entity
import androidx.room.PrimaryKey

@Entity(tableName = "cache_entries")
data class CacheEntry(
    @PrimaryKey
    val key: String,                 // 缓存唯一标识(如 API 路径)
    val data: String,                // 缓存数据(一般序列化为 JSON)
    val timestamp: Long,             // 最后更新时间戳(毫秒)
    val version: Int = 1             // 增量版本号,初始为 1
)
实体设计提示:version 字段用于增量更新时比较本地版本与服务器端版本,避免全量替换。timestamp 可用于过期策略。

2.4 创建 DAO(Data Access Object)

DAO 负责数据库读写,使用 @Dao 注解,内定义查询、插入、更新、删除方法:

import androidx.room.*

@Dao
interface CacheDao {
    @Query("SELECT * FROM cache_entries WHERE `key` = :key")
    suspend fun getCache(key: String): CacheEntry?

    @Insert(onConflict = OnConflictStrategy.REPLACE)
    suspend fun insertCache(entry: CacheEntry)

    @Query("UPDATE cache_entries SET data = :data, timestamp = :timestamp, version = :version WHERE `key` = :key")
    suspend fun updateCache(key: String, data: String, timestamp: Long, version: Int)

    @Query("DELETE FROM cache_entries WHERE `key` IN (:keys)")
    suspend fun deleteCaches(keys: List)
}
注解作用示例
@Query编写自定义 SQL,自动返回 DAO 方法结果@Query("SELECT * FROM cache_entries")
@Insert插入一条或多条记录,支持冲突策略@Insert(onConflict = OnConflictStrategy.REPLACE)
@Update更新已有记录(根据主键匹配)@Update
@Delete删除记录@Delete
关键区别:@Insert(onConflict = REPLACE) 在插入冲突时会完全替换整行,而 @Update 只更新非空字段。增量更新策略中建议使用 @Insert(REPLACE) 简化逻辑。

2.5 构建 Database 类

继承 RoomDatabase,声明实体列表、DAO 抽象方法,并定义单例数据库构建器:

import android.content.Context
import androidx.room.Database
import androidx.room.Room
import androidx.room.RoomDatabase

@Database(
    entities = [CacheEntry::class],
    version = 1,
    exportSchema = true          // 导出 schema 用于增量迁移
)
abstract class AppDatabase : RoomDatabase() {
    abstract fun cacheDao(): CacheDao

    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,
                    "room_cache.db"
                )
                    .fallbackToDestructiveMigration()   // 开发期临时使用(生产应写 Migration)
                    .build()
                INSTANCE = instance
                instance
            }
        }
    }
}
注意:fallbackToDestructiveMigration() 会在数据库版本升级且未提供 Migration 时清除所有数据。生产环境必须编写 Migration 对象实现增量迁移,否则用户数据将丢失。

2.6 初始化数据库(Application 中)

通常在 Application 的 onCreate 中预初始化,避免首次打开页面时的延迟:

import android.app.Application

class MyApp : Application() {
    lateinit var database: AppDatabase
        private set

    override fun onCreate() {
        super.onCreate()
        database = AppDatabase.getDatabase(this)
    }
}

别忘了在 AndroidManifest.xml 中注册:

<application
    android:name=".MyApp"
    ... >
    ...
</application>

2.7 搭建流程总览

Room 项目环境搭建流程 (Gradle + KSP) 添加 Room 依赖 配置 KSP 插件 定义 Entity 创建 DAO 接口 继承 RoomDatabase Application 中初始化 room_cache.db 关键:KSP ≥ 1.0.17 + Room ≥ 2.6.0 | exportSchema = true 用于增量迁移

2.8 常见错误与校验

  • KSP 版本与 Kotlin 不匹配:报错 Module was compiled with an incompatible version of Kotlin。检查 classpath "org.jetbrains.kotlin:kotlin-gradle-plugin" 版本与 KSP 版本对应关系(参考 KSP Releases)。
  • DAO 方法未生成:忘记添加 ksp 或误用 kapt 但未配置 kapt 依赖。确认 build.gradle 中 ksp("androidx.room:room-compiler:...") 存在且插件生效。
  • 实体字段未列在表中:Room 默认只保留 @PrimaryKey 和 @ColumnInfo 注解字段。如果漏加 @ColumnInfo,字段名会与 Kotlin 属性名一致,但可以通过 @ColumnInfo(name = "column_name") 显式定义。
性能优化基线:数据库初始化建议放在后台线程(如 Dispatchers.IO)执行,避免阻塞主线程。可以在 Application 中启动协程预加载。
// 预初始化示例
GlobalScope.launch(Dispatchers.IO) {
    AppDatabase.getDatabase(this@MyApp).cacheDao().insertCache(
        CacheEntry(key = "_init", data = "{}", timestamp = System.currentTimeMillis())
    )
}