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

Testar com o Kotlin Multiplatform
O Kotlin Multiplatform permite compartilhar a camada de banco de dados com outras plataformas. Saiba como configurar e trabalhar com o Room Database no KMP

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 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.

@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 @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 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.

Amostras