如何在 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()
}
我用 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()
}