Room é uma biblioteca para trabalhar com SQLite no Android, parte do Jetpack. Ela fornece uma camada de abstração sobre o SQLite puro, automatizando a criação de tabelas, a execução de consultas e a conversão de dados em objetos Kotlin e Java. De acordo com o Android Developers, o Room compila consultas SQL em tempo de compilação, verificando a correção da sintaxe e as relações entre Entity e tabelas.
Pontos principais
Room é uma biblioteca de persistência do Android Jetpack que fornece mapeamento objeto-relacional para SQLite. O Room resolve três problemas principais do SQLite puro: escrever grandes quantidades de código boilerplate para criar tabelas, a falta de verificação de consultas SQL em tempo de compilação e a conversão manual de Cursor para objetos.
A biblioteca usa um processador de anotações (kapt ou KSP) que gera a implementação das classes abstratas RoomDatabase e DAO em tempo de compilação. Isso garante que erros de sintaxe em SQL e incompatibilidades de tipo sejam descobertos antes de executar o aplicativo, em vez de em tempo de execução após a publicação no Google Play.
De acordo com o Google I/O 2023, o Room é usado em 68% dos aplicativos Android que trabalham com dados locais. É o padrão para armazenamento de dados no dispositivo, recomendado pelo Google para todos os novos projetos — em vez dos obsoletos SQLiteOpenHelper e ContentProvider.
Integre o Room em projetos que exigem cache local de dados do servidor, modo offline ou armazenamento de dados estruturados do usuário com a capacidade de executar consultas SQL complexas.
O Room faz parte do Android Jetpack e é oficialmente recomendado pelo Google para todos os novos projetos que trabalham com dados locais. Ao contrário do Realm ou ObjectBox, o Room usa SQLite nativo, garantindo compatibilidade com qualquer ferramenta de banco de dados de terceiros — desde o DB Browser até o DataGrip. Os desenvolvedores podem abrir o arquivo .db do aplicativo e executar consultas SQL diretamente, simplificando a depuração e a análise de dados durante o desenvolvimento.
Entity é uma classe de dados anotada com @Entity que o Room transforma em uma tabela de banco de dados. Cada campo da classe se torna uma coluna da tabela, e cada instância se torna uma linha. O Room usa reflexão para acessar campos, portanto, a anotação @PrimaryKey é necessária para um identificador obrigatório.
A anotação @Entity informa ao Room que a classe é uma tabela. O parâmetro tableName define o nome da tabela se for diferente do nome da classe. @PrimaryKey define a chave primária com suporte à geração automática via autoGenerate = true.
@Entity(tableName = "users")
data class User(
@PrimaryKey(autoGenerate = true)
val id: Int = 0,
@ColumnInfo(name = "full_name")
val name: String,
@Ignore
val tempData: String?
)
@ColumnInfo especifica o nome da coluna na tabela se for diferente do nome do campo em Kotlin. @Ignore exclui um campo da tabela — ele não será salvo no banco de dados. @ForeignKey descreve chaves estrangeiras para relacionamentos entre tabelas com operações em cascata ao excluir ou atualizar.
O Room oferece suporte a objetos aninhados por meio da anotação @Embedded. Os campos da classe aninhada são expandidos em colunas da tabela pai com um prefixo para evitar conflitos de nomes. Por exemplo, uma classe Address com campos city e street incorporada em User criará as colunas address_city e address_street na tabela users, eliminando a necessidade de tabelas separadas para objetos de valor simples.
O Room suporta apenas tipos primitivos e seus wrappers. Para armazenar listas, Date ou tipos personalizados, use @TypeConverter — métodos estáticos para converter entre um tipo personalizado e um primitivo SQLite, por exemplo, entre List e uma string JSON.
DAO (Objeto de Acesso a Dados) é uma interface ou classe abstrata anotada com @Dao que contém métodos para acesso a dados. Cada método é anotado com uma operação SQL: @Insert, @Update, @Delete ou @Query com uma consulta SQL explícita.
A anotação @Query recebe uma string SQL que o Room verifica em tempo de compilação quanto à correção da sintaxe e correspondência dos nomes das colunas com os campos Entity. O Room oferece suporte a consultas parametrizadas por meio da sintaxe :paramName.
@Dao
interface UserDao {
@Query("SELECT * FROM users WHERE id = :userId")
suspend fun getUserById(userId: Int): User?
@Insert(onConflict = OnConflictStrategy.REPLACE)
suspend fun insertUser(user: User)
@Query("SELECT * FROM users ORDER BY name ASC")
fun getAllUsers(): Flow<List<User>>
}
@Insert oferece suporte a estratégias OnConflictStrategy para lidar com conflitos ao inserir registros duplicados. Flow como tipo de retorno fornece atualizações reativas da interface do usuário em cada alteração de dados na tabela — a assinatura reinicia automaticamente em qualquer INSERT, UPDATE ou DELETE.
A anotação @Transaction garante a execução atômica de várias operações em um único bloco transacional. O Room bloqueia o banco de dados durante a execução, prevenindo condições de corrida durante o acesso concorrente de várias threads.
RoomDatabase é uma classe abstrata que combina Entity e DAO em um único ponto de acesso ao banco de dados. Ela é criada através de Room.databaseBuilder com a versão do esquema e uma lista de classes Entity. A instância do banco de dados deve ser criada como um singleton usando um delegate lazy para evitar múltiplas conexões.
Uma migração no Room é uma classe Migration que descreve um script SQL para a transição de uma versão antiga do esquema para uma nova. Se uma migração não for fornecida quando o esquema muda, o Room lança uma IllegalStateException. Isso protege contra perda acidental de dados do usuário ao atualizar o aplicativo.
val migration_1_2 = object : Migration(1, 2) {
override fun migrate(db: SupportSQLiteDatabase) {
db.execSQL("ALTER TABLE users ADD COLUMN age INTEGER NOT NULL DEFAULT 0")
}
}
val db = Room.databaseBuilder(
getApplication(),
AppDatabase::class.java,
"app_database"
).addMigrations(migration_1_2)
.build()
Para desenvolvimento, você pode usar fallbackToDestructiveMigration, que exclui o banco de dados antigo e cria um novo em caso de incompatibilidade de versão. Este modo é destinado apenas para depuração — versões de produção devem incluir migrações adequadas.
Para testar o banco de dados, o Room fornece uma classe especial Room.inMemoryTestBuilder que cria um banco de dados em memória sem salvar em disco. Após a conclusão de cada teste, o banco de dados é automaticamente destruído, garantindo o isolamento completo dos cenários de teste. Combinado com a biblioteca android-arch-core-testing, os desenvolvedores podem gerenciar o ciclo de vida do banco de dados e verificar a correção das migrações sem precisar limpar o estado manualmente.
O desempenho do Room depende diretamente da estrutura das consultas e dos índices. Para analisar consultas lentas, o Room fornece o sinalizador enableQueryCallback, que registra todas as consultas SQL com o tempo de execução. Os desenvolvedores podem usar este log para encontrar consultas que levam mais de 100 milissegundos e otimizá-las adicionando índices compostos através da anotação @Index em @Entity ou reescrevendo subconsultas como operações JOIN diretas usando @Relation.
O Room também suporta criptografia de banco de dados via SQLCipher. Adicionar a biblioteca net.zetetic:android-database-sqlcipher e usar SupportFactory em vez do padrão fornece criptografia transparente de todos os dados em disco sem alterar as consultas DAO ou a estrutura Entity. Isso é necessário para aplicativos que lidam com dados pessoais de usuários e está em conformidade com o GDPR e a Lei Federal Russa 152-FZ sobre proteção de dados pessoais. A senha de criptografia pode ser armazenada no Android Keystore para proteção contra extração através de ferramentas em dispositivos rooteados.
Room suporta nativamente Kotlin Coroutines a partir da versão 2.1. Os métodos DAO podem ser funções suspend que executam consultas em thread de segundo plano sem bloquear a thread principal. O Room gerencia automaticamente os dispatchers, usando Dispatchers.IO para consultas de leitura e escrita.
Para consultas reativas, o Room retorna um Flow — um fluxo de dados frio que emite um novo valor a cada alteração na tabela afetada. O ViewModel assina o Flow via stateIn ou collect, fornecendo atualizações automáticas da interface do usuário sem notificar manualmente o adaptador.
O Room também suporta Paging 3 através de uma implementação especial de PagingSource que carrega dados página por página do SQLite. Isso é eficiente para grandes listas com milhares de registros: o Paging 3 carrega apenas as linhas visíveis na tela e as atualiza automaticamente em alterações no banco de dados.
Use o Paging 3 com Room para exibir feeds de notícias, logs de operações ou listas de produtos com acesso offline e rolagem infinita.
Perguntas frequentes
Room automatiza a criação de tabelas, a conversão de Cursor para objetos e a verificação de SQL em tempo de compilação. SQLiteOpenHelper requer escrever o esquema manualmente, manipular Cursor e não tem verificação de consultas antes de executar o aplicativo, o que aumenta o risco de erros.
Sim, ao alterar uma Entity (adicionar/remover um campo, alterar um tipo), uma migração é necessária. Sem ela, o Room lança uma IllegalStateException na inicialização. Para desenvolvimento, você pode ativar fallbackToDestructiveMigration, mas versões de produção exigem scripts de migração corretos.
O Room suporta @ForeignKey para operações em cascata e @Relation para objetos aninhados. Para consultas JOIN complexas, use a anotação @Transaction com @Query retornando um POJO com entidades aninhadas via @Embedded e @Relation.
Sim, o Room é totalmente compatível com Java. Em vez de funções suspend, use LiveData ou RxJava Observable; em vez de Flow, use LiveData. O Room com Java suporta todas as mesmas anotações, mas requer mais código boilerplate para operações assíncronas.
O Room suporta criptografia via SQLCipher da Zetetic. Em vez de Room.databaseBuilder, use SupportFactory da biblioteca net.zetetic:android-database-sqlcipher, passando a senha de criptografia. Todos os dados em disco serão criptografados de forma transparente para as consultas DAO.
Resumo
Vamos desenvolver um aplicativo móvel chave na mão
A IT Sectr cria aplicativos para iOS e Android para startups e empresas desde 2017. Nós vamos aconselhá-lo e propor a melhor solução.
Leia também