Reputation: 2345
I am trying to prepoluate Room database with data using RoomDatabase.Callback() method and also had success in doing so . Later I tried to work the same with Hilt , but can't figure out how to add the callback while providing the database in hilt module .
This is the DatabaseModule :
@Module
@InstallIn(ApplicationComponent::class)
object DatabaseModule {
@Singleton
@Provides
fun provideDatabase(
@ApplicationContext context: Context
): PersonDatabase {
return Room.databaseBuilder(
context,
PersonDatabase::class.java,
"person_database"
).build()
}
@Singleton
@Provides
fun provideDao(database: PersonDatabase) = database.personDao()
}
This is the Callback class :
class PersonCallback @Inject constructor(
private val dao: PersonDao
) : RoomDatabase.Callback() {
private val applicationScope = CoroutineScope(SupervisorJob())
override fun onCreate(db: SupportSQLiteDatabase) {
super.onCreate(db)
applicationScope.launch(Dispatchers.IO) {
populateDatabase()
}
}
private suspend fun populateDatabase() {
val person = Person("FirstName", "LastName", 20)
dao.insertData(person)
}
}
What I have tried is using the database and providing the dao like this , but it stucks at a loop and the app crashes out . The error in short says , that the following manner is recursive and hence not permittable
@Singleton
@Provides
fun provideDatabase(
@ApplicationContext context: Context
): PersonDatabase {
return Room.databaseBuilder(
context,
PersonDatabase::class.java,
"person_database"
).addCallback(
PersonCallback(provideDatabase(context).personDao())
).build()
}
Then , I followed an SO post and tried to replicate it , which is :
Pre-populating Room database with Hilt without creating an extra instance of the database
Following the above , the application crashed again , providing a huge error which referenced to autogenerated class and had nothing much to it to make it more meaningful
Another Method which I tried was to pass dao as an parameter , but it again crashed with an error saying it leads to a dependency cycle since I also have passed the dao to the Repository as a constructor parameter.
@Singleton
@Provides
fun provideDatabase(
@ApplicationContext context: Context,
personDao: PersonDao
): PersonDatabase {
return Room.databaseBuilder(
context,
PersonDatabase::class.java,
"person_database"
).addCallback(
PersonCallback(personDao)
).build()
}
After all this tries , I am not able to figure out how should I pass dao to the callback class and make it work . I request to suggest some ways to make this happen or any alternatives will also be appreciated .
Upvotes: 7
Views: 1414
Reputation: 23
Explanation of the provider solution provided by Yousef:
The issue was caused by a circular dependency: PersonDatabase needed to provide PersonDao, but PersonCallback required PersonDao during the database creation, creating a loop.
The solution was to use Provider for lazy injection. By doing this, PersonDao isn’t immediately instantiated when PersonDatabase is being built. Instead, it’s only provided when PersonCallback is triggered in the onCreate() method. This ensures that the database is fully initialized before PersonDao is needed, preventing the circular dependency.
In short, Provider solves the issue by delaying the injection until it’s actually required, breaking the dependency cycle.
Upvotes: 0
Reputation: 1363
According link you add in your question you find solution just edit first method provide PersonDatabase
@Singleton
@Provides
fun provideDatabase(
@ApplicationContext context: Context,
provider: Provider<PersonDao>
): PersonDatabase {
return Room.databaseBuilder(
context,
PersonDatabase::class.java,
"person_database"
).addCallback(
PersonCallback(provider)
).build()
}
Then your callback Class take provider for PersonDao jus edit for your class
class PersonCallback (
private val provider: Provider<PersonDao>
) : RoomDatabase.Callback() {
private val applicationScope = CoroutineScope(SupervisorJob())
override fun onCreate(db: SupportSQLiteDatabase) {
super.onCreate(db)
applicationScope.launch(Dispatchers.IO) {
populateDatabase()
}
}
private suspend fun populateDatabase() {
val person = Person("FirstName", "LastName", 20)
provider.get().insertData(person)
}
}
I try this code work fine with me
Upvotes: 11