Die Room-Persistenzbibliothek bietet eine Abstraktionsschicht über SQLite, die einen robusteren Datenbankzugriff ermöglicht und gleichzeitig die volle Leistung von SQLite nutzt. Auf dieser Seite geht es hauptsächlich um die Verwendung von Room in Kotlin Multiplatform-Projekten (KMP). Weitere Informationen zur Verwendung von Room finden Sie unter Daten mit Room in einer lokalen Datenbank speichern oder in unseren offiziellen Beispielen.
Abhängigkeiten einrichten
Die aktuelle Version von Room, die KMP unterstützt, ist 2.7.0-alpha01 oder höher.
Wenn Sie Room in Ihrem KMP-Projekt einrichten möchten, fügen Sie die Abhängigkeiten für die Artefakte in der Datei build.gradle.kts
für Ihr Modul hinzu:
androidx.room:room-gradle-plugin
– das Gradle-Plug-in zum Konfigurieren von Room-Schemasandroidx.room:room-compiler
– KSP-Prozessor, der Code generiertandroidx.room:room-runtime
: Der Laufzeitteil der Bibliothekandroidx.sqlite:sqlite-bundled
– (Optional) Die mitgelieferte SQLite-Bibliothek
Außerdem müssen Sie den SQLite-Treiber von Room konfigurieren. Diese Treiber unterscheiden sich je nach Zielplattform. Beschreibungen der verfügbaren Treiberimplementierungen finden Sie unter Treiberimplementierungen.
Weitere Informationen zur Einrichtung finden Sie hier:
- Schemaspeicherort mit dem Room Gradle-Plug-in festlegen
- KSP mit Kotlin Multiplatform
- Laufzeitabhängigkeiten hinzufügen
Datenbankklassen definieren
Sie müssen eine mit @Database
annotierte Datenbankklasse sowie DAOs und Entitäten im gemeinsamen Quellsatz Ihres freigegebenen KMP-Moduls erstellen. Wenn Sie diese Klassen in gemeinsamen Quellen platzieren, können sie auf allen Zielplattformen freigegeben werden.
Wenn Sie ein expect
-Objekt mit der Schnittstelle RoomDatabaseConstructor
deklarieren, generiert der Room-Compiler die actual
-Implementierungen. Android Studio sendet möglicherweise die Warnung "Expected object 'AppDatabaseConstructor' has no actual declaration in
module"
. Sie können sie mit @Suppress("NO_ACTUAL_FOR_EXPECT")
unterdrücken.
// shared/src/commonMain/kotlin/Database.kt
@Database(entities = [TodoEntity::class], version = 1)
@ConstructedBy(AppDatabaseConstructor::class)
abstract class AppDatabase : RoomDatabase() {
abstract fun getDao(): TodoDao
}
// The Room compiler generates the `actual` implementations.
@Suppress("NO_ACTUAL_FOR_EXPECT")
expect object AppDatabaseConstructor : RoomDatabaseConstructor<AppDatabase> {
override fun initialize(): AppDatabase
}
@Dao
interface TodoDao {
@Insert
suspend fun insert(item: TodoEntity)
@Query("SELECT count(*) FROM TodoEntity")
suspend fun count(): Int
@Query("SELECT * FROM TodoEntity")
fun getAllAsFlow(): Flow<List<TodoEntity>>
}
@Entity
data class TodoEntity(
@PrimaryKey(autoGenerate = true) val id: Long = 0,
val title: String,
val content: String
)
Optional können Sie actual-/expect-Deklarationen verwenden, um plattformspezifische Room-Implementierungen zu erstellen. Sie können beispielsweise eine plattformspezifische DAO hinzufügen, die in gemeinsamem Code mit expect
definiert ist, und dann die actual
-Definitionen mit zusätzlichen Abfragen in plattformspezifischen Quellsätzen angeben.
Datenbank-Builder erstellen
Sie müssen einen Datenbank-Builder definieren, um Room auf jeder Plattform zu instanziieren. Dies ist der einzige Teil der API, der aufgrund der Unterschiede bei Dateisystem-APIs in platformspezifischen Quellsätzen enthalten sein muss. Unter Android wird der Speicherort der Datenbank beispielsweise in der Regel über die Context.getDatabasePath()
API abgerufen, während er unter iOS mit NSFileManager
abgerufen wird.
Android
Geben Sie zum Erstellen der Datenbankinstanz einen Kontext und den Pfad zur Datenbank an.
// shared/src/androidMain/kotlin/Database.kt
fun getDatabaseBuilder(ctx: Context): RoomDatabase.Builder<AppDatabase> {
val appContext = ctx.applicationContext
val dbFile = appContext.getDatabasePath("my_room.db")
return Room.databaseBuilder<AppDatabase>(
context = appContext,
name = dbFile.absolutePath
)
}
iOS
Geben Sie zum Erstellen der Datenbankinstanz einen Datenbankpfad mithilfe von NSFileManager
an, der sich normalerweise im NSDocumentDirectory
befindet.
// shared/src/iosMain/kotlin/Database.kt
fun getDatabaseBuilder(): RoomDatabase.Builder<AppDatabase> {
val dbFilePath = documentDirectory() + "/my_room.db"
return Room.databaseBuilder<AppDatabase>(
name = dbFilePath,
)
}
private fun documentDirectory(): String {
val documentDirectory = NSFileManager.defaultManager.URLForDirectory(
directory = NSDocumentDirectory,
inDomain = NSUserDomainMask,
appropriateForURL = null,
create = false,
error = null,
)
return requireNotNull(documentDirectory?.path)
}
JVM (Desktop)
Geben Sie zum Erstellen der Datenbankinstanz einen Datenbankpfad mit Java- oder Kotlin-APIs an.
// shared/src/jvmMain/kotlin/Database.kt
fun getDatabaseBuilder(): RoomDatabase.Builder<AppDatabase> {
val dbFile = File(System.getProperty("java.io.tmpdir"), "my_room.db")
return Room.databaseBuilder<AppDatabase>(
name = dbFile.absolutePath,
)
}
Minimierung und Verschleierung
Wenn das Projekt minimiert oder verschleiert ist, muss die folgende ProGuard-Regel enthalten sein, damit Room die generierte Implementierung der Datenbankdefinition finden kann:
-keep class * extends androidx.room.RoomDatabase { <init>(); }
Datenbankinstanzierung
Nachdem Sie die RoomDatabase.Builder
über einen der plattformspezifischen Konstruktoren abgerufen haben, können Sie den Rest der Room-Datenbank in gemeinsamem Code zusammen mit der tatsächlichen Datenbankinstanz konfigurieren.
// shared/src/commonMain/kotlin/Database.kt
fun getRoomDatabase(
builder: RoomDatabase.Builder<AppDatabase>
): AppDatabase {
return builder
.addMigrations(MIGRATIONS)
.fallbackToDestructiveMigrationOnDowngrade()
.setDriver(BundledSQLiteDriver())
.setQueryCoroutineContext(Dispatchers.IO)
.build()
}
SQLite-Treiber auswählen
In den vorherigen Code-Snippets wird die BundledSQLiteDriver
verwendet. Dies ist der empfohlene Treiber, der SQLite enthält, das aus der Quelle kompiliert wurde. Er bietet die konsistenteste und aktuellste Version von SQLite auf allen Plattformen. Wenn Sie die vom Betriebssystem bereitgestellte SQLite-Version verwenden möchten, verwenden Sie die setDriver
API in plattformspezifischen Quellsätzen, die einen plattformspezifischen Treiber angeben. Unter Android können Sie die Taste AndroidSQLiteDriver
verwenden, unter iOS die Taste NativeSQLiteDriver
. Wenn Sie NativeSQLiteDriver
verwenden möchten, müssen Sie eine Linker-Option angeben, damit die iOS-App dynamisch mit dem SQLite-System verknüpft wird.
// shared/build.gradle.kts
kotlin {
listOf(
iosX64(),
iosArm64(),
iosSimulatorArm64()
).forEach { iosTarget ->
iosTarget.binaries.framework {
baseName = "TodoApp"
isStatic = true
// Required when using NativeSQLiteDriver
linkerOpts.add("-lsqlite3")
}
}
}
Unterschiede
Room wurde ursprünglich als Android-Bibliothek entwickelt und später mit Schwerpunkt auf API-Kompatibilität zu KMP migriert. Die KMP-Version von Room unterscheidet sich je nach Plattform und von der Android-spezifischen Version etwas. Diese Unterschiede sind unten aufgeführt und beschrieben.
Blockieren von DAO-Funktionen
Wenn Sie Room für KMP verwenden, müssen alle DAO-Funktionen, die für andere Plattformen als Android kompiliert wurden, suspend
-Funktionen sein, mit Ausnahme von reaktiven Rückgabetypen wie Flow
.
// shared/src/commonMain/kotlin/MultiplatformDao.kt
@Dao
interface MultiplatformDao {
// ERROR: Blocking function not valid for non-Android targets
@Query("SELECT * FROM Entity")
fun blockingQuery(): List<Entity>
// OK
@Query("SELECT * FROM Entity")
suspend fun query(): List<Entity>
// OK
@Query("SELECT * FROM Entity")
fun queryFlow(): Flow<List<Entity>>
// ERROR: Blocking function not valid for non-Android targets
@Transaction
fun blockingTransaction() { // … }
// OK
@Transaction
suspend fun transaction() { // … }
}
Room profitiert von der funktionsreichen asynchronen kotlinx.coroutines
-Bibliothek, die Kotlin für mehrere Plattformen anbietet. Für eine optimale Funktionalität werden suspend
-Funktionen für DAOs erzwungen, die in einem KMP-Projekt kompiliert wurden, mit Ausnahme von Android-spezifischen DAOs, um die Abwärtskompatibilität mit der vorhandenen Codebasis aufrechtzuerhalten.
Funktionsunterschiede zu KMP
In diesem Abschnitt werden die Unterschiede zwischen den Funktionen der KMP- und der Android-Plattformversion von Room beschrieben.
@RawQuery-DAO-Funktionen
Für Funktionen, die mit @RawQuery
annotiert und für andere Plattformen als Android kompiliert werden, muss anstelle von SupportSQLiteQuery
ein Parameter vom Typ RoomRawQuery
deklariert werden.
@Dao
interface TodoDao {
@RawQuery
suspend fun getTodos(query RoomRawQuery): List<TodoEntity>
}
Mit einem RoomRawQuery
kann dann eine Abfrage zur Laufzeit erstellt werden:
suspend fun getTodosWithLowercaseTitle(title: String): List<TodoEntity> {
val query = RoomRawQuery(
sql = "SELECT * FROM TodoEntity WHERE title = ?"
onBindStatement = {
it.bindText(1, title.lowercase())
}
)
return todosDao.getTodos(query)
}
Rückruf bei Abfrage
Die folgenden APIs zum Konfigurieren von Abfrage-Callbacks sind nicht allgemein verfügbar und daher auf anderen Plattformen als Android nicht verfügbar.
RoomDatabase.Builder.setQueryCallback
RoomDatabase.QueryCallback
In einer zukünftigen Version von Room wird die Unterstützung für Abfrage-Callbacks hinzugefügt.
Die API zum Konfigurieren eines RoomDatabase
mit einem Abfrage-CallbackRoomDatabase.Builder.setQueryCallback
und die Callback-SchnittstelleRoomDatabase.QueryCallback
sind nicht allgemein verfügbar und daher auch nicht auf anderen Plattformen als Android verfügbar.
Automatisch schließende Datenbank
Die API zum Aktivieren des automatischen Schließens nach einem Zeitlimit, RoomDatabase.Builder.setAutoCloseTimeout
, ist nur auf Android-Geräten verfügbar.
Datenbank vorbereiten
Die folgenden APIs zum Erstellen einer RoomDatabase
mit einer vorhandenen Datenbank (d.h. einer vorkonfigurierten Datenbank) sind nicht allgemein verfügbar und daher auf anderen Plattformen als Android nicht verfügbar. Das sind:
RoomDatabase.Builder.createFromAsset
RoomDatabase.Builder.createFromFile
RoomDatabase.Builder.createFromInputStream
RoomDatabase.PrepackagedDatabaseCallback
In einer zukünftigen Version von Room wird es Unterstützung für vorkonfigurierte Datenbanken geben.
Entwertung mehrerer Instanzen
Die API zur Aktivierung der Invalidation für mehrere Instanzen, RoomDatabase.Builder.enableMultiInstanceInvalidation
, ist nur auf Android-Geräten verfügbar und nicht auf gängigen oder anderen Plattformen.