Informația este probabil cea mai importantă resursă în care utilizatorii au încredere cu aplicațiile. Pentru dezvoltatorii de aplicații, aceste informații ne spun cine este utilizatorul, ceea ce ne împuternicește să oferim o experiență bună pentru utilizator (UX). De asemenea, prin aplicarea unor reguli de afaceri la aceste informații, definim comportamentul pe care ar trebui să îl aibă aplicația. Aceste informații pot fi sensibile și pot expune datele private ale utilizatorilor, așa că este foarte important să le gestionăm corect pentru a le asigura integritatea, confidențialitatea și stocarea corectă. În cadrul acestui tutorial Android Room, vă vom arăta cum puteți lucra mai ușor cu aceste date, asigurând în același timp integritatea și securitatea lor, totul prin utilizarea Room. Room face parte din componentele arhitecturii Android Architecture Components.
Aveți nevoie de Room?
Când vorbim despre stocarea informațiilor într-un mod persistent pe Android, cea mai importantă și „ușoară” opțiune este să folosim SQLite simplu. Cu toate acestea, pentru a avea o implementare bună a bazei de date folosind SQLite implică generarea unei cantități mari de cod care nu oferă valoare reală. Arhitectura care urmează de multe ori nu este atât de curată sau clară pe cât ar putea fi.
Ați putea, de asemenea, să folosiți cartografierea obiect-relațională, sau ORM, dar tot va trebui să definiți și să creați manual baza de date, subclasând SQLiteOpenHelper și creând clasele de contract.
Deci, întrebarea este: Există vreo modalitate de a simplifica tot acest proces? Răspunsul este: Da, camera este soluția dumneavoastră.
O privire mai atentă la Room și la modul în care funcționează
Room este unul dintre cele mai importante instrumente din componentele arhitecturale Android. Lansat în cadrul Google I/O 2016, este un instrument puternic de stocare și manipulare a informațiilor în aplicațiile Android. Oferă o modalitate foarte ușoară de a lucra cu datele și asigură întotdeauna securitatea și integritatea acestora.
Room nu este un ORM; în schimb, este o întreagă bibliotecă care ne permite să creăm și să manipulăm mai ușor bazele de date SQLite. Prin utilizarea adnotărilor, ne putem defini bazele de date, tabelele și operațiile. Room va traduce automat aceste adnotări în instrucțiuni/interogări SQLite pentru a efectua operațiile corespunzătoare în motorul bazei de date.
Cele trei componente majore ale Room sunt:
– Entity: Reprezintă un tabel în cadrul bazei de date Room. Ar trebui să fie adnotată cu @Entity.
– DAO: O interfață care conține metodele de accesare a bazei de date. Este adnotată cu @Dao.
– Database: Reprezintă baza de date. Este un obiect care deține o conexiune la baza de date SQLite, iar toate operațiile sunt executate prin intermediul acesteia. Este adnotat cu @Database.
Arhitectura camerei arată în felul următor:
Prea multă vorbărie – Să ne uităm la un exemplu
Să ne scufundăm în acest tutorial Android Room. Imaginați-vă că trebuie să creăm o aplicație pentru a vă stoca rutina de la sala de sport. Vom avea patru entități în baza noastră de date, așa cum vom arăta mai jos. Tot codul de exemplu este scris folosind Kotlin (dacă nu cunoașteți Kotlin sau doriți să aflați mai multe, vă invit să citiți articolul meu despre acesta).
Primul lucru pe care trebuie să-l facem este să actualizăm fișierul nostru gradle. Acesta ar trebui să arate în felul următor:
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'}
Să vedem și să analizăm fiecare dintre cele trei componente majore ale camerei: Entități, DAO-uri și Baza de date.
Entități
Pentru exemplul nostru, vom folosi patru entități: Gender, Exercise, Routine și Trainee.
Gender.kt
Reprezintă genul stagiarului
@Entitydata class Gender( @PrimaryKey(autoGenerate = true) val id: Int? = null, val name: String)
Ce lucruri de observat:
– Toate clasele care reprezintă o entitate a bazei de date trebuie să fie adnotate cu @Entity
– Cu adnotarea @PrimaryKey(autoGenerate = true) indicăm că id-ul este cheia primară a entității și ar trebui să fie generat automat de către motorul bazei de date.
Exercise.kt
Reprezintă un exercițiu care face parte dintr-o rutină.
@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)
Lucruri de observat:
– În mod implicit, Room utilizează numele câmpurilor ca nume de coloane în baza de date. Dacă doriți ca o coloană să aibă un nume diferit, adăugați adnotarea @ColumnInfo la un câmp.
Routine.kt
În principiu, un container de exerciții care împreună creează o rutină de exerciții.
@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)
Ceea ce trebuie observat:
– Când o clasă este adnotată cu @Entity, numele tabelului va fi numele clasei. Dacă dorim să folosim un nume diferit, trebuie să adăugăm proprietatea tableName împreună cu adnotarea @Entity.
– Adnotarea @TypeConverters trebuie să fie utilizată atunci când declarăm o proprietate al cărei tip este o clasă personalizată, o listă, un tip de dată sau orice alt tip pe care Room și SQL nu știu cum să îl serializeze. În acest caz, folosim adnotarea la nivelul câmpului de clasă, ceea ce înseamnă că doar câmpul respectiv va putea să o folosească. În funcție de locul în care este plasată adnotarea, aceasta se va comporta diferit, așa cum este explicat aici.
Trainee.kt
Reprezintă proprietarul rutinei.
@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)
DAOs
Data Access Objects (DAOs) sunt folosite pentru a accesa datele noastre atunci când implementăm Room. Fiecare DAO trebuie să includă un set de metode de manipulare a datelor (inserare, actualizare, ștergere sau obținere).
Un DAO poate fi implementat ca o interfață sau ca o clasă abstractă. În cazul nostru, folosim o interfață. Deoarece toate DAO-urile sunt practic identice, vom arăta doar unul.
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}
Câteva lucruri de observat:
– Toate DAO-urile trebuie să fie adnotate cu @Dao.
– O funcție adnotată cu @Insert, @Update sau @Delete trebuie să primească ca parametru o instanță a clasei dorite, care reprezintă obiectul pe care dorim să îl inserăm, să îl actualizăm sau, respectiv, să îl ștergem.
– În cazul operațiilor de inserare sau de actualizare, putem folosi proprietatea onConflict pentru a indica ce trebuie să facem atunci când apare un conflict la efectuarea operației. Strategiile disponibile pentru utilizare sunt: REPLACE, ABORT, FAIL, IGNORE și ROLLBACK.
– Dacă dorim să obținem informații specifice de la una sau mai multe entități, putem adnota o funcție cu @Query și să furnizăm ca parametru un script SQL.
Database
Reprezintă baza de date. Deține o conexiune la baza de date SQLite reală.
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 } }}
Cele de observat aici:
– Aceasta este o clasă abstractă care trebuie să extindă RoomDatabase.
– Trebuie să fie adnotată cu @Database și primește o listă de entități cu toate clasele care compun baza de date (toate aceste clase trebuie să fie adnotate cu @Entity). De asemenea, trebuie să furnizăm o versiune a bazei de date.
– Trebuie să declarăm o funcție abstractă pentru fiecare dintre entitățile incluse în adnotarea @Database. Această funcție trebuie să returneze DAO-ul corespunzător (o clasă adnotată cu @Dao).
– În cele din urmă, declarăm un obiect companion pentru a obține acces static la metoda getAppDataBase, care ne oferă o instanță singleton a bazei de date.
Convertoare de tip
Convertoarele de tip sunt utilizate atunci când declarăm o proprietate pe care Room și SQL nu știu cum să o serializeze. Să vedem un exemplu de cum se serializează un tip de date ‘date’.
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 }}
Utilizarea bazei de date Room
Acum să vedem un exemplu foarte simplu de utilizare a bazei de date Room pe care tocmai am creat-o:
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() }}
Ce facem?
– Obținerea instanței bazei de date și a lui GenderDao.
– Crearea a două instanțe Gender: Male și Female.
– Inserarea celor două instanțe create în baza de date.
– Interogarea bazei de date pentru a obține toate genurile stocate în ea.
– Unirea numelui tuturor genurilor pe care le-am obținut din baza de date și setarea textului din TextView cu această valoare.
Room pentru a face mai mult cu mai puțin
Room este unul dintre elementele importante ale componentelor arhitecturale Android. Acesta ne oferă un cadru foarte robust cu care să lucrăm și informații persistente, asigurând întotdeauna securitatea și integritatea datelor. De asemenea, oferă ușurință de utilizare dezvoltatorilor, astfel încât aceștia să poată scrie cod lizibil și autoexplicativ. Dacă doriți să faceți mai mult cu mai puțin cod și, de asemenea, să asigurați securitatea datelor utilizatorilor, ar trebui să folosiți Room ca strat de persistență în aplicația dumneavoastră.
Și asta e tot! Acesta este aproape tot ce trebuie să știți pentru a crea și utiliza o bază de date pe Android cu Room. Obțineți întregul cod pentru acest proiect aici. Vă mulțumim că ați citit acest tutorial Android Room!