I'm using Room
onCreate()
callback to initialize some data when the db is created.
Using hilt:
@Singleton
@Provides
fun provideDatabase(@ApplicationContext applicationContext: Context): AppDatabase {
return Room.databaseBuilder(applicationContext, AppDatabase::class.java, DATABASE_NAME)
.allowMainThreadQueries()
.fallbackToDestructiveMigration()
.addCallback(object : RoomDatabase.Callback() {
override fun onCreate(db: SupportSQLiteDatabase) {
super.onCreate(db)
val provideDatabase = provideDatabase(applicationContext)
provideDatabase.userDao().createUser(LocalUser())
}
})
.build()
}
But I got exception when using userDao
:
java.nio.channels.OverlappingFileLockException
Not sure why it happen, maybe I'm using Room+Hilt wrongly?
Based on this tutorial without hilt it should work without exception: https://developermemos.com/posts/prepopulate-android-room-data#google_vignette
When the onCreate
callback is invoked, the database has already been created. Calling onCreate again as per
super.onCreate
will try to again create the database, the exact same file and hence encounter the lock.
The database is passed to the callback hence db: SupportSQLiteDatabase
Consider, as an example:-
....
.addCallback(object : RoomDatabase.Callback() {
override fun onCreate(db: SupportSQLiteDatabase) {
//super.onCreate(db)
//val provideDatabase = provideDatabase(applicationContext)
//provideDatabase.userDao().createUser(LocalUser())
DatabaseUtils.dumpCursor(db.query("SELECT * FROM sqlite_master"))
}
})
.build()
Where the schema (sqlite_master) is queried and the resultant cursor dumped (output to the log). e.g. using the above results in:-
2024-04-17 17:33:55.152 I/System.out: >>>>> Dumping cursor android.database.sqlite.SQLiteCursor@68ac7e0
2024-04-17 17:33:55.152 I/System.out: 0 {
2024-04-17 17:33:55.152 I/System.out: type=table
2024-04-17 17:33:55.152 I/System.out: name=android_metadata
2024-04-17 17:33:55.152 I/System.out: tbl_name=android_metadata
2024-04-17 17:33:55.152 I/System.out: rootpage=3
2024-04-17 17:33:55.152 I/System.out: sql=CREATE TABLE android_metadata (locale TEXT)
2024-04-17 17:33:55.153 I/System.out: }
2024-04-17 17:33:55.153 I/System.out: 1 {
2024-04-17 17:33:55.153 I/System.out: type=table
2024-04-17 17:33:55.153 I/System.out: name=tasks
2024-04-17 17:33:55.153 I/System.out: tbl_name=tasks
2024-04-17 17:33:55.153 I/System.out: rootpage=4
2024-04-17 17:33:55.153 I/System.out: sql=CREATE TABLE `tasks` (`taskId` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `name` TEXT NOT NULL)
2024-04-17 17:33:55.153 I/System.out: }
2024-04-17 17:33:55.153 I/System.out: 2 {
2024-04-17 17:33:55.153 I/System.out: type=table
2024-04-17 17:33:55.153 I/System.out: name=sqlite_sequence
2024-04-17 17:33:55.153 I/System.out: tbl_name=sqlite_sequence
2024-04-17 17:33:55.153 I/System.out: rootpage=5
2024-04-17 17:33:55.153 I/System.out: sql=CREATE TABLE sqlite_sequence(name,seq)
2024-04-17 17:33:55.153 I/System.out: }
2024-04-17 17:33:55.153 I/System.out: 3 {
2024-04-17 17:33:55.153 I/System.out: type=table
2024-04-17 17:33:55.153 I/System.out: name=TaskTaskCR
2024-04-17 17:33:55.153 I/System.out: tbl_name=TaskTaskCR
2024-04-17 17:33:55.153 I/System.out: rootpage=6
2024-04-17 17:33:55.153 I/System.out: sql=CREATE TABLE `TaskTaskCR` (`parentTaskId` INTEGER NOT NULL, `childTaskId` INTEGER NOT NULL, PRIMARY KEY(`parentTaskId`, `childTaskId`))
2024-04-17 17:33:55.153 I/System.out: }
2024-04-17 17:33:55.153 I/System.out: 4 {
2024-04-17 17:33:55.153 I/System.out: type=index
2024-04-17 17:33:55.153 I/System.out: name=sqlite_autoindex_TaskTaskCR_1
2024-04-17 17:33:55.153 I/System.out: tbl_name=TaskTaskCR
2024-04-17 17:33:55.153 I/System.out: rootpage=7
2024-04-17 17:33:55.154 I/System.out: sql=null
2024-04-17 17:33:55.154 I/System.out: }
2024-04-17 17:33:55.154 I/System.out: 5 {
2024-04-17 17:33:55.154 I/System.out: type=table
2024-04-17 17:33:55.154 I/System.out: name=room_master_table
2024-04-17 17:33:55.154 I/System.out: tbl_name=room_master_table
2024-04-17 17:33:55.154 I/System.out: rootpage=8
2024-04-17 17:33:55.154 I/System.out: sql=CREATE TABLE room_master_table (id INTEGER PRIMARY KEY,identity_hash TEXT)
2024-04-17 17:33:55.154 I/System.out: }
2024-04-17 17:33:55.154 I/System.out: <<<<<
i.e. as can be seen the database consists of the following:-
@Entity
annotated class)@Entity
annotated class)What you should be doing is utilising the passed SupportSQLiteDatabase to initialise the data. That's why it is passed to the callback.
e.g. db.execSQL("INSERT INTO tasks (name) VALUES('Task001')")
Using the above with the extra line, then App Inspection, after running, shows:-