You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

使用Hilt预填充Room数据库后数据无法持久化的问题

问题:Room+Hilt预填充数据库后修改无法持久化

我通过Hilt配置的Room数据库,用onCreate从asset预填充数据,运行正常但修改后的数据无法持久化——修改后在应用前台/后台都能保留,但彻底清除后台重启后,所有数据会恢复到预填充的初始状态。需求是仅在应用首次安装时预填充数据,后续修改能永久保存。


相关代码

预填充数据库表结构

CREATE TABLE words (id INTEGER PRIMARY KEY, en TEXT, pl TEXT, speech TEXT)

实体类

@Entity(tableName = "words")
data class MyEntity (
    @PrimaryKey
    @ColumnInfo(name = "id")val id: Int,
    @ColumnInfo(name = "en") val englishWord: String?,
    @ColumnInfo(name = "pl") val polishWord: String?,
    @ColumnInfo(name = "speech") val speech: String?
)

Hilt数据库提供器

@Provides
@Singleton
fun provide(@ApplicationContext context: Context) = Room.databaseBuilder(
    context, AppDatabase::class.java, MY_DATABASE)
    .createFromAsset("database/database.db")
    //.allowMainThreadQueries()
    .fallbackToDestructiveMigration()
    .build()

DAO方法

@Query("SELECT * FROM words")
fun getAll(): LiveData<List<MyEntity>>

@Update
fun updateThat(ent: MyEntity)

解决方案

问题根源是createFromAsset()的默认行为:每次数据库不存在时都会从asset复制,若数据库版本变更且使用fallbackToDestructiveMigration(),会删除旧数据库再重新复制asset里的初始库,导致修改丢失。

方案1:自定义Room回调实现首次预填充

移除createFromAsset()调用,改为在数据库类中添加自定义回调,仅在数据库首次创建时执行预填充:

@Database(entities = [MyEntity::class], version = 1)
abstract class AppDatabase : RoomDatabase() {
    abstract fun wordDao(): WordDao

    companion object {
        private val PREPOPULATE_CALLBACK = object : RoomDatabase.Callback() {
            override fun onCreate(db: SupportSQLiteDatabase) {
                super.onCreate(db)
                // 后台线程执行预填充操作
                CoroutineScope(Dispatchers.IO).launch {
                    // 可直接执行SQL插入预填充数据
                    db.execSQL("INSERT INTO words (id, en, pl, speech) VALUES (1, 'hello', 'cześć', 'noun')")
                    // 批量数据可循环插入或使用事务优化
                }
            }
        }

        fun getInstance(context: Context): AppDatabase {
            return Room.databaseBuilder(context, AppDatabase::class.java, MY_DATABASE)
                .addCallback(PREPOPULATE_CALLBACK)
                .fallbackToDestructiveMigration()
                .build()
        }
    }
}

同步修改Hilt提供器:

@Provides
@Singleton
fun provideAppDatabase(@ApplicationContext context: Context): AppDatabase {
    return AppDatabase.getInstance(context)
}

方案2:手动控制asset数据库复制时机

如果仍想保留从asset复制完整数据库的方式,可通过SharedPreferences标记判断是否首次启动,仅在首次启动时复制:

private fun copyDatabaseIfNotExists(context: Context) {
    val sharedPrefs = context.getSharedPreferences("app_prefs", Context.MODE_PRIVATE)
    val isFirstLaunch = sharedPrefs.getBoolean("first_launch", true)
    
    if (isFirstLaunch) {
        val dbPath = context.getDatabasePath(MY_DATABASE).absolutePath
        val dbFile = File(dbPath)
        if (!dbFile.exists()) {
            // 复制asset中的数据库到目标路径
            context.assets.open("database/database.db").use { inputStream ->
                FileOutputStream(dbFile).use { outputStream ->
                    inputStream.copyTo(outputStream)
                }
            }
        }
        // 标记首次启动已完成
        sharedPrefs.edit().putBoolean("first_launch", false).apply()
    }
}

在Application的onCreate()中先调用该方法,再初始化Room数据库,此时Room会直接使用已存在的数据库,不会再覆盖。


内容的提问来源于stack exchange,提问作者Rifat

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.25 00:32:27