Precompila il database delle stanze virtuali

Se vuoi che la tua app inizi con un database già caricato con un set di dati specifico, puoi precompilare il database. In Room, puoi utilizzare le API per precompilare un database all'inizializzazione con i contenuti di un file di database precompilato nel file system del dispositivo.

Precompilare da un asset per app

Per precompilare un database Room da un file di database precompilato che si trova ovunque nella directory assets/ della tua app, chiama la funzione createFromAsset dall'oggetto RoomDatabase.Builder prima di chiamare build:

Room.databaseBuilder<AppDatabase>(appContext, "sample.db")
    .createFromAsset("database/myapp.db")
    .build()

La funzione createFromAsset accetta un argomento stringa che contiene un percorso relativo dalla directory assets/ al file di database precompilato.

Precompilare dal file system

Per precompilare un database Room da un file di database precompilato che si trova ovunque nel file system del dispositivo tranne nella directory assets/ della tua app, chiama la funzione createFromFile dall'oggetto RoomDatabase.Builder prima di chiamare build:

Room.databaseBuilder<AppDatabase>(appContext, "sample.db")
    .createFromFile(File("mypath"))
    .build()

La funzione createFromFile accetta un File argomento per il file di database precompilato. Room crea una copia del file designato anziché aprirlo direttamente, quindi assicurati che la tua app disponga delle autorizzazioni di lettura sul file.

Gestire le migrazioni che includono database precompilati

I file di database precompilati possono anche modificare il modo in cui il database Room gestisce le migrazioni di riserva. In genere, quando le migrazioni distruttive sono abilitate e Room deve eseguire una migrazione senza un percorso di migrazione, Room elimina tutte le tabelle nel database e crea un database vuoto con lo schema specificato per la versione di destinazione. Tuttavia, se includi un file di database precompilato con lo stesso numero della versione di destinazione, Room popola il database appena ricreato con i contenuti del file di database precompilato dopo aver eseguito la migrazione distruttiva.

Per ulteriori informazioni sulle migrazioni dei database Room, consulta Eseguire la migrazione del database Room.

Le sezioni seguenti presentano alcuni esempi di come funziona in pratica.

Esempio: migrazione di riserva con un database precompilato

Supponiamo che:

  • La tua app definisce un database Room nella versione 3.
  • L'istanza del database già installata sul dispositivo è nella versione 2.
  • Esiste un file di database precompilato nella versione 3.
  • Non esiste un percorso di migrazione implementato dalla versione 2 alla versione 3.
  • Le migrazioni distruttive sono abilitate.

// Database class definition declaring version 3.
@Database(entities = [SampleEntity::class], version = 3)
abstract class FallbackAppDatabase : RoomDatabase() {
    // ...
}

fun createFallbackDb(appContext: Context) {
    Room.databaseBuilder<FallbackAppDatabase>(appContext, "sample.db")
        .createFromAsset("database/myapp.db")
        .fallbackToDestructiveMigration()
        .build()
}

Ecco cosa succede in questa situazione:

  1. Poiché il database definito nella tua app è nella versione 3 e l'istanza del database già installata sul dispositivo è nella versione 2, è necessaria una migrazione.
  2. Poiché non esiste un piano di migrazione implementato dalla versione 2 alla versione 3, la migrazione è una migrazione di riserva.
  3. Poiché chiami la fallbackToDestructiveMigration funzione di creazione, la migrazione di riserva è distruttiva. Room elimina l'istanza del database installata sul dispositivo.
  4. Poiché esiste un file di database precompilato nella versione 3, Room ricrea il database e lo popola utilizzando i contenuti del file di database precompilato. Se il file di database precompilato è nella versione 2, Room determina che non corrisponde alla versione di destinazione e non lo utilizza per la migrazione di riserva.

Esempio: migrazione implementata con un database precompilato

Supponiamo invece che la tua app implementi un percorso di migrazione dalla versione 2 alla versione 3:

// Database class definition declaring version 3.
@Database(entities = [SampleEntity::class], version = 3)
abstract class ImplementedAppDatabase : RoomDatabase() {
    // ...
}

// Migration path definition from version 2 to version 3.
val MIGRATION_2_3 = object : Migration(2, 3) {
    override suspend fun migrate(connection: SQLiteConnection) {
        // ...
    }
}

fun createImplementedDb(appContext: Context) {
    Room.databaseBuilder<ImplementedAppDatabase>(appContext, "sample.db")
        .createFromAsset("database/myapp.db")
        .addMigrations(MIGRATION_2_3)
        .build()
}

Ecco cosa succede in questa situazione:

  1. Poiché il database definito nella tua app è nella versione 3 e il database già installato sul dispositivo è nella versione 2, è necessaria una migrazione.
  2. Poiché esiste un percorso di migrazione implementato dalla versione 2 alla versione 3, Room esegue la funzione migrate definita per aggiornare l'istanza del database sul dispositivo alla versione 3, conservando i dati già presenti nel database. Room non utilizza il file di database precompilato, perché Room utilizza i file di database precompilati solo in caso di migrazione di riserva.

Esempio: migrazione in più passaggi con un database precompilato

I file di database precompilati possono anche influire sulle migrazioni costituite da più passaggi. Considera il seguente caso:

  • La tua app definisce un database Room nella versione 4.
  • L'istanza del database già installata sul dispositivo è nella versione 2.
  • Esiste un file di database precompilato nella versione 3.
  • Esiste un percorso di migrazione implementato dalla versione 3 alla versione 4, ma non dalla versione 2 alla versione 3.
  • Le migrazioni distruttive sono abilitate.

// Database class definition declaring version 4.
@Database(entities = [SampleEntity::class], version = 4)
abstract class MultiStepAppDatabase : RoomDatabase() {
    // ...
}

val MIGRATION_3_4 = object : Migration(3, 4) {
    override suspend fun migrate(connection: SQLiteConnection) {
        // ...
    }
}

fun createMultiStepDb(appContext: Context) {
    Room.databaseBuilder<MultiStepAppDatabase>(appContext, "sample.db")
        .createFromAsset("database/myapp.db")
        .addMigrations(MIGRATION_3_4)
        .fallbackToDestructiveMigration()
        .build()
}

Ecco cosa succede in questa situazione:

  1. Poiché il database definito nella tua app è nella versione 4 e l'istanza del database già installata sul dispositivo è nella versione 2, è necessaria una migrazione.
  2. Poiché non esiste un percorso di migrazione implementato dalla versione 2 alla versione 3, la migrazione è una migrazione di riserva.
  3. Poiché chiami la fallbackToDestructiveMigration funzione di creazione, la migrazione di riserva è distruttiva. Room elimina l'istanza del database sul dispositivo.
  4. Poiché esiste un file di database precompilato nella versione 3, Room ricrea il database e lo popola utilizzando i contenuti del file di database precompilato.
  5. Il database installato sul dispositivo è ora nella versione 3. Poiché è ancora inferiore alla versione definita nella tua app, è necessaria un'altra migrazione.
  6. Poiché esiste un percorso di migrazione implementato dalla versione 3 alla versione 4, Room esegue la funzione migrate definita per aggiornare l'istanza del database sul dispositivo alla versione 4, conservando i dati copiati dal file di database precompilato della versione 3.