Android Room Tutorial: Simplifying How You Work with App Data

Informatie is waarschijnlijk de belangrijkste bron die gebruikers met apps vertrouwen. Voor app-ontwikkelaars vertelt die informatie ons wie de gebruiker is, wat ons in staat stelt om een goede gebruikerservaring (UX) te bieden. Door bedrijfsregels op deze informatie toe te passen, definiëren we bovendien het gedrag dat de app moet vertonen. Deze informatie kan gevoelig zijn en de privégegevens van gebruikers blootleggen, dus het is heel belangrijk dat we er correct mee omgaan om de integriteit, privacy en juiste opslag te garanderen. In deze Android Room-tutorial laten we u zien hoe u gemakkelijker met deze gegevens kunt werken, terwijl u de integriteit en veiligheid ervan waarborgt, allemaal door Room te gebruiken. Room is onderdeel van de Android Architecture Components.

Do You Need Room?

Als we het hebben over het opslaan van informatie op een persistente manier op Android, de belangrijkste en “gemakkelijke” optie is het gebruik van SQLite. Voor een goede database-implementatie met SQLite moet echter veel code worden gegenereerd die niet echt waardevol is. De architectuur die volgt is vaak niet zo schoon of duidelijk als het zou kunnen zijn.

Je zou ook object-relational mapping, of ORM, kunnen gebruiken, maar dan moet je nog steeds handmatig de database definiëren en maken, SQLiteOpenHelper subclassen en de contractklassen maken.

Dus, de vraag is: Is er een manier om dit alles te vereenvoudigen? Het antwoord is: Ja, Kamer is uw oplossing.

Een nadere blik op Room en hoe het werkt

Room is een van de belangrijkste hulpmiddelen in de Android Architectural Components. Uitgebracht in de Google I/O 2016, het is een krachtig hulpmiddel om informatie op te slaan en te manipuleren op Android apps. Het biedt een zeer eenvoudige manier om met gegevens te werken en zorgt altijd voor de veiligheid en integriteit ervan.

Room is geen ORM; in plaats daarvan is het een hele bibliotheek die ons in staat stelt om SQLite-databases gemakkelijker te maken en te manipuleren. Door annotaties te gebruiken, kunnen we onze databases, tabellen en operaties definiëren. Room vertaalt deze annotaties automatisch in SQLite instructies/queries om de corresponderende bewerkingen in de database engine uit te voeren.

De drie belangrijkste onderdelen van Room zijn:

– Entity: Vertegenwoordigt een tabel binnen de Room Database. Het moet worden geannoteerd met @Entity.

– DAO: Een interface die de methoden bevat om toegang te krijgen tot de database. Het wordt geannoteerd met @Dao.

– Database: Vertegenwoordigt de database. Het is een object dat een verbinding met de SQLite database bevat, en alle operaties worden via dit object uitgevoerd. Het is geannoteerd met @Database.

Ruimte-architectuur ziet er als volgt uit:

Android Room-architectuur

Too much talk-Let’s look at an Example

Laten we eens duiken in deze Android Room-tutorial. Stel je voor dat we een app moeten maken om je fitness routine op te slaan. We gaan vier entiteiten in onze database hebben, zoals we hieronder zullen laten zien. Alle voorbeeldcode is geschreven met Kotlin (als je Kotlin niet kent of er meer over wilt leren, nodig ik je uit om mijn artikel erover te lezen).

Het eerste wat we moeten doen is ons gradle-bestand bijwerken. Het zou er zo uit moeten zien:

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'}

Laten we eens kijken en analyseren elk van de drie grote Room componenten: Entiteiten, DAO’s, en Database.

Entiteiten

Voor ons voorbeeld, gaan we vier entiteiten gebruiken: Geslacht, Oefening, Routine, en Stagiair.

Geest.kt

Vertegenwoordigt het geslacht van de stagiair

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

Dingen om op te merken:

– Alle klassen die een entiteit van de database vertegenwoordigen, moeten worden geannoteerd met @Entity

– Met de annotatie @PrimaryKey(autoGenerate = true) geven we aan dat de id de primaire sleutel van de entiteit is en automatisch door de database-engine moet worden gegenereerd.

Exercise.kt

Stelt een oefening voor die deel uitmaakt van een routine.

@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)

Dingen om op te merken:

– Standaard gebruikt Room de veldnamen als de kolomnamen in de database. Als u een kolom een andere naam wilt geven, voegt u de annotatie @ColumnInfo aan een veld toe.

Routine.kt

In principe een container met oefeningen die samen een oefenroutine vormen.

@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)

Dingen om op te merken:

– Wanneer een klasse is geannoteerd met @Entity, zal de naam van de tabel de naam van de klasse zijn. Als we een andere naam willen gebruiken, moeten we de eigenschap tableName toevoegen samen met de @Entity annotatie.

– De @TypeConverters annotatie moet worden gebruikt wanneer we een eigenschap declareren waarvan het type een aangepaste klasse is, een lijst, een datumtype, of een ander type waarvan Room en SQL niet weten hoe het te serialiseren. In dit geval gebruiken we de annotatie op het niveau van het klasseveld, zodat alleen dat veld de annotatie kan gebruiken. Afhankelijk van waar de annotatie wordt geplaatst, gedraagt deze zich anders, zoals hier wordt uitgelegd.

Trainee.kt

Het vertegenwoordigt de eigenaar van de routine.

@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’s

Data Access Objects (DAO’s) worden gebruikt om toegang te krijgen tot onze gegevens wanneer we Room implementeren. Elke DAO moet een reeks methoden bevatten om de gegevens te manipuleren (invoegen, bijwerken, verwijderen of ophalen).

Een DAO kan worden geïmplementeerd als een interface of als een abstracte klasse. In ons geval gebruiken we een interface. Aangezien alle DAO’s in principe identiek zijn, zullen we er slechts één laten zien.

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}

Een paar dingen om op te merken:

– Alle DAO’s moeten worden geannoteerd met @Dao.

– Een functie geannoteerd met @Insert, @Update, of @Delete moet een instantie van de gewenste klasse als parameter ontvangen, die het object vertegenwoordigt dat we respectievelijk willen invoegen, bijwerken, of verwijderen.

– In het geval van insert of update operaties, kunnen we de property onConflict gebruiken om aan te geven wat er moet gebeuren als er een conflict optreedt bij het uitvoeren van de operatie. De te gebruiken strategieën zijn: REPLACE, ABORT, FAIL, IGNORE, en ROLLBACK.

– Als we specifieke informatie uit een of meer entiteiten willen halen, kunnen we een functie annoteren met @Query en een SQL-script als parameter opgeven.

Database

Voorbeeldt de database. Het bevat een verbinding met de eigenlijke 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 } }}

Dingen om hier op te merken:

– Dit is een abstracte klasse die RoomDatabase moet uitbreiden.

– Het moet worden geannoteerd met @Database, en het ontvangt een lijst van entiteiten met alle klassen die de database samenstellen (al deze klassen moeten worden geannoteerd met @Entity). We moeten ook een database versie opgeven.

– We moeten een abstracte functie declareren voor elk van de entiteiten die in de @Database annotatie zijn opgenomen. Deze functie moet de overeenkomstige DAO (een met @Dao geannoteerde klasse) teruggeven.

– Tenslotte declareren we een companion-object om statische toegang te krijgen tot de methode getAppDataBase, die ons een singleton-instantie van de database geeft.

Type Converters

Type converters worden gebruikt wanneer we een eigenschap declareren waarvan Room en SQL niet weten hoe ze te serialiseren. Een voorbeeld van serialisatie van een datatype ‘datum’

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 }}

Gebruik van de Room-database

Nu een eenvoudig voorbeeld van het gebruik van de Room-database die we net hebben gemaakt:

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() }}

Wat doen we?

– De instantie van de database en GenderDao ophalen.

– Twee Gender-instanties maken: Male en Female.

– Het invoegen van de twee instanties in de database.

– Het bevragen van de database om alle geslachten die erin zijn opgeslagen.

– Het samenvoegen van de naam van alle geslachten die we uit de database hebben gehaald, en het instellen van de tekst van de TextView met die waarde.

Room om meer te doen met minder

Room is een van de belangrijke elementen van de Android Architectural Components. Het geeft ons een zeer robuust kader om mee te werken en persistente informatie, altijd zorgen voor beveiliging en integriteit van gegevens. Het biedt ook gebruiksgemak aan ontwikkelaars, zodat ze leesbare en voor zichzelf sprekende code kunnen schrijven. Als je meer wilt doen met minder code, en ook de beveiliging van gebruikersgegevens wilt waarborgen, zou je Room moeten gebruiken als persistentielaag voor je app.

En dat is het! Dit is bijna alles wat je moet weten om een database op Android te maken en te gebruiken met Room. Verkrijg de hele code voor dit project hier. Bedankt voor het lezen van deze Android Room tutorial!

Geef een antwoord

Het e-mailadres wordt niet gepubliceerd.