Room 3.0 は、ライブラリを Kotlin ファーストに移行するメジャー バージョン アップデートです。Kotlin Multiplatform(KMP)をサポートし、Kotlin Symbol Processing(KSP)を必要とし、非同期オペレーションにコルーチンを適用します。
既存の Room 2.x アプリや推移的な依存関係との互換性の問題を回避するため、Room 3.0 は新しいパッケージ androidx.room3 に配置されます。
このガイドでは、既存の Room 2.x 実装を Room 3.0 に移行するために必要な手順について説明します。
Room 3.0 の主な変更点
移行を開始する前に、主な違いを把握してください。
- 新しいパッケージとアーティファクト: すべてのクラスは
androidx.room3に配置されます。 アーティファクトにはroom3接頭辞(androidx.room3:room3-runtimeなど)が使用されます。 - Kotlin と KSP のみ: Room 3.0 は Java コード生成をサポートしていません。KAPT または Java アノテーション プロセッサの代わりに KSP を使用してください。Room 3.0 は、入力として Java ソースをサポートしています。
- コルーチン ファースト: DAO 関数は、オブザーバブル型を除き、
suspend関数にする必要があります。CoroutineContextは実行プログラムに置き換わります。 - SupportSQLite なし:
SQLiteDriverAPI が Room をサポートします。Room はコア API からSupportSQLiteDatabaseを削除します。 - API の変更: 移行とデータベース コールバックでは、
SupportSQLiteDatabaseではなくSQLiteConnectionが使用されます。 - リアクティブ型のコンバータ: RxJava、LiveData、Guava、Paging
の戻り値の型では、
@DaoReturnTypeConvertersを登録する必要があります。
2 つの異なるフェーズで移行することをおすすめします。まず、Room 2.x でコードベースを準備して最新化し、次に Room 3.0 に切り替えます。
Room 2.x で準備と最新化を行う
Room 3.0 に移行する前に、Room 2.8 などの現在の Room 2.x リリースに更新することで、ほとんどの最新化作業を行うことができます。Room 2.8 は Kotlin Multiplatform(KMP)をサポートし、Room 3.0 で使用される多くのドライバ API が含まれています。
Room 2.8 以降に更新する
現在の 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" }
KAPT から KSP に移行する
Room 3.0 は、Java アノテーション プロセッサまたは KAPT をサポートしていません。Kotlin Symbol Processing(KSP)を使用する必要があります。この移行は、Room 2.x を使用している間に行うことができます。
モジュールの
build.gradle.ktsで、KSP プラグインを適用します。plugins { id("com.google.devtools.ksp") version "<ksp_version>" }KSP のバージョンが Kotlin のバージョンと互換性があることを確認します。
Room コンパイラの依存関係で、
kaptまたはannotationProcessorをkspに置き換えます。dependencies { implementation(libs.androidx.room.runtime) ksp(libs.androidx.room.compiler) }
コルーチンを採用する
Room 3.0 では、非同期オペレーションにコルーチンが必要です。
- DAO を更新します。
Flowや RxJava 型などのオブザーバブルなリアクティブ型を返さない限り、すべての DAO 関数はsuspend関数にする必要があります。
// 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>
}
- カスタム
Executorを使用してRoomDatabaseを構成し、データベース オペレーションを実行した場合は、ビルダーでsetQueryCoroutineContextを使用してCoroutineContextに移行します。
Room.databaseBuilder<AppDatabase>(context, "db")
.setQueryCoroutineContext(Dispatchers.IO)
.build()
ドライバ API を採用し、Support SQLite を回避する
Room 3.0 は SQLiteDriver によって完全にサポートされており、コア API で SupportSQLiteDatabase をサポートしなくなりました。
データベース ビルダーで SQLiteDriver を設定するために setDriver を呼び出さない場合、Room 2.8 は Support SQLite と Driver API の両方が機能する互換モードで動作します。この互換モードでは、ドライバを有効にする前にコードベースを段階的に変換できます。
- 移行を変換する:
MigrationとAutoMigrationSpecサブクラスを移行して、SupportSQLiteDatabaseではなくSQLiteConnectionを使用します。
// 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"
)
}
}
- データベース コールバックを変換する:
RoomDatabase.Callback実装を更新してSQLiteConnectionを使用します。
// 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) {
// ...
}
}
@RawQueryDAO 関数を変換する:@RawQueryでアノテーションが付けられた関数には、SupportSQLiteQueryではなくRoomRawQueryを使用します。
// Before (SupportSQLiteQuery)
@Dao
interface UserDao {
@RawQuery
fun getUser(query: SupportSQLiteQuery): User
}
// After (RoomRawQuery)
@Dao
interface UserDao {
@RawQuery
suspend fun getUser(query: RoomRawQuery): User
}
RoomRawQuery は実行時に構築できます。
val query = RoomRawQuery(
sql = "SELECT * FROM User WHERE id = ?",
onBindStatement = { statement ->
statement.bindInt(1, userId)
}
)
- トランザクション API を変換する: Android 専用の
withTransactionとrunInTransactionブロックをwithWriteTransactionまたはwithReadTransactionに置き換えます。
// Before (withTransaction)
db.withTransaction {
// perform database operations
}
// After (withWriteTransaction - Room 2.8)
import androidx.room.withWriteTransaction
db.withWriteTransaction {
// perform database operations
}
トランザクション接続に直接低レベルでアクセスする必要がある場合は、immediateTransaction で useWriterConnection を使用することもできます。
- を直接使用しない
SupportSQLiteDatabase:SupportSQLiteDatabaseが必要な大規模な レガシー コードがあり、まだ移行できない場合は、androidx.room:room-sqlite-wrapper互換性アーティファクトを使用します。
dependencies {
implementation("androidx.room:room-sqlite-wrapper:$roomVersion")
}
次に、getSupportWrapper を使用して、Room データベース インスタンスから SupportSQLiteDatabase を取得します。
import androidx.room.support.getSupportWrapper
val legacyDb: SupportSQLiteDatabase = roomDatabase.getSupportWrapper()
- SQLite ドライバを設定する: すべての Room API の使用をドライバ
API に移行したら、
RoomDatabaseビルダーでsetDriverを呼び出して、BundledSQLiteDriverやAndroidSQLiteDriverなどのドライバを構成します。
import androidx.sqlite.driver.bundled.BundledSQLiteDriver
val db = Room.databaseBuilder<AppDatabase>(context, "db")
.setDriver(BundledSQLiteDriver())
.build()
フローベースの無効化トラッキングを採用する
Room 2.8 では、InvalidationTracker.createFlow API が導入されています。この API を使用すると、Room 2.x を使用しながら、レガシー InvalidationTracker.Observer 実装から移行できます。これにより、Observer を完全に削除する Room 3.0 のコードベースを準備できます。
// 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()
}
Room 3.0 に移行する
Room 2.x でアプリケーションを最新化したら、Room 3.0 への移行には、依存関係、パッケージ インポート、データベース コールバックの更新が含まれます。
依存関係とパッケージ インポートを更新する
- ビルド構成で、
androidx.roomの依存関係をandroidx.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" }
- 依存関係ブロックを更新します。
dependencies {
implementation(libs.androidx.room3.runtime)
ksp(libs.androidx.room3.compiler)
}
- パッケージ インポートを更新します。
import androidx.room.*をimport androidx.room3.*に置き換えます。
型コンバータ API を更新する
Room 3.0 では、型コンバータ API の名前が変更され、列値の変換での使用方法が明確になり、DAO の戻り値の型コンバータとの混同を避けることができます。
コードベースで次のアノテーションと関数を更新します。
@TypeConverterを@ColumnTypeConverterに名前変更します。@TypeConvertersを@ColumnTypeConvertersに名前変更します。@ProvidedTypeConverterを@ProvidedColumnTypeConverterに名前変更します。RoomDatabase.Builder.addTypeConverterをaddColumnTypeConverterに名前変更します。
例:
// 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()
コールバックを suspend 関数に更新する
Room 3.0 では、データベース コールバックと移行で SQLiteConnection が使用され、suspend 関数になります。
- 手動の
Migrationクラスを更新します。
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"
)
}
}
RoomDatabase.Callback実装を更新します。
val callback = object : RoomDatabase.Callback() {
override suspend fun onCreate(connection: SQLiteConnection) {
// ...
}
}
DAO の戻り値の型コンバータを登録する
Room 3.0 では、RxJava、LiveData、Guava、Paging などのリアクティブな戻り値の型では、@DaoReturnTypeConverters を使用して DAO の戻り値の型コンバータを登録する必要があります。
import androidx.room3.paging.PagingSourceDaoReturnTypeConverter
@Dao
@DaoReturnTypeConverters(PagingSourceDaoReturnTypeConverter::class)
interface UserDao {
@Query("SELECT * FROM User")
fun getAllPaginated(): PagingSource<Int, User>
}
- Paging (
PagingSource):PagingSourceDaoReturnTypeConverterをandroidx.room3:room3-pagingアーティファクトから登録します。 - RxJava(
Observable、Flowable、Single、Maybe、Completable):androidx.room3:room3-rxjava3アーティファクトからRxDaoReturnTypeConvertersを登録します。 - Guava(
ListenableFuture):androidx.room3:room3-guavaアーティファクトからGuavaDaoReturnTypeConverterを登録します。 - LiveData (
LiveData):androidx.room3:room3-livedataアーティファクトからLiveDataDaoReturnTypeConverterを登録します。
InvalidationTracker Observer の削除を確認する
Room 3.0 では、InvalidationTracker.Observer と、addObserver や removeObserver などの関連する登録メソッドが完全に削除されます。
フェーズ 1 でコルーチン フローに移行していない場合は、すべての Observer の使用を
createFlow に移行する必要があります。
val userFlow = db.invalidationTracker.createFlow("User").map { _ ->
userDao.getAllUsers()
}