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

Posted

技术标签:

【中文标题】如何在 hilt viewModel 下使用一些数据预先填充房间数据库?【英文标题】:How to pre-populate the room database with some data under hilt viewModel? 【发布时间】:2021-10-18 19:35:23 【问题描述】:

我用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() 而不使用 hilt 看起来像这样

@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?

【问题讨论】:

【参考方案1】:

我试着用之前的思路写了,大致上,成功了,但是不知道有没有不对的地方,如有不对请告诉我!

@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()

【讨论】:

【参考方案2】:

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

@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

【讨论】:

以上是关于如何在 hilt viewModel 下使用一些数据预先填充房间数据库?的主要内容,如果未能解决你的问题,请参考以下文章

带 Hilt 的活动片段通信

找不到`activityViewModels()` Hilt Android

Android Compose 新闻AppViewModelHlit数据流

没有 @Provides-annotated 方法就不能提供 Hilt

刀柄找不到服务

Android MVVM框架搭建HiltViewBindingActivity Result API