如何在 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 下使用一些数据预先填充房间数据库?的主要内容,如果未能解决你的问题,请参考以下文章
找不到`activityViewModels()` Hilt Android
Android Compose 新闻AppViewModelHlit数据流