(Android) Room 数据库中的初始数据是什么时候存储的?

(Android) When is the initial data stored in the Room database?

我目前正在尝试在 Room database 中设置一些初始数据。

结果,初始数据设置成功,但App Inspection确认只有在getWorkoutList()时才保存数据。

更详细地解释一下,初始数据不是单独用插入函数保存的,只有当调用名为getWorkoutList()的DB数据的函数被执行时,初始数据才会保存在DB中ViewModel.

在视图模型中创建数据库时,我希望仅使用插入函数保存初始数据。但事实并非如此。

为什么只有insert函数不能保存初始数据?


以下是根据函数调用在App检查中的DB状态。

1.当只执行insertWorkoutList(data)

2。当只执行getWokroutList()时。

3。当两者都执行时。


代码

@Dao
interface WorkoutListDao {
    @Query("SELECT * FROM WorkoutList")
    suspend fun getWorkoutList() : WorkoutList

    @Insert
    suspend fun insertWorkoutList(workoutList: WorkoutList)
}

锻炼列表数据库

@Database(
    entities = [WorkoutList::class],
    version = 1
)
@TypeConverters(WorkoutListTypeConverter::class)
abstract class WorkoutListDatabase : RoomDatabase() {
    abstract fun workoutListDao() : WorkoutListDao

    companion object {
        private var INSTANCE : WorkoutListDatabase? = null

        @Synchronized
        fun getDatabase(context: Context) : WorkoutListDatabase {
            return INSTANCE ?: synchronized(this) {
                val instance = Room.databaseBuilder(
                    context.applicationContext,
                    WorkoutListDatabase::class.java,
                    "workoutlist_db"
                )
                    .addCallback(WorkoutListCallback(context))
                    .build()
                INSTANCE = instance
                instance
            }
        }
    }
}

WorkoutListCallback

class WorkoutListCallback(private val context: Context) : RoomDatabase.Callback() {
    override fun onCreate(db: SupportSQLiteDatabase) {
        super.onCreate(db)
        CoroutineScope(Dispatchers.IO).launch {
            fillWithStartingWorkoutList(context)
        }
    }

    private fun fillWithStartingWorkoutList(context: Context) {
        val dao = WorkoutListDatabase.getDatabase(context).workoutListDao()

        try {
            val data = loadJsonData(context)
//            dao.insertWorkoutList(data)

        } catch (e: JSONException) {
            e.printStackTrace()
        }
    }

    private fun loadJsonData(context: Context) : WorkoutList {
        val assetManager = context.assets
        val inputStream = assetManager.open(WORKOUTLIST_JSON_FILE)
        
        BufferedReader(inputStream.reader()).use { reader ->
            val gson = Gson()
            return gson.fromJson(reader, WorkoutList::class.java)
        }
    }
}

ViewModel

class WorkoutListViewModel(application: Application) : AndroidViewModel(application) {
    private val workoutDao = WorkoutListDatabase.getDatabase(application).workoutListDao()
    private val workoutListRepo = WorkoutListRepository(workoutDao)

    
    fun setList(part : BodyPart) {
        viewModelScope.launch(Dispatchers.IO) {
            workoutListRepo.getWorkoutList()
        }
    }
}

只有在实际访问数据库时才会调用回调,而不是在获得注解为 class 的 @Database 实例时调用。

因此数据库不是由以下人员创建的:-

val dao = WorkoutListDatabase.getDatabase(context).workoutListDao()

但由

创建
workoutListRepo.getWorkoutList()

那是打开数据库的实际相对耗费资源的操作,留到绝对需要时才使用。

绕过可能是使用:-

    fun getDatabase(context: Context) : WorkoutListDatabase {
        return INSTANCE ?: synchronized(this) {
            val instance = Room.databaseBuilder(
                context.applicationContext,
                WorkoutListDatabase::class.java,
                "workoutlist_db"
            )
                .addCallback(WorkoutListCallback(context))
                .build()
            INSTANCE = instance
            instance.getOpenHelper().getWritableDatabase() //<<<<< FORCE OPEN
            instance
        }
    }

这将强制打开数据库,如果它实际上不存在,则将调用覆盖的 onCreate 方法。 然而,这(我认为)将在主线程上完成,这是您不希望的。

我强烈建议不要使用带有注释的@Dao class(es) 中的函数,特别是如果它们已挂起,因为您可能无法控制线程何时 运行.

您应该使用 SupportSQLiteDatabase passed to the function, it has many methods e.g. you would likely use the insert 方法。

有关订单有问题的示例,请参阅 ROOM database: insert static data before other CRUD operations。该示例包括转换操作以利用预期的 SupportSQLiteDatabase 方法。

您可能会注意到该示例包括将所有数据库更改放入单个事务中,这比将每个单独的操作(示例中的insert/delete)写入磁盘更有效,整个事务(所有动作)都写入磁盘一次。