A Room 3.0 é uma atualização de versão principal que faz a transição da biblioteca para priorizar o Kotlin. Ela oferece suporte ao Kotlin Multiplatform (KMP), exige o Kotlin Symbol Processing (KSP) e aplica corrotinas para operações assíncronas.
Para evitar problemas de compatibilidade com apps Room 2.x e dependências transitivas, a Room 3.0 reside em um novo pacote: androidx.room3.
Este guia descreve as etapas necessárias para migrar sua implementação atual da Room 2.x para a Room 3.0.
Principais mudanças na Room 3.0
Antes de iniciar a migração, familiarize-se com as principais diferenças:
- Novo pacote e artefatos: todas as classes residem em
androidx.room3. Os artefatos usam oroom3prefixo, comoandroidx.room3:room3-runtime. - Somente Kotlin e KSP: a Room 3.0 não oferece suporte à geração de código Java. Use o KSP em vez de processadores de anotações KAPT ou Java. A Room 3.0 ainda oferece suporte a fontes Java como entradas.
- Corrotinas primeiro: as funções DAO precisam ser
suspendfunções, exceto para tipos observáveis.CoroutineContextsubstitui executores. - Sem SupportSQLite:
SQLiteDriveras APIs fazem o backup da Room. A Room removeSupportSQLiteDatabasedas APIs principais. - Mudanças na API: as migrações e os callbacks de banco de dados usam
SQLiteConnectionem vez deSupportSQLiteDatabase. - Conversores para tipos reativos: os tipos de retorno RxJava, LiveData, Guava e Paging
exigem que você registre
@DaoReturnTypeConverters.
Recomendamos migrar em duas fases distintas: primeiro, preparar e modernizar sua base de código na Room 2.x e, em seguida, mudar para a Room 3.0.
Preparar e modernizar na Room 2.x
Antes de migrar para a Room 3.0, você pode realizar a maior parte do trabalho de modernização atualizando para a versão atual da Room 2.x, como a Room 2.8. A Room 2.8 oferece suporte ao Kotlin Multiplatform (KMP) e inclui muitas APIs de driver que a Room 3.0 usa.
Atualizar para a Room 2.8 e versões mais recentes
Atualize a configuração de build para usar a versão atual da Room 2.x:
[versions]
room2 = "2.8.4" # Use the current Room 2.8 version
[libraries]
androidx-room-runtime = { module = "androidx.room:room-runtime", version.ref = "room2" }
androidx-room-compiler = { module = "androidx.room:room-compiler", version.ref = "room2" }
Migrar do KAPT para o KSP
A Room 3.0 não oferece suporte a processadores de anotações Java ou KAPT. Você precisa usar o Kotlin Symbol Processing (KSP). É possível fazer essa transição enquanto ainda estiver na Room 2.x.
No
build.gradle.ktsdo módulo, aplique o plug-in KSP:plugins { id("com.google.devtools.ksp") version "<ksp_version>" }Verifique se a versão do KSP é compatível com a versão do Kotlin.
Substitua
kaptouannotationProcessorporksppara a dependência do compilador da Room:dependencies { implementation(libs.androidx.room.runtime) ksp(libs.androidx.room.compiler) }
Adotar corrotinas
A Room 3.0 exige corrotinas para operações assíncronas.
- Atualize seus DAOs: a menos que retornem um tipo reativo observável, como
Flowou tipos RxJava, todas as funções DAO precisam ser funçõessuspend.
// Before (Blocking)
@Dao
interface UserDao {
@Query("SELECT * FROM User")
fun getAll(): List<User>
}
// After (Suspend)
@Dao
interface UserDao {
@Query("SELECT * FROM User")
suspend fun getAll(): List<User>
}
- Se você configurou seu
RoomDatabasecom umExecutorpersonalizado para realizar operações de banco de dados, migre paraCoroutineContextusandosetQueryCoroutineContextno builder:
Room.databaseBuilder<AppDatabase>(context, "db")
.setQueryCoroutineContext(Dispatchers.IO)
.build()
Adotar APIs de driver e evitar o Support SQLite
A Room 3.0 tem suporte total do SQLiteDriver e não oferece mais suporte ao SupportSQLiteDatabase nas APIs principais.
Se você não chamar setDriver para definir um SQLiteDriver no builder do banco de dados, a Room 2.8 vai operar em um modo de compatibilidade em que as APIs do Support SQLite e do Driver funcionam. Esse modo de compatibilidade permite converter sua base de código de forma incremental antes de ativar o driver.
- Converter migrações: migre suas
MigrationeAutoMigrationSpecsubclasses para usarSQLiteConnectionem vez deSupportSQLiteDatabase.
// Before (SupportSQLiteDatabase)
val MIGRATION_1_2 = object : Migration(1, 2) {
override fun migrate(db: SupportSQLiteDatabase) {
db.execSQL("ALTER TABLE User ADD COLUMN age INTEGER DEFAULT 0 NOT NULL")
}
}
// After (SQLiteConnection - Room 2.8)
import androidx.sqlite.SQLiteConnection
import androidx.sqlite.execSQL
val MIGRATION_1_2 = object : Migration(1, 2) {
override fun migrate(connection: SQLiteConnection) {
connection.execSQL(
"ALTER TABLE User ADD COLUMN age INTEGER DEFAULT 0 NOT NULL"
)
}
}
- Converter callbacks de banco de dados: atualize as
RoomDatabase.Callbackimplementações para usarSQLiteConnection:
// Before (SupportSQLiteDatabase)
val callback = object : RoomDatabase.Callback() {
override fun onCreate(db: SupportSQLiteDatabase) {
// ...
}
}
// After (SQLiteConnection - Room 2.8)
val callback = object : RoomDatabase.Callback() {
override fun onCreate(connection: SQLiteConnection) {
// ...
}
}
- Converter funções DAO
@RawQuery: para funções anotadas com@RawQuery, useRoomRawQueryem vez deSupportSQLiteQuery:
// Before (SupportSQLiteQuery)
@Dao
interface UserDao {
@RawQuery
fun getUser(query: SupportSQLiteQuery): User
}
// After (RoomRawQuery)
@Dao
interface UserDao {
@RawQuery
suspend fun getUser(query: RoomRawQuery): User
}
É possível criar um RoomRawQuery no ambiente de execução:
val query = RoomRawQuery(
sql = "SELECT * FROM User WHERE id = ?",
onBindStatement = { statement ->
statement.bindInt(1, userId)
}
)
- Converter APIs de transação: substitua os blocos somente para Android
withTransactionerunInTransactionporwithWriteTransactionouwithReadTransaction:
// Before (withTransaction)
db.withTransaction {
// perform database operations
}
// After (withWriteTransaction - Room 2.8)
import androidx.room.withWriteTransaction
db.withWriteTransaction {
// perform database operations
}
Se você precisar de acesso direto de baixo nível à conexão de transação, também poderá usar useWriterConnection com immediateTransaction.
- Evitar o uso direto de
SupportSQLiteDatabase: se você tiver um código legado extenso que ainda exigeSupportSQLiteDatabasee não puder migrá-lo ainda, use o artefato de compatibilidadeandroidx.room:room-sqlite-wrapper:
dependencies {
implementation("androidx.room:room-sqlite-wrapper:$roomVersion")
}
Em seguida, use getSupportWrapper para receber um SupportSQLiteDatabase da instância do banco de dados da Room:
import androidx.room.support.getSupportWrapper
val legacyDb: SupportSQLiteDatabase = roomDatabase.getSupportWrapper()
- Definir o driver SQLite: depois de migrar todos os usos da API Room para APIs de driver, configure um driver, como
BundledSQLiteDriverouAndroidSQLiteDriver, chamandosetDriverno builderRoomDatabase:
import androidx.sqlite.driver.bundled.BundledSQLiteDriver
val db = Room.databaseBuilder<AppDatabase>(context, "db")
.setDriver(BundledSQLiteDriver())
.build()
Adotar o rastreamento de invalidação baseado em fluxo
A Room 2.8 apresenta a API InvalidationTracker.createFlow. Use essa API para migrar das implementações legadas de InvalidationTracker.Observer enquanto ainda estiver na Room 2.x. Isso prepara sua base de código para a Room 3.0, que remove completamente o Observer.
// Before (InvalidationTracker.Observer)
val observer = object : InvalidationTracker.Observer("User") {
override fun onInvalidated(tables: Set<String>) {
// reload user data
}
}
db.invalidationTracker.addObserver(observer)
// After (createFlow - Room 2.8)
val userFlow = db.invalidationTracker.createFlow("User").map { _ ->
userDao.getAllUsers()
}
Migrar para a Room 3.0
Depois de modernizar o aplicativo na Room 2.x, a transição para a Room 3.0 envolve a atualização de dependências, importações de pacotes e callbacks de banco de dados.
Atualizar dependências e importações de pacotes
- Na configuração do build, substitua as dependências
androidx.roomporandroidx.room3:
[versions]
room3 = "3.0.0" # Use the current Room 3.0 version
[libraries]
androidx-room3-runtime = { module = "androidx.room3:room3-runtime", version.ref = "room3" }
androidx-room3-compiler = { module = "androidx.room3:room3-compiler", version.ref = "room3" }
- Atualize o bloco de dependências:
dependencies {
implementation(libs.androidx.room3.runtime)
ksp(libs.androidx.room3.compiler)
}
- Atualize as importações de pacotes. Substitua
import androidx.room.*porimport androidx.room3.*.
Atualizar APIs de conversor de tipo
A Room 3.0 renomeia as APIs do conversor de tipo para esclarecer o uso delas na conversão de valores de coluna e evitar confusão com os conversores de tipo de retorno DAO.
Atualize as seguintes anotações e funções na sua base de código:
- Renomeie
@TypeConverterpara@ColumnTypeConverter. - Renomeie
@TypeConverterspara@ColumnTypeConverters. - Renomeie
@ProvidedTypeConverterpara@ProvidedColumnTypeConverter. - Renomeie
RoomDatabase.Builder.addTypeConverterparaaddColumnTypeConverter.
Exemplo:
// Before
@ProvidedTypeConverter
class Converters {
@TypeConverter
fun fromTimestamp(value: Long?): Date? = ...
}
@Database(entities = [User::class], version = 1)
@TypeConverters(Converters::class)
abstract class AppDatabase : RoomDatabase()
val db = Room.databaseBuilder<AppDatabase>(...)
.addTypeConverter(convertersInstance)
.build()
// After
import androidx.room3.ColumnTypeConverter
import androidx.room3.ColumnTypeConverters
import androidx.room3.ProvidedColumnTypeConverter
@ProvidedColumnTypeConverter
class Converters {
@ColumnTypeConverter
fun fromTimestamp(value: Long?): Date? = ...
}
@Database(entities = [User::class], version = 1)
@ColumnTypeConverters(Converters::class)
abstract class AppDatabase : RoomDatabase()
val db = Room.databaseBuilder<AppDatabase>(...)
.addColumnTypeConverter(convertersInstance)
.build()
Atualizar callbacks para suspender funções
Na Room 3.0, os callbacks e as migrações de banco de dados usam SQLiteConnection e são funções suspend.
- Atualize suas classes
Migrationmanuais:
import androidx.sqlite.SQLiteConnection
import androidx.sqlite.async.executeSQL
val MIGRATION_1_2 = object : Migration(1, 2) {
override suspend fun migrate(connection: SQLiteConnection) {
connection.executeSQL(
"ALTER TABLE User ADD COLUMN age INTEGER DEFAULT 0 NOT NULL"
)
}
}
- Atualize suas implementações de
RoomDatabase.Callback:
val callback = object : RoomDatabase.Callback() {
override suspend fun onCreate(connection: SQLiteConnection) {
// ...
}
}
Registrar conversores de tipo de retorno DAO
Na Room 3.0, os tipos de retorno reativos, como RxJava, LiveData, Guava e Paging, exigem que você registre conversores de tipo de retorno DAO usando @DaoReturnTypeConverters.
import androidx.room3.paging.PagingSourceDaoReturnTypeConverter
@Dao
@DaoReturnTypeConverters(PagingSourceDaoReturnTypeConverter::class)
interface UserDao {
@Query("SELECT * FROM User")
fun getAllPaginated(): PagingSource<Int, User>
}
- Paging (
PagingSource): registrePagingSourceDaoReturnTypeConverterdo artefatoandroidx.room3:room3-paging. - RxJava (
Observable,Flowable,Single,Maybe,Completable): registreRxDaoReturnTypeConvertersdo artefatoandroidx.room3:room3-rxjava3. - Guava (
ListenableFuture): registreGuavaDaoReturnTypeConverterdo artefatoandroidx.room3:room3-guava. - LiveData (
LiveData): registreLiveDataDaoReturnTypeConverterdoandroidx.room3:room3-livedataartefato.
Verificar a remoção do observador InvalidationTracker
A Room 3.0 remove completamente InvalidationTracker.Observer e métodos de registro relacionados, como addObserver e removeObserver.
Se você ainda não fez a transição para fluxos de corrotina em
Fase 1, migre todos os usos de Observer para
createFlow:
val userFlow = db.invalidationTracker.createFlow("User").map { _ ->
userDao.getAllUsers()
}