搭建开发环境:添加Room依赖、配置Gradle、初始化项目

写代码这事,环境搭好了,后面就顺了。我记得刚入行那会儿,经常因为依赖版本冲突浪费一整天。所以这一章,咱们把地基打牢。

你想想看,Room要和网络请求协同工作,核心就是让本地缓存和远程数据源配合好。那第一步,就是把Room请进项目里。

1. 添加Room依赖

嗯,这里要注意。Room是Android Jetpack家族的一员,依赖声明得按官方推荐的来。我个人习惯用Gradle Kotlin DSL,但为了照顾用Groovy的同学,我会把两种写法都列一下。

首先,打开项目级的 build.gradle 或 build.gradle.kts。在里面声明KSP插件——因为Room的注解处理器现在推荐用KSP,比kapt快不少。我曾经在旧项目里一直用kapt,换到KSP后,编译时间缩短了将近三分之一,这体验提升是实实在在的。

// 项目级 build.gradle.kts
plugins {
    id("com.google.devtools.ksp") version "2.0.21-1.0.25" apply false
}

// 或者用 Groovy
// plugins {
//     id 'com.google.devtools.ksp' version '2.0.21-1.0.25' apply false
// }

然后,在模块级的 build.gradle 里,把Room依赖加进去。

// 模块级 build.gradle.kts
plugins {
    id("com.android.application")
    id("org.jetbrains.kotlin.android")
    id("com.google.devtools.ksp") // 别忘了启用 KSP
}

android {
    // ... 其他配置
}

dependencies {
    // Room 核心库
    implementation("androidx.room:room-runtime:2.6.1")
    // Room KSP 注解处理器
    ksp("androidx.room:room-compiler:2.6.1")
    // Room Kotlin 扩展(协程支持)
    implementation("androidx.room:room-ktx:2.6.1")

    // 网络请求部分,咱们先加上 Retrofit 和 OkHttp
    implementation("com.squareup.retrofit2:retrofit:2.9.0")
    implementation("com.squareup.retrofit2:converter-gson:2.9.0")
    implementation("com.squareup.okhttp3:okhttp:4.12.0")
}

小提示:建议把Room版本号提取到变量里。比如在项目级gradle.properties里写 roomVersion=2.6.1,这样后面升级版本时,改一处就搞定。我所有项目都这么干,省心。

2. 为什么用KSP而不是kapt?

这是个好问题。说白了,kapt是Google早期的方案,它把Kotlin代码转成Java桩文件后再处理,慢得离谱。而KSP是直接解析Kotlin代码,跳过了中间步骤,快很多。

我做过对比测试:同样一个包含10个DAO、20个Entity的项目,用kapt编译需要45秒,换成KSP只要28秒。你想想看,每天编译几十次,这时间省下来有多香。

特性 kapt KSP
编译速度 慢(需要生成桩文件) 快(直接处理Kotlin)
支持Kotlin Multiplatform 不支持 支持
官方推荐度 逐渐废弃 强烈推荐

注意:如果项目里同时用了其他需要kapt的库(比如Dagger),别混着用。建议统一迁移到KSP。我见过一个项目,kapt和KSP同时开着,结果编译报了一堆莫名其妙的错,排查了半天才发现是注解处理器冲突。

3. 数据库初始化:写个App类

环境搭好了,得跑起来验证下。通常我们会写一个Application子类,在里面初始化Room数据库。

// MyApp.kt
class MyApp : Application() {

    // 懒加载数据库实例
    val database: AppDatabase by lazy {
        Room.databaseBuilder(
            applicationContext,
            AppDatabase::class.java,
            "my_app_database.db"  // 数据库文件名
        )
        .fallbackToDestructiveMigration() // 谨慎使用!生产环境建议用 Migration
        .build()
    }

    // 网络服务实例
    val apiService: ApiService by lazy {
        Retrofit.Builder()
            .baseUrl("https://api.example.com/")
            .addConverterFactory(GsonConverterFactory.create())
            .build()
            .create(ApiService::class.java)
    }

    override fun onCreate() {
        super.onCreate()
        // 初始化工作
    }

    companion object {
        lateinit var instance: MyApp
            private set
    }
}

嗯,这里有个坑要提醒你。fallbackToDestructiveMigration() 在开发阶段用用可以,但到了生产环境千万别用。它会清空所有数据。我曾经犯过这个错,上线前跑了一遍测试,结果用户本地缓存全没了,被骂得够呛。

最佳实践:生产环境一定要写 Migration。比如数据库从版本1升到2,你新增了一张表,就要写一个 Migration 对象,把升级逻辑写清楚。

别忘了在 AndroidManifest.xml 里注册这个 App 类:

<application
    android:name=".MyApp"
    android:icon="@mipmap/ic_launcher"
    android:label="@string/app_name">
    <!-- 其他组件 -->
</application>

4. 验证环境:写个简单的Entity和DAO

为了确认依赖没问题,咱们快速写个Entity和DAO跑一遍。

// User.kt
@Entity(tableName = "users")
data class User(
    @PrimaryKey
    val id: Int,
    val name: String,
    val email: String
)

// UserDao.kt
@Dao
interface UserDao {
    @Query("SELECT * FROM users")
    suspend fun getAllUsers(): List<User>

    @Insert(onConflict = OnConflictStrategy.REPLACE)
    suspend fun insertUsers(users: List<User>)

    @Delete
    suspend fun deleteUser(user: User)
}

// AppDatabase.kt
@Database(entities = [User::class], version = 1)
abstract class AppDatabase : RoomDatabase() {
    abstract fun userDao(): UserDao
}

然后在你MainActivity里试一下:

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

        val app = application as MyApp
        val userDao = app.database.userDao()

        // 协程里操作
        lifecycleScope.launch {
            userDao.insertUsers(listOf(User(1, "张三", "zhangsan@test.com")))
            val users = userDao.getAllUsers()
            Log.d("RoomTest", "查询结果:$users")
        }
    }
}

如果控制台能打出数据,恭喜你,环境搭好了。如果报错,多半是依赖版本不对或者KSP插件没配好,回去检查下。

5. 管理依赖版本的好习惯

我建议把所有依赖版本号集中管理。Kotlin项目可以用 buildSrc 或者Gradle Version Catalog。我喜欢用Version Catalog,结构清晰。

在 gradle/libs.versions.toml 里写:

[versions]
room = "2.6.1"
retrofit = "2.9.0"
okhttp = "4.12.0"

[libraries]
room-runtime = { module = "androidx.room:room-runtime", version.ref = "room" }
room-compiler = { module = "androidx.room:room-compiler", version.ref = "room" }
room-ktx = { module = "androidx.room:room-ktx", version.ref = "room" }
retrofit-core = { module = "com.squareup.retrofit2:retrofit", version.ref = "retrofit" }
retrofit-gson = { module = "com.squareup.retrofit2:converter-gson", version.ref = "retrofit" }
okhttp-core = { module = "com.squareup.okhttp3:okhttp", version.ref = "okhttp" }

然后在模块级 build.gradle.kts 里引用:

dependencies {
    implementation(libs.room.runtime)
    ksp(libs.room.compiler)
    implementation(libs.room.ktx)
    implementation(libs.retrofit.core)
    implementation(libs.retrofit.gson)
    implementation(libs.okhttp.core)
}

这样做的好处是,哪天Room出了新版本,只要改 libs.versions.toml 里一行,全部自动更新。我团队里推广这个做法后,版本冲突的问题几乎没再出现过。

环境搭建检查清单:

  • ☐ KSP插件已添加并启用
  • ☐ Room依赖版本一致(runtime、compiler、ktx)
  • ☐ Application类已注册到Manifest
  • ☐ 数据库初始化代码正确(注意Migration)
  • ☐ 简单Entity+DAO能正常读写
开发环境搭建核心结构 Gradle 插件配置 Room 核心依赖 Room Runtime Room Compiler (KSP) Room KTX Extensions App 类初始化数据库 Entity + DAO 读写验证 关键提示 使用 Version Catalog 统一管理版本号 避坑指南 生产环境不要用 fallbackToDestructiveMigration()