#android #dependency-injection #android-room #dagger-hilt #android-database
Вопрос:
У меня есть приложение, в котором я предварительно заполняю базу данных своей комнаты файлом json. Вот моя настройка базы данных для этого:
@Database(version = 2, entities = [(ClimbingRoute::class)], exportSchema = false)
abstract class ClimbingRoutesDatabase : RoomDatabase() {
abstract fun climbingRouteDao(): ClimbingRouteDao
private class ClimbingRouteDatabaseCallback(
private val scope: CoroutineScope,
private val resources: Resources
) : RoomDatabase.Callback() {
override fun onCreate(db: SupportSQLiteDatabase) {
super.onCreate(db)
INSTANCE?.let { database ->
//1
scope.launch{
val climbingRouteDao = database.climbingRouteDao() // 2
prePopulateDatabase(climbingRouteDao) // 3
} }
}
private suspend fun prePopulateDatabase(climbingRouteDao: ClimbingRouteDao){
// 1
val jsonString = resources.openRawResource(R.raw.climbing_routes).bufferedReader().use {
it.readText()
}
// 2
val typeToken = object : TypeToken<List<ClimbingRoute>>() {}.type
val boulderClimbingRoutes = Gson().fromJson<List<ClimbingRoute>>(jsonString, typeToken)
// 3
climbingRouteDao.insertAllClimbingRoutes(boulderClimbingRoutes)
}
}
companion object {
@Volatile
private var INSTANCE: ClimbingRoutesDatabase? = null
fun getDatabase(
context: Context,
coroutineScope: CoroutineScope, // 1
resources: Resources // 2
): ClimbingRoutesDatabase {
val tempInstance = INSTANCE
if (tempInstance != null) {
return tempInstance
}
synchronized(this) {
val instance = Room.databaseBuilder(context.applicationContext,
ClimbingRoutesDatabase::class.java,
"climbing_routes_database")
.addCallback(ClimbingRouteDatabaseCallback(coroutineScope, resources))
.build()
INSTANCE = instance
return instance
}
}
}
}
Теперь я следую учебнику из codingwithflow, в котором используется Hilt. Я хочу использовать его шаблон для добавления инъекции зависимостей в мое приложение. Вот база данных задач и модуль приложений codingwithflow из его учебника.
Его база данных задач
@Database(entities = [Task::class], version = 1)
abstract class TaskDatabase : RoomDatabase() {
abstract fun taskDao(): TaskDao
class Callback @Inject constructor(
private val database: Provider<TaskDatabase>,
@ApplicationScope private val applicationScope: CoroutineScope,
) : RoomDatabase.Callback() {
override fun onCreate(db: SupportSQLiteDatabase) {
super.onCreate(db)
val dao = database.get().taskDao()
applicationScope.launch {
dao.insert(Task("Wash the dishes"))
dao.insert(Task("Do the laundry"))
dao.insert(Task("Buy groceries", important = true))
dao.insert(Task("Prepare food", completed = true))
dao.insert(Task("Call mom"))
dao.insert(Task("Visit grandma", completed = true))
dao.insert(Task("Repair my bike"))
dao.insert(Task("Call Elon Musk"))
}
}
}
}
His AppModule:
@Module
@InstallIn(ApplicationComponent::class)
object AppModule {
@Provides
@Singleton
fun provideDatabase(
app: Application,
callback: TaskDatabase.Callback
) = Room.databaseBuilder(app, TaskDatabase::class.java, "task_database")
.fallbackToDestructiveMigration()
.addCallback(callback)
.build()
@Provides
fun provideTaskDao(db: TaskDatabase) = db.taskDao()
@ApplicationScope
@Provides
@Singleton
fun provideApplicationScope() = CoroutineScope(SupervisorJob())
}
@Retention(AnnotationRetention.RUNTIME)
@Qualifier
annotation class ApplicationScope
I have attempted to utilize this approach for my app but cannot successfully pre-populate the database using my json with dependency injection. The app is loading up with no crashes, but I keep getting an empty database.
I do not yet understand di enough to modify appmodule to make this work. Here is my database setup, appmodule and dao as it stands right now. (not working):
My ClimbingRouteDatabase:
@Database(entities = [(ClimbingRoute::class)], version = 1, exportSchema = false)
abstract class ClimbingRouteDatabase : RoomDatabase() {
abstract fun climbingRouteDao(): ClimbingRouteDao
class Callback @Inject constructor(
private val database: Provider<ClimbingRouteDatabase>,
@ApplicationScope private val applicationScope: CoroutineScope,
private val resources: Resources
) : RoomDatabase.Callback() {
override fun onCreate(db: SupportSQLiteDatabase) {
super.onCreate(db)
val climbingRouteDao = database.get().climbingRouteDao()
applicationScope.launch{
prePopulateDatabase(climbingRouteDao)
}
}
private suspend fun prePopulateDatabase(climbingRouteDao: ClimbingRouteDao){
val jsonString = resources.openRawResource(R.raw.climbing_routes).bufferedReader().use {
it.readText()
}
val typeToken = object : TypeToken<List<ClimbingRoute>>() {}.type
val boulderClimbingRoutes = Gson().fromJson<List<ClimbingRoute>>(jsonString, typeToken)
climbingRouteDao.insert(boulderClimbingRoutes)
}
}
}
My AppModule:
@Module
@InstallIn(ApplicationComponent::class)
object AppModule {
@Provides
@Singleton
fun provideDatabase(
app: Application,
callback: ClimbingRouteDatabase.Callback
) = Room.databaseBuilder(
app,
ClimbingRouteDatabase::class.java,
"climbing_route_database")
.fallbackToDestructiveMigration()
.addCallback(callback)
.build()
@Provides
fun provideClimbingRouteDao(db: ClimbingRouteDatabase) = db.climbingRouteDao()
@Provides
@Singleton
fun resourcesProvider(@ApplicationContext context: Context): Resources = context.resources
@ApplicationScope
@Provides
@Singleton
fun provideApplicationScope() = CoroutineScope(SupervisorJob())
}
@Retention(AnnotationRetention.RUNTIME)
@Qualifier
annotation class ApplicationScope
Восхождение на вершину
@Dao
interface ClimbingRouteDao {
@Insert(onConflict = OnConflictStrategy.IGNORE)
suspend fun insert(climbingRoutes: List<ClimbingRoute>)
@Query("SELECT * FROM climbing_route_table")
fun getClimbingRoutes(): Flow<List<ClimbingRoute>>
@Update
suspend fun update(climbingRoutes: ClimbingRoute)
@Delete
suspend fun delete(climbingRoutes: ClimbingRoute)
}
Любая помощь будет очень признательна. Спасибо.
Ответ №1:
Хорошо, поэтому я решил проблему с помощью….
УДАЛИТЕ ПРИЛОЖЕНИЕ С МОЕГО УСТРОЙСТВА, ПРЕЖДЕ ЧЕМ ЗАПУСКАТЬ ЕГО СНОВА ИЗ ANDROID STUDIO!
Боже, я думаю, живи и учись. Этот приведенный выше код, похоже, пока работает нормально.