Precompila il database delle stanze virtuali

A volte potresti voler avviare l'app con un database già caricato con un set di dati specifico. Questa operazione è chiamata precompilazione di un database. Nella stanza 2.2.0 e nelle versioni successive, puoi usare i metodi API per precompilare un database della stanza al momento dell'inizializzazione con i contenuti di un file di database predefinito nel file system del dispositivo.

Precompilazione da un asset per app

Per precompilare un database di stanze da un file di database predefinito che si trova in qualsiasi punto della directory assets/ dell'app, chiama il metodo createFromAsset() dall'oggetto RoomDatabase.Builder prima di chiamare build():

Kotlin

Room.databaseBuilder(appContext, AppDatabase::class.java, "Sample.db")
    .createFromAsset("database/myapp.db")
    .build()

Java

Room.databaseBuilder(appContext, AppDatabase.class, "Sample.db")
    .createFromAsset("database/myapp.db")
    .build();

Il metodo createFromAsset() accetta un argomento stringa che contiene un percorso relativo dalla directory assets/ al file di database predefinito.

Precompilazione dal file system

Per precompilare un database di una stanza virtuale a partire da un file di database predefinito che si trova in qualsiasi punto del file system del dispositivo, ad eccezione della directory assets/ dell'app, chiama il metodo createFromFile() dall'oggetto RoomDatabase.Builder prima di chiamare build():

Kotlin

Room.databaseBuilder(appContext, AppDatabase::class.java, "Sample.db")
    .createFromFile(File("mypath"))
    .build()

Java

Room.databaseBuilder(appContext, AppDatabase.class, "Sample.db")
    .createFromFile(new File("mypath"))
    .build();

Il metodo createFromFile() accetta un argomento File per il file di database predefinito. La stanza virtuale crea una copia del file designato anziché aprirlo direttamente, quindi assicurati che la tua app disponga delle autorizzazioni di lettura per il file.

Gestire le migrazioni che includono database predefiniti

I file di database predefiniti possono anche modificare il modo in cui il database della stanza gestisce le migrazioni di riserva. In genere, quando sono abilitate migrazioni distruttive e la 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 predefinito con lo stesso numero della versione di destinazione, dopo aver eseguito la migrazione distruttiva, Room tenta di completare il database appena ricreato con i contenuti del file di database predefinito.

Per saperne di più sulle migrazioni dei database delle stanze, vedi Migrazione dei database delle stanze.

Le seguenti sezioni presentano alcuni esempi pratici di questa procedura.

Esempio: migrazione di riserva con un database predefinito

Supponiamo che:

  • L'app definisce un database delle stanze nella versione 3.
  • L'istanza di database già installata sul dispositivo è alla versione 2.
  • È presente un file di database predefinito nella versione 3.
  • Non è stato implementato alcun percorso di migrazione dalla versione 2 alla versione 3.
  • Le migrazioni distruttive sono abilitate.

Kotlin

// Database class definition declaring version 3.
@Database(version = 3)
abstract class AppDatabase : RoomDatabase() {
    ...
}

// Destructive migrations are enabled and a prepackaged database
// is provided.
Room.databaseBuilder(appContext, AppDatabase::class.java, "Sample.db")
    .createFromAsset("database/myapp.db")
    .fallbackToDestructiveMigration()
    .build()

Java

// Database class definition declaring version 3.
@Database(version = 3)
public abstract class AppDatabase extends RoomDatabase {
    ...
}

// Destructive migrations are enabled and a prepackaged database
// is provided.
Room.databaseBuilder(appContext, AppDatabase.class, "Sample.db")
    .createFromAsset("database/myapp.db")
    .fallbackToDestructiveMigration()
    .build();

Ecco cosa succede in questa situazione:

  1. Poiché il database definito nella tua app è alla versione 3 e l'istanza di database già installata sul dispositivo è la versione 2, è necessaria una migrazione.
  2. Poiché non è stato implementato un piano di migrazione dalla versione 2 alla 3, la migrazione è una migrazione di riserva.
  3. Poiché viene chiamato il metodo del builder di fallbackToDestructiveMigration(), la migrazione di riserva è distruttiva. La stanza virtuale elimina l'istanza di database installata sul dispositivo.
  4. Poiché esiste un file di database predefinito nella versione 3, la stanza virtuale ricrea il database e lo compila utilizzando i contenuti del file di database predefinito. Se, invece, il file del database predefinito fosse la versione 2, Room noterebbe che non corrisponde alla versione di destinazione e non lo userebbe nell'ambito della migrazione di riserva.

Esempio: implementazione della migrazione con un database predefinito

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

Kotlin

// Database class definition declaring version 3.
@Database(version = 3)
abstract class AppDatabase : RoomDatabase() {
    ...
}

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

// A prepackaged database is provided.
Room.databaseBuilder(appContext, AppDatabase::class.java, "Sample.db")
    .createFromAsset("database/myapp.db")
    .addMigrations(MIGRATION_2_3)
    .build()

Java

// Database class definition declaring version 3.
@Database(version = 3)
public abstract class AppDatabase extends RoomDatabase {
    ...
}

// Migration path definition from version 2 to version 3.
static final Migration MIGRATION_2_3 = new Migration(2, 3) {
    @Override
    public void migrate(SupportSQLiteDatabase database) {
        ...
    }
};

// A prepackaged database is provided.
Room.databaseBuilder(appContext, AppDatabase.class, "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 è alla versione 3 e il database già installato sul dispositivo è alla versione 2, è necessaria una migrazione.
  2. Poiché è stato implementato un percorso di migrazione dalla versione 2 alla versione 3, la stanza virtuale esegue il metodo migrate() definito per aggiornare l'istanza di database sul dispositivo alla versione 3, mantenendo i dati già presenti nel database. La Room non usa il file di database predefinito, perché usa i file di database preconfezionati solo nel caso di una migrazione di riserva.

Esempio: migrazione di più passaggi con un database predefinito

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

  • L'app definisce un database delle stanze nella versione 4.
  • L'istanza di database già installata sul dispositivo è alla versione 2.
  • È presente un file di database predefinito nella versione 3.
  • È implementato un percorso di migrazione dalla versione 3 alla versione 4, ma non dalla versione 2 alla versione 3.
  • Le migrazioni distruttive sono abilitate.

Kotlin

// Database class definition declaring version 4.
@Database(version = 4)
abstract class AppDatabase : RoomDatabase() {
    ...
}

// Migration path definition from version 3 to version 4.
val MIGRATION_3_4 = object : Migration(3, 4) {
    override fun migrate(database: SupportSQLiteDatabase) {
        ...
    }
}

// Destructive migrations are enabled and a prepackaged database is
// provided.
Room.databaseBuilder(appContext, AppDatabase::class.java, "Sample.db")
    .createFromAsset("database/myapp.db")
    .addMigrations(MIGRATION_3_4)
    .fallbackToDestructiveMigration()
    .build()

Java

// Database class definition declaring version 4.
@Database(version = 4)
public abstract class AppDatabase extends RoomDatabase {
    ...
}

// Migration path definition from version 3 to version 4.
static final Migration MIGRATION_3_4 = new Migration(3, 4) {
    @Override
    public void migrate(SupportSQLiteDatabase database) {
        ...
    }
};

// Destructive migrations are enabled and a prepackaged database is
// provided.
Room.databaseBuilder(appContext, AppDatabase.class, "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 è alla versione 4 e l'istanza di database già installata sul dispositivo è la versione 2, è necessaria una migrazione.
  2. Poiché non è stato implementato alcun percorso di migrazione dalla versione 2 alla versione 3, la migrazione è una migrazione di riserva.
  3. Poiché viene chiamato il metodo del builder di fallbackToDestructiveMigration(), la migrazione di riserva è distruttiva. La stanza virtuale elimina l'istanza di database sul dispositivo.
  4. Poiché esiste un file di database predefinito nella versione 3, la stanza virtuale ricrea il database e lo compila utilizzando i contenuti del file di database predefinito.
  5. Il database installato sul dispositivo è ora alla 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, la stanza virtuale esegue il metodo migrate() definito per aggiornare l'istanza di database sul dispositivo alla versione 4, mantenendo i dati copiati dal file di database predefinito della versione 3.

Risorse aggiuntive

Per scoprire di più sulla precompilazione di un database delle stanze, consulta le seguenti risorse aggiuntive.

Video

Blog