Raymond Arteaga
Raymond Arteaga

Reputation: 4673

Detect when Room has finished running migrations

I have a complex migration added to my Room database builder. As this particular migration requires downloading some data from a server, I'm using a splash screen to wait for the whole process to finish before opening the app's main activity.

The problem is migrations are not automatically executed on the "build()" method, as they seems to be executed internally on a separated thread.

I also thought on using RoomDatabase.Callback to detect when the database is opened but as the migration is complex and attempts to download data from a server, it can fail, and the app can stay waiting forever.

I need to trigger the migration and wait for its completion, and if possible catch migration errors.

This is the code on the Application class:

appLoadingStatus.value = AppLoadingStatus.INITIALIZING
db= Room.databaseBuilder(this@App,Database::class.java,"app-database")
        .addMigrations(Database.MIGRATION_12_13).build()
//Here I need to wait for migrations to finish in case they are executed, and catch ay possible errors
appLoadingStatus.value = AppLoadingStatus.INITIALIZED

And this is the migration:

val MIGRATION_12_13: Migration = object : Migration(12, 13) {
    override fun migrate(database: SupportSQLiteDatabase) {
        runBlocking(Dispatchers.Main) {
            val newIds =App.instance.getIdsForMigration()?:throw IllegalStateException("Unable do get data from server")
            database.execSQL("ALTER TABLE Coin ADD `id` INTEGER NOT NULL")
            newIds.forEach {symbolIdPair->
                database.execSQL("UPDATE Coin SET id='${symbolIdPair.id}' where symbol='${symbolIdPair.symbol}'")
            }
            database.execSQL("ALTER TABLE Coin RENAME TO `Coin_backup`")
            database.execSQL("CREATE TABLE `Coin` (`symbol` TEXT NOT NULL, `id` INTEGER NOT NULL, `name` TEXT, `imageSrc` TEXT, `rank` INTEGER NOT NULL, `max_supply` REAL NOT NULL, `quantity` REAL NOT NULL, `market` TEXT NOT NULL, `type` TEXT, `price` REAL NOT NULL, `lastUpdate` INTEGER NOT NULL, `lastVolume` REAL NOT NULL, `lastVolumeTo` REAL NOT NULL, `volumeDay` REAL NOT NULL, `volumeDayTo` REAL NOT NULL, `volume24h` REAL NOT NULL, `volume24hTo` REAL NOT NULL, `openDay` REAL NOT NULL, `highDay` REAL NOT NULL, `lowDay` REAL NOT NULL, `open24h` REAL NOT NULL, `high24h` REAL NOT NULL, `low24h` REAL NOT NULL, `change24h` REAL NOT NULL, `changePct24h` REAL NOT NULL, `changeDay` REAL NOT NULL, `changePctDay` REAL NOT NULL, `supply` REAL NOT NULL, `mktCap` REAL NOT NULL, `totalVolume24h` REAL NOT NULL, `totalVolume24hTo` REAL NOT NULL, `favourite` INTEGER NOT NULL, PRIMARY KEY(`id`))")
            database.execSQL("INSERT INTO `Coin` SELECT * FROM `Coin_backup`")
        }
    }
}

Upvotes: 5

Views: 1420

Answers (1)

Raymond Arteaga
Raymond Arteaga

Reputation: 4673

Well, after testing a lot and reading half of the Room source code, it turns out that the migrations will run the first time you access the built database. Also any errors occurred during migration will be thrown at this first call.

Is important if you're using coroutines, to be careful when calling any DB related methods on runBlocking blocks, as it can cause a deadlock in the room implementation.

You can simply call

db.openHelper.readableDatabase

And wait for it's completion. Errors can be catched here too, so you can detect where the migration fails.

You don't know till you know...

Upvotes: 5

Related Questions