跳转到正文
YGJoon
返回

Android Kotlin Room 数据库进阶实践

编辑文章

架构设计

数据库配置

@Database(
    entities = [ArticleEntity::class, AuthorEntity::class],
    version = 4,
    exportSchema = true
)
abstract class AppDatabase : RoomDatabase() {
    abstract fun articleDao(): ArticleDao
}

fun provideDatabase(context: Context): AppDatabase = Room.databaseBuilder(
    context,
    AppDatabase::class.java,
    "app.db"
).fallbackToDestructiveMigrationOnDowngrade()
 .addMigrations(MIGRATION_3_4)
 .build()

实体与关联

@Entity(tableName = "article")
data class ArticleEntity(
    @PrimaryKey val id: Long,
    val title: String,
    val authorId: Long,
    val publishTime: Instant
)

@Entity(tableName = "author")
data class AuthorEntity(
    @PrimaryKey val id: Long,
    val name: String
)

data class ArticleWithAuthor(
    @Embedded val article: ArticleEntity,
    @Relation(parentColumn = "authorId", entityColumn = "id")
    val author: AuthorEntity
)

Dao

@Dao
interface ArticleDao {
    @Transaction
    @Query("SELECT * FROM article ORDER BY publishTime DESC")
    fun observeArticles(): Flow<List<ArticleWithAuthor>>

    @Insert(onConflict = OnConflictStrategy.REPLACE)
    suspend fun insertArticles(articles: List<ArticleEntity>)
}

迁移与测试

val MIGRATION_3_4 = object : Migration(3, 4) {
    override fun migrate(database: SupportSQLiteDatabase) {
        database.execSQL("ALTER TABLE article ADD COLUMN isPinned INTEGER NOT NULL DEFAULT 0")
    }
}

@RunWith(AndroidJUnit4::class)
class MigrationTest {
    @get:Rule
    val helper = MigrationTestHelper(
        InstrumentationRegistry.getInstrumentation(),
        AppDatabase::class.java.canonicalName,
        FrameworkSQLiteOpenHelperFactory()
    )
}

多模块协同

interface ArticleRepository {
    fun observeArticles(): Flow<List<Article>>
    suspend fun sync()
}

class ArticleRepositoryImpl @Inject constructor(
    private val dao: ArticleDao,
    private val api: ArticleApi
) : ArticleRepository {
    override fun observeArticles(): Flow<List<Article>> =
        dao.observeArticles().map { list -> list.map { it.toDomain() } }

    override suspend fun sync() {
        val remote = api.fetchArticles()
        dao.insertArticles(remote.map { it.toEntity() })
    }
}

性能优化

@Transaction
suspend fun replaceAll(articles: List<ArticleEntity>) {
    deleteAll()
    insertArticles(articles)
}

协程与事务

@Transaction
suspend fun ArticleDao.upsert(article: ArticleEntity) {
    val old = getById(article.id)
    if (old == null) insert(article) else update(article)
}

常见问题

问题原因解决方案
UI 卡顿在主线程执行数据库操作确保使用 suspend/Flow + Dispatchers.IO
迁移失败schema_version 不匹配更新 exportSchema,编写准确迁移脚本
多模块冲突DAO 重复定义在核心模块统一管理 DAO

总结

  1. Room 通过 Flow、协程与 Compose 无缝衔接,适合构建实时数据体验。
  2. 设计清晰的实体、迁移与 Repository,可提升可维护性。
  3. 对性能、迁移、测试持续投入,使数据库层稳定可靠,为离线与同步能力打下基础。

编辑文章
分享这篇文章:

上一篇
Android Kotlin Wear OS 与 Compose 多端协同
下一篇
Android Kotlin KSP 编译期代码生成