Android Room Tutorial: Simplifying How You Work with App Data

Informationer er nok den vigtigste ressource, som brugerne stoler på i forbindelse med apps. For app-udviklere fortæller disse oplysninger os, hvem brugeren er, hvilket gør os i stand til at give en god brugeroplevelse (UX). Ved at anvende forretningsregler på disse oplysninger definerer vi også den adfærd, som appen skal have. Disse oplysninger kan være følsomme og afsløre brugernes private data, så det er meget vigtigt, at vi håndterer dem korrekt for at sikre deres integritet, privatlivets fred og korrekt opbevaring. Inden for denne Android Room-tutorial vil vi vise dig, hvordan du lettere kan arbejde med disse data, samtidig med at du sikrer deres integritet og sikkerhed, alt sammen ved hjælp af Room. Room er en del af Android Architecture Components.

Har du brug for Room?

Når vi taler om opbevaring af oplysninger på en vedvarende måde på Android, er den vigtigste og “nemme” mulighed at bruge almindelig SQLite. Men for at få en god databaseimplementering ved hjælp af SQLite indebærer generering af en masse kode, som ikke giver reel værdi. Den arkitektur, der følger, er ofte ikke så ren eller klar, som den kunne være.

Du kunne også bruge objekt-relationel mapping, eller ORM, men du skal stadig manuelt definere og oprette databasen, underklasser SQLiteOpenHelper og oprette kontraktklasserne.

Så spørgsmålet er: Er der nogen måde at forenkle hele denne proces på? Svaret er: Ja, Room er din løsning.

Nærmere kig på Room, og hvordan det fungerer

Room er et af de vigtigste værktøjer i Android-arkitekturkomponenterne. Det blev lanceret på Google I/O 2016 og er et kraftfuldt værktøj til at gemme og manipulere oplysninger i Android-apps. Det giver en meget nem måde at arbejde med data på og sikrer altid deres sikkerhed og integritet.

Room er ikke en ORM; i stedet er det et helt bibliotek, der gør det nemmere for os at oprette og manipulere SQLite-databaser. Ved at bruge annotationer kan vi definere vores databaser, tabeller og operationer. Room vil automatisk oversætte disse annotationer til SQLite-instruktioner/forespørgsler for at udføre de tilsvarende operationer i databasemotoren.

De tre hovedkomponenter i Room er:

– Entity: Repræsenterer en tabel i Room-databasen. Den skal annoteres med @Entity.

– DAO: En grænseflade, der indeholder metoderne til at få adgang til databasen. Den er annoteret med @Dao.

– Database: Repræsenterer databasen. Det er et objekt, der har en forbindelse til SQLite-databasen, og alle operationer udføres gennem den. Det er annoteret med @Database.

Rumsarkitekturen ser således ud:

Android Room-arkitektur

Tå meget snak – lad os se på et eksempel

Lad os dykke ned i denne Android Room-tutorial. Forestil dig, at vi har brug for at oprette en app til at gemme din træningsrutine. Vi vil have fire enheder i vores database, som vi vil vise nedenfor. Al prøvekoden er skrevet ved hjælp af Kotlin (hvis du ikke kender Kotlin, eller hvis du vil lære mere, opfordrer jeg dig til at læse min artikel om det).

Den første ting, vi skal gøre, er at opdatere vores gradle-fil. Den skal se sådan her ud:

apply plugin: 'com.android.application'apply plugin: 'kotlin-android'apply plugin: 'kotlin-android-extensions'apply plugin: 'kotlin-kapt'android { //.. Omitted since it is not relevant for the example}dependencies { //... some dependencies were omitted due to they are not relevant for the example def room_version = "2.2.0-rc01" implementation "androidx.room:room-runtime:$room_version" kapt "androidx.room:room-compiler:$room_version" compile 'com.google.code.gson:gson:2.2.4' implementation 'io.reactivex.rxjava2:rxandroid:2.0.2' implementation 'io.reactivex.rxjava2:rxjava:2.1.17'}

Lad os se og analysere hver af de tre store Room-komponenter: Entities, DAO’er og Database.

Entities

I vores eksempel bruger vi fire entiteter: Køn, Motion, Rutine og Praktikant.

Køn.kt

Repræsenterer praktikantens køn

@Entitydata class Gender( @PrimaryKey(autoGenerate = true) val id: Int? = null, val name: String)

Ting at bemærke:

– Alle klasser, der repræsenterer en enhed i databasen, skal annoteres med @Entity

– Med annotationen @PrimaryKey(autoGenerate = true) angiver vi, at id’et er den primære nøgle for entiteten og skal autogenereres af databasemaskinen.

Exercise.kt

Repræsenterer en øvelse, der er en del af en rutine.

@Entitydata class Exercise( @PrimaryKey(autoGenerate = true) val exerciseId: Int, val name: String, val repetitions:Int, @ColumnInfo(name = "machine_name") val machineName: String, val liftedWeight: Int)

Det er værd at bemærke:

– Som standard bruger Room feltnavnene som kolonnenavne i databasen. Hvis du ønsker, at en kolonne skal have et andet navn, skal du tilføje annotationen @ColumnInfo til et felt.

Routine.kt

Grundlæggende en container med øvelser, der tilsammen skaber en øvelsesrutine.

@Entity(tableName = "traineeRoutine")data class Routine( @PrimaryKey(autoGenerate = true) val routineId: Int, @ColumnInfo(name = "due_day") val dueDay: Date, @TypeConverters(ListConverter::class) val exercises: List)

Ting, du skal bemærke:

– Når en klasse er annoteret med @Entity, vil navnet på tabellen være navnet på klassen. Hvis vi ønsker at bruge et andet navn, skal vi tilføje tableName-egenskaben sammen med @Entity-annotationen.

– Annotationen @TypeConverters skal bruges, når vi erklærer en egenskab, hvis type er en brugerdefineret klasse, en liste, en datatype eller en hvilken som helst anden type, som Room og SQL ikke ved, hvordan de skal serialiseres. I dette tilfælde bruger vi annotationen på klassefeltniveau, hvorved kun det pågældende felt vil kunne bruge den. Afhængigt af hvor annotationen er placeret, vil den opføre sig forskelligt, som forklaret her.

Trainee.kt

Det repræsenterer ejeren af rutinen.

@Entity(indices = , foreignKeys = , childColumns = )])data class Trainee( @PrimaryKey(autoGenerate = true) val id: Int, val name: String, val age: Int, val gender: Int?, @Embedded val routine: Routine)

DAO’er

Data Access Objects (DAO’er) bruges til at få adgang til vores data, når vi implementerer Room. Hver DAO skal indeholde et sæt metoder til at manipulere dataene (indsætte, opdatere, slette eller hente).

En DAO kan implementeres som en grænseflade eller som en abstrakt klasse. I vores tilfælde bruger vi en grænseflade. Da alle DAO’er stort set er identiske, vil vi kun vise en enkelt.

GenderDao.kt@Daointerface GenderDao { @Insert(onConflict = OnConflictStrategy.REPLACE) fun insertGender(gender: Gender) @Update fun updateGender(gender: Gender) @Delete fun deleteGender(gender: Gender) @Query("SELECT * FROM Gender WHERE name == :name") fun getGenderByName(name: String): List @Query("SELECT * FROM Gender") fun getGenders(): List}

Et par ting skal bemærkes:

– Alle DAO’er skal annoteres med @Dao.

– En funktion annoteret med @Insert, @Update eller @Delete skal modtage en instans af den ønskede klasse som parameter, der repræsenterer det objekt, som vi ønsker at indsætte, opdatere eller slette henholdsvis.

– I tilfælde af indsætnings- eller opdateringsoperationer kan vi bruge egenskaben onConflict til at angive, hvad der skal ske, når der opstår en konflikt, der udfører operationen. De strategier, der er tilgængelige at bruge, er: REPLACE, ABORT, FAIL, IGNORE og ROLLBACK.

– Hvis vi ønsker at få specifikke oplysninger fra en eller flere enheder, kan vi annotere en funktion med @Query og angive et SQL-script som parameter.

Database

Repræsenterer databasen. Den indeholder en forbindelse til den faktiske SQLite-database.

AppDatabase.kt@Database(entities = , version = 1)@TypeConverters(DateTypeConverter::class)abstract class AppDatabase : RoomDatabase() { abstract fun exerciseDao(): ExerciseDao abstract fun genderDao(): GenderDao abstract fun routineDao(): RoutineDao abstract fun traineeDao(): TraineeDao companion object { var INSTANCE: AppDatabase? = null fun getAppDataBase(context: Context): AppDatabase? { if (INSTANCE == null){ synchronized(AppDatabase::class){ INSTANCE = Room.databaseBuilder(context.applicationContext, AppDatabase::class.java, "myDB").build() } } return INSTANCE } fun destroyDataBase(){ INSTANCE = null } }}

Ting, der skal bemærkes her:

– Dette er en abstrakt klasse, der skal udvide RoomDatabase.

– Den skal annoteres med @Database, og den modtager en liste over entiteter med alle de klasser, der udgør databasen (alle disse klasser skal annoteres med @Entity). Vi skal også angive en databaseversion.

– Vi skal deklarere en abstrakt funktion for hver af de entiteter, der er medtaget i @Database-annotationen. Denne funktion skal returnere den tilsvarende DAO (en klasse annoteret med @Dao).

– Endelig erklærer vi et ledsageobjekt for at få statisk adgang til metoden getAppDataBase, som giver os en singletoninstans af databasen.

Type Converters

Type Converters bruges, når vi erklærer en egenskab, som Room og SQL ikke ved, hvordan den skal serialiseres. Lad os se et eksempel på, hvordan vi serialiserer en ‘date’-datatype.

DateTypeConverter.ktclass DateTypeConverter { @TypeConverter fun fromTimestamp(value: Long?): Date? { return if (value == null) null else Date(value) } @TypeConverter fun dateToTimestamp(date: Date?): Long? { return date?.time }}

Brug af Room-databasen

Nu skal vi se på et meget simpelt eksempel på, hvordan vi bruger Room-databasen, som vi lige har oprettet:

MainActivity.ktclass MainActivity : AppCompatActivity() { private var db: AppDatabase? = null private var genderDao: GenderDao? = null override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_main) Observable.fromCallable({ db = AppDatabase.getAppDataBase(context = this) genderDao = db?.genderDao() var gender1 = Gender(name = "Male") var gender2 = Gender(name = "Female") with(genderDao){ this?.insertGender(gender1) this?.insertGender(gender2) } db?.genderDao()?.getGenders() }).doOnNext({ list -> var finalString = "" list?.map { finalString+= it.name+" - " } tv_message.text = finalString }).subscribeOn(Schedulers.io()) .observeOn(AndroidSchedulers.mainThread()) .subscribe() }}

Hvad gør vi?

– Hentning af instansen af databasen og GenderDao.

– Oprettelse af to Gender-instanser: Mand og Kvinde.

– Indsættelse af de to oprettede instanser i databasen.

– Søgning i databasen for at få alle de køn, der er gemt i den.

– Sammenlægning af navnet på alle de køn, vi fik fra databasen, og indstilling af teksten i TextView med denne værdi.

Rum til at gøre mere med mindre

Rum er et af de vigtige elementer i de arkitektoniske Android-komponenter. Det giver os en meget robust ramme til at arbejde med og persistente oplysninger, der altid sikrer datasikkerhed og integritet. Det giver også brugervenlighed til udviklere, så de kan skrive læsbar og selvforklarende kode. Hvis du vil gøre mere med mindre kode og også sikre brugernes datasikkerhed, bør du bruge Room som dit persistenslag på din app.

Og det var det! Dette er næsten alt, hvad du behøver at vide for at oprette og bruge en database på Android med Room. Få hele koden til dette projekt her. Tak fordi du læste denne Android Room-tutorial!

Skriv et svar

Din e-mailadresse vil ikke blive publiceret.