Salvare i dati in un database locale utilizzando Room   Componente di Android Jetpack.

Le app che gestiscono quantità non trascurabili di dati strutturati possono trarre grandi vantaggi dalla memorizzazione locale di questi dati. Il caso d'uso più comune è memorizzare nella cache frammenti di dati pertinenti in modo che, quando il dispositivo non può accedere alla rete, l'utente possa comunque sfogliare i contenuti offline.

La libreria di persistenza Room fornisce un livello di astrazione su SQLite per consentire un accesso fluido al database sfruttando al contempo tutta la potenza di SQLite. In particolare, Room offre i seguenti vantaggi:

  • Verifica in fase di compilazione delle query SQL.
  • Annotazioni di praticità che riducono al minimo il codice boilerplate ripetitivo e soggetto a errori.
  • Percorsi di migrazione del database semplificati.

Per questi motivi, ti consigliamo vivamente di utilizzare Room anziché utilizzare direttamente le API SQLite.

Configura

Per utilizzare Room nella tua app, aggiungi le seguenti dipendenze al file build.gradle dell'app.

Kotlin

dependencies {
    val room_version = "2.6.1"

    implementation("androidx.room:room-runtime:$room_version")

    // If this project uses any Kotlin source, use Kotlin Symbol Processing (KSP)
    // See Add the KSP plugin to your project
    ksp("androidx.room:room-compiler:$room_version")

    // If this project only uses Java source, use the Java annotationProcessor
    // No additional plugins are necessary
    annotationProcessor("androidx.room:room-compiler:$room_version")

    // optional - Kotlin Extensions and Coroutines support for Room
    implementation("androidx.room:room-ktx:$room_version")

    // optional - RxJava2 support for Room
    implementation("androidx.room:room-rxjava2:$room_version")

    // optional - RxJava3 support for Room
    implementation("androidx.room:room-rxjava3:$room_version")

    // optional - Guava support for Room, including Optional and ListenableFuture
    implementation("androidx.room:room-guava:$room_version")

    // optional - Test helpers
    testImplementation("androidx.room:room-testing:$room_version")

    // optional - Paging 3 Integration
    implementation("androidx.room:room-paging:$room_version")
}

Groovy

dependencies {
    def room_version = "2.6.1"

    implementation "androidx.room:room-runtime:$room_version"

    // If this project uses any Kotlin source, use Kotlin Symbol Processing (KSP)
    // See KSP Quickstart to add KSP to your build
    ksp "androidx.room:room-compiler:$room_version"

    // If this project only uses Java source, use the Java annotationProcessor
    // No additional plugins are necessary
    annotationProcessor "androidx.room:room-compiler:$room_version"

    // optional - RxJava2 support for Room
    implementation "androidx.room:room-rxjava2:$room_version"

    // optional - RxJava3 support for Room
    implementation "androidx.room:room-rxjava3:$room_version"

    // optional - Guava support for Room, including Optional and ListenableFuture
    implementation "androidx.room:room-guava:$room_version"

    // optional - Test helpers
    testImplementation "androidx.room:room-testing:$room_version"

    // optional - Paging 3 Integration
    implementation "androidx.room:room-paging:$room_version"
}

Componenti principali

Room è composto da tre componenti principali:

  • La classe database che contiene il database e funge da punto di accesso principale per la connessione sottostante ai dati permanenti della tua app.
  • Entità di dati che rappresentano le tabelle nel database della tua app.
  • Oggetti di accesso ai dati (DAO) che forniscono metodi che la tua app può utilizzare per eseguire query, aggiornare, inserire ed eliminare i dati nel database.

La classe del database fornisce all'app istanze delle DAO associate a quel database. A sua volta, l'app può utilizzare i DAO per recuperare i dati dal database come istanze degli oggetti entità di dati associati. L'app può anche utilizzare le entità di dati definite per aggiornare le righe delle tabelle corrispondenti o per creare nuove righe per l'inserimento. La Figura 1 illustra la relazione tra i diversi componenti di Room.

Figura 1. Diagramma dell'architettura della raccolta Room.

Esempio di implementazione

Questa sezione presenta un esempio di implementazione di un database Room con una singola entità di dati e un singolo DAO.

Entità dati

Il codice seguente definisce un'entità di dati User. Ogni istanza di User rappresenta una riga in una tabella user nel database dell'app.

Kotlin

@Entity
data class User(
    @PrimaryKey val uid: Int,
    @ColumnInfo(name = "first_name") val firstName: String?,
    @ColumnInfo(name = "last_name") val lastName: String?
)

Java

@Entity
public class User {
    @PrimaryKey
    public int uid;

    @ColumnInfo(name = "first_name")
    public String firstName;

    @ColumnInfo(name = "last_name")
    public String lastName;
}

Per scoprire di più sulle entità di dati in Room, consulta la sezione Definire i dati utilizzando le entità Room.

Oggetto di accesso ai dati (DAO)

Il codice seguente definisce un DAO chiamato UserDao. UserDao fornisce i metodi utilizzati dal resto dell'app per interagire con i dati nella tabella user.

Kotlin

@Dao
interface UserDao {
    @Query("SELECT * FROM user")
    fun getAll(): List<User>

    @Query("SELECT * FROM user WHERE uid IN (:userIds)")
    fun loadAllByIds(userIds: IntArray): List<User>

    @Query("SELECT * FROM user WHERE first_name LIKE :first AND " +
           "last_name LIKE :last LIMIT 1")
    fun findByName(first: String, last: String): User

    @Insert
    fun insertAll(vararg users: User)

    @Delete
    fun delete(user: User)
}

Java

@Dao
public interface UserDao {
    @Query("SELECT * FROM user")
    List<User> getAll();

    @Query("SELECT * FROM user WHERE uid IN (:userIds)")
    List<User> loadAllByIds(int[] userIds);

    @Query("SELECT * FROM user WHERE first_name LIKE :first AND " +
           "last_name LIKE :last LIMIT 1")
    User findByName(String first, String last);

    @Insert
    void insertAll(User... users);

    @Delete
    void delete(User user);
}

Per scoprire di più sulle DAO, consulta Accedere ai dati utilizzando le DAO di Room.

Database

Il codice seguente definisce una classe AppDatabase per contenere il database. AppDatabase definisce la configurazione del database e funge da punto di accesso principale dell'app ai dati persistenti. La classe del database deve soddisfare le seguenti condizioni:

  • La classe deve essere annotata con un'annotazione @Database che include un array entities che elenca tutte le entità di dati associate al database.
  • La classe deve essere una classe astratta che estende RoomDatabase.
  • Per ogni classe DAO associata al database, la classe del database deve definire un metodo astratto senza argomenti che restituisce un'istanza della classe DAO.

Kotlin

@Database(entities = [User::class], version = 1)
abstract class AppDatabase : RoomDatabase() {
    abstract fun userDao(): UserDao
}

Java

@Database(entities = {User.class}, version = 1)
public abstract class AppDatabase extends RoomDatabase {
    public abstract UserDao userDao();
}

Nota: se la tua app viene eseguita in un'unica operazione, devi seguire il pattern di progettazione singleton quando esegui l'inizializzazione di un oggetto AppDatabase. Ogni istanza RoomDatabase è piuttosto costosa e raramente è necessario accedere a più istanze all'interno di un'unica operazione.

Se la tua app viene eseguita in più processi, includi enableMultiInstanceInvalidation() nell'invocazione del generatore di database. In questo modo, quando hai un'istanza di AppDatabase in ogni processo, puoi invalidare il file del database condiviso in un processo, e questa invalidazione si propaga automaticamente alle istanze di AppDatabase all'interno di altri processi.

Utilizzo

Dopo aver definito l'entità di dati, il DAO e l'oggetto database, puoi utilizzare il seguente codice per creare un'istanza del database:

Kotlin

val db = Room.databaseBuilder(
            applicationContext,
            AppDatabase::class.java, "database-name"
        ).build()

Java

AppDatabase db = Room.databaseBuilder(getApplicationContext(),
        AppDatabase.class, "database-name").build();

Puoi quindi utilizzare i metodi astratti di AppDatabase per ottenere un'istanza del DAO. A tua volta, puoi utilizzare i metodi dell'istanza DAO per interagire con il database:

Kotlin

val userDao = db.userDao()
val users: List<User> = userDao.getAll()

Java

UserDao userDao = db.userDao();
List<User> users = userDao.getAll();

Risorse aggiuntive

Per scoprire di più su Room, consulta le seguenti risorse aggiuntive:

Campioni

Codelab

Blog