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 relevantes de dados para que, quando o dispositivo não conseguir acessar a rede, os usuários ainda possam navegar pelo conteúdo off-line.
A biblioteca de persistência do 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.
Recomendamos usar o Room em vez de usar diretamente as APIs SQLite.
Configuração
Para usar o Room no app, adicione as dependências abaixo ao arquivo build.gradle.kts do módulo. O Room 3.0 exige o KSP para o processamento de anotações.
Kotlin
dependencies { val room_version = "3.0.0" implementation("androidx.room3:room3-runtime:$room_version") ksp("androidx.room3:room3-compiler:$room_version") }
Groovy
dependencies { def room_version = "3.0.0" implementation "androidx.room3:room3-runtime:$room_version" ksp "androidx.room3:room3-compiler:$room_version" }
Principais componentes
Existem três componentes principais no Room:
- A classe de banco de dados que contém o banco de dados e serve como o ponto de acesso principal para a conexão com os dados persistidos do app.
- As entidades de dados que representam tabelas no banco de dados do app.
- Os objetos de acesso a dados (DAOs, na sigla em inglês), que fornecem funções que o app pode usar para consultar, atualizar, inserir e excluir dados do banco de dados.
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.
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.
@Entity data class User( @PrimaryKey val uid: Int, @ColumnInfo(name = "first_name") val firstName: String, @ColumnInfo(name = "last_name") val lastName: String )
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. UserDao fornece as funções que o restante do app usa para interagir com os dados na tabela user.
@Dao interface UserDao { @Query("SELECT * FROM user") suspend fun getAll(): List<User> @Query("SELECT * FROM user WHERE uid IN (:userIds)") suspend fun loadAllByIds(userIds: IntArray): List<User> @Query( """ SELECT * FROM user WHERE first_name LIKE :first AND last_name LIKE :last LIMIT 1 """ ) suspend fun findByName(first: String, last: String): User @Insert suspend fun insertAll(vararg users: User) @Delete suspend fun 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
@Databaseque inclua uma matrizentitieslistando 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 uma função abstrata que não tenha argumentos e retorne uma instância da classe DAO.
@Database(entities = [User::class], version = 1) abstract class AppDatabase : RoomDatabase() { abstract fun userDao(): UserDao }
Observação : caso o app seja executado em um único processo, siga o
padrão singleton ao instanciar um AppDatabase
objeto. 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:
val db = Room.databaseBuilder<AppDatabase>(applicationContext, "database-name") .setDriver(AndroidSQLiteDriver()) .build()
Em seguida, use as funções abstratas da classe AppDatabase para acessar uma instância do DAO. Como alternativa, é possível usar as funções da instância do DAO para interagir com o banco de dados:
val userDao = db.userDao() val users: List<User> = userDao.getAll()
Outros recursos
Para saber mais sobre o Room, consulte os recursos abaixo.