Room ist eine Bibliothek für die Arbeit mit SQLite in Android, Teil von Jetpack. Sie bietet eine Abstraktionsschicht über rohes SQLite und automatisiert die Tabellenerstellung, Abfrageausführung und Datenkonvertierung in Kotlin- und Java-Objekte. Laut Android Developers kompiliert Room SQL-Abfragen zur Build-Zeit und überprüft die Syntaxkorrektheit sowie die Beziehungen zwischen Entity und Tabellen.
Wichtige Punkte
Room ist eine Persistenzbibliothek von Android Jetpack, die objektrelationales Mapping für SQLite bereitstellt. Room löst drei Hauptprobleme von rohem SQLite: das Schreiben großer Mengen an Boilerplate-Code zur Tabellenerstellung, das Fehlen der SQL-Abfrageüberprüfung zur Kompilierzeit und die manuelle Konvertierung von Cursor in Objekte.
Die Bibliothek verwendet einen Annotationsprozessor (kapt oder KSP), der die Implementierung der abstrakten RoomDatabase- und DAO-Klassen zur Build-Zeit generiert. Dadurch wird sichergestellt, dass Syntaxfehler in SQL und Typkonflikte bereits vor dem Ausführen der Anwendung erkannt werden, nicht erst zur Laufzeit nach der Veröffentlichung im Google Play Store.
Laut Google I/O 2023 wird Room in 68% der Android-Anwendungen verwendet, die mit lokalen Daten arbeiten. Es ist der Standard für die Datenspeicherung auf dem Gerät, empfohlen von Google für alle neuen Projekte — anstelle der veralteten SQLiteOpenHelper und ContentProvider.
Integrieren Sie Room in Projekte, die lokales Caching von Serverdaten, Offline-Modus oder die Speicherung strukturierter Benutzerdaten mit der Möglichkeit komplexer SQL-Abfragen erfordern.
Room ist Teil von Android Jetpack und wird von Google offiziell für alle neuen Projekte empfohlen, die mit lokalen Daten arbeiten. Im Gegensatz zu Realm oder ObjectBox verwendet Room natives SQLite, was die Kompatibilität mit allen Datenbanktools von Drittanbietern gewährleistet — von DB Browser bis DataGrip. Entwickler können die .db-Datei der Anwendung öffnen und SQL-Abfragen direkt ausführen, was das Debugging und die Datenanalyse während der Entwicklung vereinfacht.
Entity ist eine mit @Entity annotierte Datenklasse, die Room in eine Datenbanktabelle umwandelt. Jedes Feld der Klasse wird zu einer Tabellenspalte, jede Instanz zu einer Zeile. Room verwendet Reflektion für den Feldzugriff, daher ist die @PrimaryKey-Annotation für eine obligatorische ID erforderlich.
Die @Entity-Annotation teilt Room mit, dass die Klasse eine Tabelle ist. Der Parameter tableName legt den Tabellennamen fest, wenn er vom Klassennamen abweicht. @PrimaryKey definiert den Primärschlüssel mit Auto-Generierung durch 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 gibt den Spaltennamen in der Tabelle an, wenn er vom Kotlin-Feldnamen abweicht. @Ignore schließt ein Feld aus der Tabelle aus — es wird nicht in der Datenbank gespeichert. @ForeignKey beschreibt Fremdschlüssel für Beziehungen zwischen Tabellen mit Kaskadenoperationen beim Löschen oder Aktualisieren.
Room unterstützt verschachtelte Objekte durch die @Embedded-Annotation. Die Felder der verschachtelten Klasse werden mit einem Präfix in Spalten der Elterntabelle erweitert, um Namenskonflikte zu vermeiden. Beispielsweise erzeugt eine Address-Klasse mit city- und street-Feldern, die in User eingebettet ist, die Spalten address_city und address_street in der Tabelle users, wodurch separate Tabellen für einfache Wertobjekte überflüssig werden.
Room unterstützt nur primitive Typen und deren Wrapper. Zum Speichern von Listen, Date oder benutzerdefinierten Typen verwenden Sie @TypeConverter — statische Methoden zur Konvertierung zwischen einem benutzerdefinierten Typ und einem SQLite-Primitiv, z.B. zwischen List und einem JSON-String.
DAO (Data Access Object) ist ein mit @Dao annotiertes Interface oder eine abstrakte Klasse mit Methoden für den Datenzugriff. Jede Methode wird mit einer SQL-Operation annotiert: @Insert, @Update, @Delete oder @Query mit einer expliziten SQL-Abfrage.
Die @Query-Annotation nimmt einen SQL-String entgegen, den Room zur Kompilierzeit auf Syntaxkorrektheit und Übereinstimmung der Spaltennamen mit Entity-Feldern prüft. Room unterstützt parametrisierte Abfragen durch die Syntax :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 unterstützt OnConflictStrategy-Optionen zur Behandlung von Konflikten beim Einfügen doppelter Datensätze. Flow als Rückgabetyp sorgt für reaktive UI-Updates bei jeder Datenänderung in der Tabelle — das Abonnement wird bei jedem INSERT, UPDATE oder DELETE automatisch neu gestartet.
Die @Transaction-Annotation garantiert die atomare Ausführung mehrerer Operationen in einem einzigen Transaktionsblock. Room sperrt die Datenbank während der Ausführung und verhindert so Wettlaufsituationen bei gleichzeitigem Zugriff aus mehreren Threads.
RoomDatabase ist eine abstrakte Klasse, die Entity und DAO zu einem einzigen Datenbankzugriffspunkt zusammenfasst. Sie wird über Room.databaseBuilder mit der Schema-Version und einer Liste von Entity-Klassen erstellt. Die Datenbankinstanz sollte als Singleton über einen lazy-Delegaten erstellt werden, um mehrere Verbindungen zu vermeiden.
Eine Migration in Room ist eine Migration-Klasse, die ein SQL-Skript für den Übergang von einer alten Schema-Version zu einer neuen beschreibt. Wenn bei einer Schemaänderung keine Migration bereitgestellt wird, wirft Room eine IllegalStateException. Dies schützt vor versehentlichem Benutzerdatenverlust beim Aktualisieren der Anwendung.
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()
Für die Entwicklung können Sie fallbackToDestructiveMigration verwenden, das bei Versionskonflikten die alte Datenbank löscht und eine neue erstellt. Dieser Modus ist nur für das Debugging gedacht — Produktionsversionen müssen ordnungsgemäße Migrationen enthalten.
Zum Testen der Datenbank bietet Room die spezielle Klasse Room.inMemoryTestBuilder, die eine Datenbank im Arbeitsspeicher erstellt, ohne sie auf die Festplatte zu schreiben. Nach jedem Test wird die Datenbank automatisch zerstört, was eine vollständige Isolation der Testszenarien gewährleistet. In Kombination mit der Bibliothek android-arch-core-testing können Entwickler den Datenbanklebenszyklus verwalten und die Korrektheit von Migrationen überprüfen, ohne den Zustand manuell bereinigen zu müssen.
Die Leistung von Room hängt direkt von der Abfragestruktur und den Indizes ab. Zur Analyse langsamer Abfragen stellt Room das enableQueryCallback-Flag bereit, das alle SQL-Abfragen mit Ausführungszeit protokolliert. Entwickler können dieses Protokoll verwenden, um Abfragen zu finden, die länger als 100 Millisekunden dauern, und sie durch Hinzufügen zusammengesetzter Indizes über die @Index-Annotation in @Entity oder durch Umschreiben von Unterabfragen als direkte JOIN-Operationen mit @Relation optimieren.
Room unterstützt auch die Datenbankverschlüsselung über SQLCipher. Das Hinzufügen der Bibliothek net.zetetic:android-database-sqlcipher und die Verwendung von SupportFactory anstelle der Standardimplementierung bietet eine transparente Verschlüsselung aller Daten auf der Festplatte, ohne DAO-Abfragen oder die Entity-Struktur zu ändern. Dies ist für Anwendungen erforderlich, die mit persönlichen Benutzerdaten umgehen, und entspricht der DSGVO sowie dem russischen Bundesgesetz 152-FZ zum Schutz personenbezogener Daten. Das Verschlüsselungspasswort kann im Android Keystore gespeichert werden, um es vor dem Auslesen durch Tools auf gerooteten Geräten zu schützen.
Room unterstützt Kotlin Coroutines nativ ab Version 2.1. DAO-Methoden können als suspend-Funktionen deklariert werden, die Abfragen im Hintergrundthread ausführen, ohne den Hauptthread zu blockieren. Room verwaltet die Dispatcher automatisch und verwendet Dispatchers.IO für Lese- und Schreibabfragen.
Für reaktive Abfragen gibt Room einen Flow zurück — einen kalten Datenstrom, der bei jeder Änderung der betroffenen Tabelle einen neuen Wert ausgibt. Das ViewModel abonniert den Flow über stateIn oder collect und sorgt so für automatische UI-Updates ohne manuelle Benachrichtigung des Adapters.
Room unterstützt auch Paging 3 über eine spezielle PagingSource-Implementierung, die Daten seitenweise aus SQLite lädt. Dies ist effizient für große Listen mit Tausenden von Datensätzen: Paging 3 lädt nur die auf dem Bildschirm sichtbaren Zeilen und aktualisiert sie bei Datenbankänderungen automatisch.
Verwenden Sie Paging 3 mit Room zum Anzeigen von Nachrichtenfeeds, Operationslogs oder Produktlisten mit Offline-Zugriff und unendlichem Scrollen.
Häufig gestellte Fragen
Room automatisiert die Tabellenerstellung, die Cursor-zu-Objekt-Konvertierung und die SQL-Überprüfung zur Kompilierzeit. SQLiteOpenHelper erfordert das manuelle Schreiben des Schemas, die Verarbeitung von Cursor und hat keine Abfrageüberprüfung vor dem Ausführen der Anwendung, was das Fehlerrisiko erhöht.
Ja, beim Ändern einer Entity (Hinzufügen/Entfernen eines Feldes, Ändern eines Typs) ist eine Migration erforderlich. Ohne sie wirft Room beim Start eine IllegalStateException. Für die Entwicklung können Sie fallbackToDestructiveMigration aktivieren, aber Produktionsversionen benötigen korrekte Migrationsskripte.
Room unterstützt @ForeignKey für Kaskadenoperationen und @Relation für verschachtelte Objekte. Für komplexe JOIN-Abfragen verwenden Sie die @Transaction-Annotation mit @Query, die ein POJO mit verschachtelten Entitäten über @Embedded und @Relation zurückgibt.
Ja, Room ist vollständig kompatibel mit Java. Statt suspend-Funktionen verwenden Sie LiveData oder RxJava Observable; statt Flow verwenden Sie LiveData. Room mit Java unterstützt alle gleichen Annotationen, erfordert jedoch mehr Boilerplate-Code für asynchrone Operationen.
Room unterstützt Verschlüsselung über SQLCipher von Zetetic. Statt Room.databaseBuilder verwenden Sie SupportFactory aus der Bibliothek net.zetetic:android-database-sqlcipher und übergeben das Verschlüsselungspasswort. Alle Daten auf der Festplatte werden für DAO-Abfragen transparent verschlüsselt.
Zusammenfassung
Wir entwickeln eine mobile Applikation schlüsselfertig
IT Sectr entwickelt seit 2017 iOS- und Android-Apps für Startups und Unternehmen. Wir beraten Sie und schlagen die beste Lösung vor.
Lesen Sie auch