如何在 hilt viewModel 下用一些数据预填充房间数据库?

How to pre-populate the room database with some data under hilt viewModel?

我用 hilt 写了一个数据库创建方法,我想在数据库中预填充一些数据,但是我应该如何在 AppModule 中写一个 RoomDatabase.Callback() ?

@Database(entities = [Puzzle::class], version = 1, exportSchema = false)
abstract class PuzzleDatabase : RoomDatabase() {
    abstract fun getPuzzleDao() : PuzzleDao
}

@Module
@InstallIn(SingletonComponent::class)
object AppModule {

    @Singleton
    @Provides
    fun PuzzleDatabase(
        @ApplicationContext app: Context,
        scope: CoroutineScope
    ) = Room.databaseBuilder(
        app,
        PuzzleDatabase::class.java,
        "puzzle_database"
    ).build()

    @Singleton
    @Provides
    fun getDao(db: PuzzleDatabase) = db.getPuzzleDao()
}

在不使用刀柄的情况下使用 viewModel 创建 RoomDatabase.Callback() 如下所示

@Database(
    entities = [Puzzle::class], version = 1
)
abstract class PuzzleDatabase : RoomDatabase() {
    abstract fun puzzleDao(): PuzzleDao

    private class PuzzleDataBaseCallBack(
        private val scope: CoroutineScope
    ): RoomDatabase.Callback() {
        override fun onCreate(db: SupportSQLiteDatabase) {
            super.onCreate(db)
            INSTANCE?.let { database ->
                scope.launch {
                    val puzzleDao = database.puzzleDao()
                    puzzleDao.insert(Puzzle(0, 9L, 5L, Puzzles.TWO))
                }
            }
        }
    }

    companion object {

        @Volatile
        private var INSTANCE: PuzzleDatabase? = null

        fun getDatabase(
            context: Context,
            scope: CoroutineScope
        ): PuzzleDatabase {
            return INSTANCE ?: synchronized(this) {
                val instance = Room.databaseBuilder(
                    context.applicationContext,
                    PuzzleDatabase::class.java,
                    "puzzle_database"
                )
                    .addCallback(PuzzleDataBaseCallBack(scope))
                    .build()
                INSTANCE = instance
                instance
            }
        }
    }
}

但是在我的AppModule中,PuzzleDataBase()有@Singleton @Provides注解,我该如何正确创建PuzzleDataBaseCallBack?

您可以尝试使用以下代码:

@Database(entities = [Puzzle::class], version = 1, exportSchema = false)
abstract class PuzzleDatabase : RoomDatabase() {
abstract fun getPuzzleDao() : PuzzleDao

companion object {
    @Volatile
    private var instance: PuzzleDatabase ? = null

    fun getInstance(
        context: Context,
        scope: CoroutineScope
    ): PuzzleDatabase {
        return instance ?: synchronized(this) {
            instance ?: buildDatabase(context).also { instance = it }
        }
    }

    private fun buildDatabase(context: Context): PuzzleDatabase {
        return Room.databaseBuilder(context, PuzzleDatabase ::class.java, "puzzleapp.db")
            .addCallback(object : RoomDatabase.Callback() {
                override fun onCreate(db: SupportSQLiteDatabase) {
                    super.onCreate(db)
            // moving to a new thread
            ioThread {
                getInstance(context).dataDao()
                                    .insert(PREPOPULATE_DATA)
                }
            })
            .build()
    }
}
}

我的情况是:

@Database(entities = [BeerItem::class], version = 2, exportSchema = false)
abstract class BeerDatabase : RoomDatabase() {
    abstract fun beerDao(): BeerDao

    companion object {
        @Volatile
        private var instance: BeerDatabase? = null

        fun getInstance(
            context: Context,
            scope: CoroutineScope
        ): BeerDatabase {
            return instance ?: synchronized(this) {
                instance ?: buildDatabase(context).also { instance = it }
            }
        }

        private fun buildDatabase(context: Context): BeerDatabase {
            return Room.databaseBuilder(context, BeerDatabase::class.java, "beerapp.db")
                .addCallback(object : RoomDatabase.Callback() {
                    override fun onCreate(db: SupportSQLiteDatabase) {
                        super.onCreate(db)
                        val request = OneTimeWorkRequestBuilder<DatabaseWorker>().build()
                        WorkManager.getInstance(context).enqueue(request)
                    }
                })
                .build()
        }
    }
}

##DatabaseWorker 是:

class DatabaseWorker(
    context: Context,
    workerParams: WorkerParameters
) : CoroutineWorker(context, workerParams) {
    
    override suspend fun doWork(): Result = coroutineScope {
        withContext(Dispatchers.IO) {
            try {
                @Suppress("BlockingMethodInNonBlockingContext")
                applicationContext.assets.open(DATA_FILENAME).use { inputStream ->
                    JsonReader.of(Okio.buffer(Okio.source(inputStream))).use { jsonReader ->
                        val moshi = Moshi.Builder().add(KotlinJsonAdapterFactory()).build()
                        val listType =
                            Types.newParameterizedType(List::class.java, BeerItem::class.java)
                        val adapter: JsonAdapter<List<BeerItem>> = moshi.adapter(listType)
                        val list = adapter.fromJson(jsonReader)

                        BeerDatabase.getInstance(
                            applicationContext,
                            CheersApp.instance.applicationScope
                        ).beerDao().insertBeers(list!!)
                        Result.success()
                    }
                }
            } catch (e: Exception) {
                Timber.e(e, "Error seeding database")
                Result.failure()
            }
        }
    }
}

您可以在这里找到所有代码:https://github.com/ArcaDone/PunkAPI

我试着用之前的思路写的,大致的,成功了,但是不知道有没有不对的地方,如果有的话还请大家指教!

@Database(entities = [Puzzle::class], version = 1, exportSchema = false)
abstract class PuzzleDatabase : RoomDatabase() {
    abstract fun getPuzzleDao() : PuzzleDao
}

@Module
@InstallIn(SingletonComponent::class)
object AppModule {

    @Volatile
    private var INSTANCE: PuzzleDatabase? = null

    private class PuzzleDatabaseCallback(
        private val scope: CoroutineScope
    ) : RoomDatabase.Callback() {
        override fun onCreate(db: SupportSQLiteDatabase) {
            super.onCreate(db)
            INSTANCE?.let { /*database ->*/
                scope.launch {
                    // you can pre-populate some data here
                    // database.getPuzzleDao().insert(Puzzle(0, 90000L, 2L, Puzzles.TWO))
                }
            }
        }
    }

    @Singleton
    @Provides
    fun puzzleDataBase(
        @ApplicationContext app: Context
    ) : PuzzleDatabase {
        return INSTANCE ?: synchronized(this) {
            val scope = CoroutineScope(Dispatchers.IO)
            val instance = Room.databaseBuilder(
                app,
                PuzzleDatabase::class.java,
                "puzzle_database"
            )   .addCallback(PuzzleDatabaseCallback(scope))
                .build()
                .also { INSTANCE = it }
            instance
        }
    }

    @Singleton
    @Provides
    fun getDao(db: PuzzleDatabase) = db.getPuzzleDao()

}

repository.kt

class PuzzleRepository @Inject constructor (
    val puzzleDao: PuzzleDao
) {
    val all = puzzleDao.getAll()
}