Salvar dados em um banco de dados local usando o Room Parte do Android Jetpack

A persistência de dados local pode ser muito útil para apps que processam quantidades não triviais de dados estruturados. O caso de uso mais comum é armazenar em cache partes importantes de dados para que, quando o dispositivo não puder acessar a rede, o usuário ainda consiga ter acesso a esse conteúdo off-line.

A biblioteca de persistência Room oferece uma camada de abstração sobre o SQLite para permitir acesso fluente ao banco de dados, aproveitando toda a capacidade do SQLite. O Room oferece principalmente estes benefícios:

  • Verificação de consultas SQL durante a compilação.
  • Anotações de conveniência que minimizam o código boilerplate repetitivo e propenso a erros.
  • Caminhos de migração de banco de dados simplificados.

Dessa forma, recomendamos que você use o Room em vez de usar diretamente as APIs SQLite.

Configurar

Para usar o Room no app, adicione as dependências abaixo ao arquivo build.gradle do app:

Groovy

dependencies {
    def room_version = "2.6.1"

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

    // To use Kotlin annotation processing tool (kapt)
    kapt "androidx.room:room-compiler:$room_version"
    // To use Kotlin Symbol Processing (KSP)
    ksp "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"
}

Kotlin

dependencies {
    val room_version = "2.6.1"

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

    // To use Kotlin annotation processing tool (kapt)
    kapt("androidx.room:room-compiler:$room_version")
    // To use Kotlin Symbol Processing (KSP)
    ksp("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")
}

Principais componentes

Existem três componentes principais no Room:

A classe do banco de dados fornece ao app instâncias dos DAOs associadas ao banco de dados. O app pode usar os DAOs para extrair dados do banco de dados como instâncias dos objetos da entidade de dados associados. Ele também pode usar as entidades de dados definidas para atualizar linhas das tabelas correspondentes ou criar novas linhas para inserção. A Figura 1 mostra a relação entre os diferentes componentes do Room.

Figura 1. Diagrama da arquitetura da biblioteca do Room.

Exemplo de implementação

Nesta seção, apresentamos um exemplo de implementação de um banco de dados do Room com uma única entidade de dados e um único DAO.

Entidade de dados

O código abaixo define uma entidade de dados User. Cada instância de User representa uma linha em uma tabela user no banco de dados do 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;
}

Para saber mais sobre entidades de dados no Room, consulte Como definir dados usando entidades do Room.

Objeto de acesso a dados (DAO)

O código abaixo define um DAO com o nome UserDao. O UserDao fornece os métodos que o restante do app usa para interagir com os dados na tabela 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);
}

Para saber mais sobre os DAOs, consulte Como acessar dados usando DAOs do Room.

Banco de dados

O código abaixo define uma classe AppDatabase para armazenar o banco de dados. A classe AppDatabase define a configuração do banco de dados e serve como o ponto de acesso principal do app aos dados persistidos. A classe de banco de dados precisa atender a estas condições:

  • A classe precisa ter uma anotação @Database que inclua uma matriz entities listando todas as entidades de dados associados ao banco de dados.
  • A classe precisa ser abstrata e estender RoomDatabase.
  • Para cada classe DAO associada ao banco de dados, a classe de banco de dados precisa definir um método abstrato que não tenha argumentos e retorne uma instância da 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();
}

Observação: caso o app seja executado em um único processo, siga o padrão singleton ao instanciar um objeto AppDatabase. Cada instância RoomDatabase é bastante cara do ponto de vista computacional e raramente é necessário ter acesso a várias instâncias em um único processo.

Caso o app seja executado em vários processos, inclua enableMultiInstanceInvalidation() ao invocar o builder do banco de dados. Dessa forma, quando você tiver uma instância de AppDatabase em cada processo, é possível invalidar o arquivo do banco de dados compartilhado em um processo. Essa invalidação é automaticamente propagada para as instâncias de AppDatabase em outros processos.

Uso

Depois de definir a entidade de dados, o DAO e o objeto de banco de dados, é possível usar o código abaixo para criar uma instância do banco de dados:

Kotlin

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

Java

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

Em seguida, use os métodos abstratos da classe AppDatabase para acessar uma instância do DAO. Como alternativa, é possível usar os métodos da instância do DAO para interagir com o banco de dados:

Kotlin

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

Java

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

Outros recursos

Para saber mais sobre o Room, consulte os recursos abaixo.

Exemplos

Codelabs

Blogs