Room är ett ORM-bibliotek från Android Jetpack som tillhandahåller ett abstraktionslager ovanför SQLite för att arbeta med lokala databaser på Android. Enligt den officiella dokumentationen Android Developers, 2025, genererar Room automatiskt DAO-implementationer baserade på annoteringar under kompilering, vilket eliminerar cirka 70 % av standardkoden jämfört med direkt användning av SQLiteOpenHelper. Biblioteket utför validering av SQL-frågor i kompileringsfasen, vilket gör det möjligt att upptäcka syntaxfel innan applikationen startas på enheten.
Huvudpunkter
Room är ett ORM-bibliotek från Android Jetpack, skapat av Google för att förenkla arbetet med lokala SQLite-databaser på Android-plattformen. Det tillhandahåller annoteringar för att beskriva dataschemat och genererar automatiskt implementationen av DAO-gränssnitt i kompileringsfasen. Till skillnad från direkt användning av SQLiteOpenHelper, befriar Room utvecklaren från att skriva betydande mängder standardkod för att skapa, öppna och hantera anslutningen till databasen.
Biblioteket introducerades på Google I/O 2017 som en del av Android-arkitekturkomponenterna. Sedan dess har Room blivit de facto-standarden för lokal datalagring och har i popularitet överträffat lösningar som GreenDAO och Realm för Android. Enligt Google används biblioteket i mer än 60 % av apparna som publiceras i Google Play och som arbetar med lokala data på enheten.
Nyckelfunktion — validering av SQL-frågor i kompileringsfasen med hjälp av en annoteringsprocessor. Om utvecklaren gör ett misstag i ett SQL-kommando, till exempel anger ett icke-existerande kolumnnamn, avslutas kompileringen med ett fel innan appen installeras. Detta skiljer sig radikalt från SQLiteOpenHelper-metoden, där sådana fel upptäcks först under körning, ofta i produktion.
SQLite stöder endast fem datatyper: TEXT, INTEGER, REAL, BLOB och NULL. I Java och Kotlin används dock komplexa typer: Date, List, Enum och anpassade objekt. För att lagra dem tillhandahåller Room TypeConverters-mekanismen — statiska metoder som konverterar en komplex typ till en primitiv typ som är förståelig för SQLite. Till exempel konverteras ett Date-objekt till Long (timestamp) och List<String> till en JSON-sträng via Gson eller Moshi.
@Database(entities = [User::class], version = 1)
abstract class AppDatabase : RoomDatabase() {
abstract fun userDao(): UserDao
}
val db = Room
.databaseBuilder(context, AppDatabase::class.java, "app-db")
.build()
För att deklarera en omvandlare räcker det att lägga till annoteringen @TypeConverter till en statisk metod och ange omvandlarklassen i @TypeConverters-annoteringen på databasnivå. Room tillämpar automatiskt omvandlaren vid läsning och skrivning av motsvarande typ i varje SQL-fråga utan manuell anropning av konverteringsmetoder.
Room består av tre huvudkomponenter: Entity, DAO och Database. Var och en har en strikt definierad roll och annoteras med motsvarande annotering. Tillsammans bildar de ett fullfjädrat dataåtkomstlager som isolerar appens affärslogik från implementationsdetaljerna i SQLite.
Entity är en dataklass som beskriver strukturen för en tabell i databasen. Varje fält i klassen motsvarar en kolumn i tabellen och varje rad i databasen motsvarar en instans av klassen. @Entity-annoteringen talar om för Room att klassen är en tabell. Fältet med @PrimaryKey-annoteringen definierar primärnyckeln, som kan vara auto-inkrementerande eller sammansatt. För relationer mellan tabeller används @ForeignKey, som säkerställer dataintegritet på databasnivå.
@Entity(tableName = "users")
data class User(
@PrimaryKey(autoGenerate = true)
val id: Int = 0,
@ColumnInfo(name = "full_name")
val name: String,
val age: Int,
val email: String
)
DAO (Data Access Object) är ett gränssnitt eller en abstrakt klass som deklarerar operationer för att arbeta med data: infoga, läsa, uppdatera och ta bort. Varje operation annoteras med @Insert, @Query, @Update eller @Delete. Room genererar automatiskt implementationen av detta gränssnitt i kompileringsfasen. Speciellt värde har @Query-annoteringen, som tar emot en SQL-fråga som en sträng och kontrollerar dess korrekthet i byggfasen.
@Dao
interface UserDao {
@Insert
suspend fun insert(user: User): Long
@Query("SELECT * FROM users WHERE id = :userId")
suspend fun getUserById(userId: Int): User?
@Query("SELECT * FROM users")
fun getAllUsers(): Flow<List<User>>
@Delete
suspend fun delete(user: User)
}
Database är en abstrakt klass som ärver från RoomDatabase och fungerar som startpunkt till databasen. Den innehåller en lista över alla Entity och tillhandahåller abstrakta metoder för att få DAO. Klassen annoteras med @Database, där schemaversionen och listan över entiteter anges. Skapandet av en databasinstans sker via Room.databaseBuilder med angivande av applikationskontext, filnamn och Database-klass.
Room ersätter inte SQLite, utan fungerar ovanpå det som ett abstraktionslager. Den interna arkitekturen inkluderar en annoteringsprocessor, kodgenerator och en anslutningspool. I kompileringsfasen analyserar annoteringsprocessorn klasserna Entity, DAO och Database, och genererar sedan implementationsklasser med suffixet _Impl. Alla genererade klasser placeras i byggpaketet och är inte direkt synliga för utvecklaren.
Kodgenerering i kompileringsfasen — den centrala mekanismen i Room. För varje DAO-gränssnitt genereras en klass med fullständig implementation av alla annoterade metoder. SQL-frågor från @Query-annoteringen kontrolleras för korrekthet: processorn matchar kolumnnamn med Entity-fält och kontrollerar SQL-syntaxen. Vid upptäckt av ett fel avbryts kompileringen med ett begripligt meddelande. Detta är omöjligt vid användning av rå SQLiteOpenHelper, där fel uppträder först vid körning.
Genereringsprocessen omfattar tre faser. Första — schemavalidering: processorn kontrollerar om alla klasser som anges i @Database är giltiga Entity. Andra — generering av DAO-kroppen: för varje metod skapas en implementation med hjälp av ett internt RoomSQLiteQuery-objekt som utför förberedda frågor. Tredje — generering av Database_Impl-klassen, som implementerar skapande och öppnande av databasen samt initialisering av alla DAO-objekt.
class UserDao_Impl(private val __db: RoomDatabase) : UserDao {
private val __insertionAdapter = __db
.createInsertionAdapter(User::class, 0)
override suspend fun insert(user: User): Long {
__db.assertNotSuspendingTransaction()
return __db.runInTransaction {
__insertionAdapter.insertAndReturnId(user)
}
}
}
Room skapar inte en separat trådpool för databasoperationer. Som standard utförs frågor i den anropande tråden med en begränsning: läsning och skrivning blockerar tråden. För asynkront arbete integreras Room med Kotlin-korutiner via suspend-funktioner, med LiveData via returvärden och med Flow via reaktiva omslag. Detta ger utvecklaren flexibilitet att välja arkitekturlösning för en specifik uppgift.
Låt oss titta på ett praktiskt exempel på att skapa en app för att lagra anteckningar med Room. Appen innehåller en tabell Note med fälten id, title, content och timestamp. Användaren kommer att kunna lägga till, visa och ta bort anteckningar. För demonstrationen används korutiner för asynkrona operationer.
För att ansluta Room till ett Android-projekt måste beroenden läggas till i appmodulens build.gradle-fil. Room kräver tre komponenter: runtime-bibliotek, kapt-annotationsprocessor och valfritt stöd för korutiner. Biblioteksversionen anges i variabeln room_version för enkel uppdatering. Från och med Room 2.4.0 stöds KSP som ett alternativ till kapt med högre bygghastighet.
dependencies {
def room_version = "2.6.1"
implementation "androidx.room:room-runtime:$room_version"
kapt "androidx.room:room-compiler:$room_version"
implementation "androidx.room:room-ktx:$room_version"
// Valfritt: testning
testImplementation "androidx.room:room-testing:$room_version"
}
Efter konfigurering av beroenden skapas tre filer: Note Entity, NoteDao-gränssnittet och AppDatabase-klassen. Note Entity innehåller fält med @PrimaryKey- och @ColumnInfo-annoteringar. DAO tillhandahåller metoder för att infoga, hämta lista och ta bort. Database kopplar samman Entity och DAO via @Database-annoteringen.
@Entity(tableName = "notes")
data class Note(
@PrimaryKey(autoGenerate = true)
val id: Int = 0,
val title: String,
val content: String,
@ColumnInfo(name = "created_at")
val timestamp: Long = System.currentTimeMillis()
)
@Dao
interface NoteDao {
@Insert
suspend fun insert(note: Note)
@Query("SELECT * FROM notes ORDER BY created_at DESC")
fun getAllNotes(): Flow<List<Note>>
@Delete
suspend fun delete(note: Note)
}
Filen AppDatabase deklareras som en abstrakt klass som ärver från RoomDatabase. I @Database-annoteringen listas alla Entity i den aktuella versionen och schemaversionens nummer. För att få en instans används singleton-mönstret via build-metoden Room.databaseBuilder med applikationskontexten. Cachning av databasinstansen förhindrar flera skapelser som kan leda till minnesläckor.
Migreringar i Room är en mekanism för att ändra databasschemat vid uppdatering av appen utan att förlora befintliga data. När användaren installerar en ny version med ändrade Entity, upptäcker Room versionsskillnaden och utför de angivna migreringsstegen. Utan migrering kommer databasen att raderas och skapas på nytt, vilket leder till förlust av alla sparade användardata.
En migrering beskrivs av klassen Migration, som tar emot databasens start- och slutversion. Inuti migrate-metoden utförs en SQL-fråga ALTER TABLE eller CREATE TABLE för att ändra schemat. Room kan inte automatiskt upptäcka schemaändringar — utvecklaren måste manuellt skriva en migrering för varje Entity-ändring. Från och med Room-version 2.4.0 finns den experimentella funktionen autoMigrations tillgänglig för automatisk generering av migreringar.
Funktionen autoMigrations gör det möjligt för Room att automatiskt generera migreringar baserat på skillnader mellan Entity-versioner. För att använda den räcker det att lägga till @AutoMigration-annoteringen i @Database och ange export av schemat till JSON. Room jämför scheman för angränsande versioner och genererar nödvändiga ALTER-frågor. Dock stöder autoMigrations endast bakåtkompatibla ändringar: tillägg av kolumner, skapande av index och typändringar med kompatibla konverteringar.
val MIGRATION_1_2 = object : Migration(1, 2) {
override fun migrate(db: SupportSQLiteDatabase) {
db.execSQL(
"ALTER TABLE users ADD COLUMN phone TEXT"
)
}
}
val db = Room
.databaseBuilder(context, AppDatabase::class.java, "app-db")
.addMigrations(MIGRATION_1_2)
.build()
Vid tillägg av komplexa ändringar, såsom omdöpning av kolumner eller sammanslagning av tabeller, krävs manuell migrering med hjälp av temporära tabeller. Typiskt scenario: skapa en temporär tabell med det gamla schemat, kopiera data från den gamla tabellen till den nya med konverteringar, ta bort den gamla tabellen och byt namn på den temporära. Room garanterar att alla migreringar utförs i en enda transaktion och att ändringarna vid ett fel helt återställs.
Vanliga frågor
Room tillhandahåller en ORM-abstraktion med annoteringar och SQL-validering i kompileringsfasen, medan SQLiteOpenHelper kräver manuell skrivning av alla frågor och anslutningshantering. Room genererar automatiskt kod för CRUD-operationer och integreras med Android-arkitekturkomponenter, inklusive LiveData och Flow.
Room stöder alla primitiva Java-typer: Int, Long, Boolean, Float, Double, samt String, ByteArray och Date. För komplexa typer som List eller Enum används TypeConverters — statiska konverteringsmetoder som omvandlar icke-standardtyper till format som stöds av SQLite.
Ja, Room stöder synkrona anrop utan korutiner, men de blockerar tråden där de körs. För asynkront arbete kan LiveData eller RxJava användas istället för korutiner. Google rekommenderar att använda korutiner som det primära sättet för asynkron dataåtkomst i nya projekt.
Om Room upptäcker en versionsskillnad i databasen och inte hittar en lämplig migrering, inträffar som standard ett IllegalStateException med en felbeskrivning. Utvecklaren kan åsidosätta detta beteende med metoden fallbackToDestructiveMigration, som kommer att ta bort den befintliga databasen och skapa en ny med förlust av alla data.
Room stöder relationer genom nästlade objekt med @Embedded-annoteringen och genom relationsklasser med @Relation-annoteringen. För komplexa frågor med tabellsammanslagningar används anpassade POJO-klasser vars fält fylls från @Query-resultat med JOIN-operatorn i SQL.
Sammanfattning
Vi utvecklar en mobil applikation nyckelfärdigt
IT Sectr skapar iOS- och Android-applikationer för startups och företag sedan 2017. Vi ger dig råd och föreslår den bästa lösningen.
Läs också