Temps : 2h.
Difficulté : ***
Pre-requis : Android 101,
Kotlin, Bonne pratique Gradle
Dans ce challenge, une base de données contenant des plages est implémentée avec la bibliothèque Room
et une architecture légère.
En particulier, une BDD locale est nécessaire pour enregistrer les plages préférées de l'utilisateur.
Voici les étapes d'implémentation :
> Configurer le projet Android Studio
> Créer le modèle de données
> Créer, remplir la BDD Room
> Instancier la BDD
> Tester la BDD
Pour commencer il s'agit de configurer un projet Android Studio pour utiliser Room.
Plus précisément cela consiste à modifier les fichiers Gradle.
Gradle Script
du module app/
,
build.gradle(Module: RoomApp)
, ajouter le plugin kapt :
plugins {
id 'com.android.application'
id 'kotlin-android'
id 'kotlin-kapt'
}
dependencies
de build.gradle(Module: RoomApp)
:
dependencies {
implementation "androidx.appcompat:appcompat:$appCompatVersion"
implementation "com.google.android.material:material:$materialVersion"
implementation "androidx.constraintlayout:constraintlayout:$constraintLayoutVersion"
testImplementation 'junit:junit:4.+'
androidTestImplementation "androidx.test.ext:junit:$androidxJunitVersion"
androidTestImplementation "androidx.test.espresso:espresso-core:$espressoVersion"
implementation("androidx.room:room-runtime:$roomVersion")
annotationProcessor("androidx.room:room-compiler:$roomVersion")
// To use Kotlin annotation processing tool (kapt)
kapt("androidx.room:room-compiler:$roomVersion")
// optional - Kotlin Extensions and Coroutines support for Room
implementation("androidx.room:room-ktx:$roomVersion")
}
Gradle Script
du projet, build.gradle(Project: RoomApp)
,
ajouter les variables de version dans la section buildscript
:
ext {
kotlin_version = '1.5.31'
appCompatVersion = '1.4.1'
constraintLayoutVersion = '2.1.3'
materialVersion = '1.5.0'
roomVersion = '2.4.1'
// testing
junitVersion = '4.13.2'
espressoVersion = '3.4.0'
androidxJunitVersion = '1.1.3'
}
Le but est de créer une entité représentant un objet plage.
Une plage a :
Beach
,
avec ces annotations Room :
@Entity(tableName = "beach_table")
class Beach(
@PrimaryKey @ColumnInfo(name = "name") val name: String,
@ColumnInfo(name = "favorite") val favorite: Boolean
)
import androidx.room.ColumnInfo
import androidx.room.Entity
import androidx.room.PrimaryKey
BeachDao
:
@Dao
interface BeachDao {
@Query("SELECT * FROM beach_table ORDER BY name ASC")
fun getBeach(): List<Beach>
@Insert(onConflict = OnConflictStrategy.IGNORE)
suspend fun insert(beach: Beach)
@Query("DELETE FROM beach_table")
suspend fun deleteAll()
}
import androidx.room.Dao
import androidx.room.Insert
import androidx.room.OnConflictStrategy
import androidx.room.Query
L'objectif est de créer la BDD Room et créer la classe pour instancier et remplir la BDD.
BeachRoomDatabase
:
@Database(entities = arrayOf(Beach::class), version = 1, exportSchema = false)
abstract class BeachRoomDatabase : RoomDatabase() {
abstract fun beachDao(): BeachDao
companion object {
// Singleton prevents multiple instances of database opening at the
// same time.
@Volatile
private var INSTANCE: BeachRoomDatabase? = null
fun getDatabase(context: Context, scope: CoroutineScope): BeachRoomDatabase {
// if the INSTANCE is not null, then return it,
// if it is, then create the database
return INSTANCE ?: synchronized(this) {
val instance = Room.databaseBuilder(
context.applicationContext,
BeachRoomDatabase::class.java,
"beach_database"
)
.addCallback(BeachDatabaseCallback(scope))
.build()
INSTANCE = instance
// return instance
instance
}
}
}
}
BeachDatabaseCallback
dans BeachRoomDatabase
:
private class BeachDatabaseCallback(private val scope: CoroutineScope) :
RoomDatabase.Callback() {
override fun onCreate(db: SupportSQLiteDatabase) {
super.onCreate(db)
INSTANCE?.let { database ->
scope.launch {
var beachDao = database.beachDao()
beachDao.deleteAll()
// Populate the db:
var beach = Beach("Plage des Rochers, Cannes")
beachDao.insert(beach)
}
}
}
}
Note: le numéro de version
doit être incrementé lorsque des changements sont fait sur la BDD.
Il s'agit d'instancier la BDD Room via la classe Application
.
BeachApplication
:
class BeachApplication : Application() {
val applicationScope = CoroutineScope(SupervisorJob())
val database by lazy { BeachRoomDatabase.getDatabase(this, applicationScope) }
}
Dans cette ultime étape, des données sont manipulées et analisées via la console logcat (des plages sont ajoutées à la volée dans MainActivity
).
MainActivity
:
//In the repository for component architecture with Flow stuff (dynamic display)
fun showBeach() {
(application as BeachApplication).applicationScope.launch {
allBeach = (application as BeachApplication).database.beachDao().getBeach()
for (i in 0..(allBeach.size - 1)) {
Log.i(
MainActivity::class.java.simpleName,
"BEACH My favorite beach ${allBeach[i].name}"
)
}
}
}
//In the ViewModel for component architecture
fun addBeach() {
val beach = Beach("Praia de Carcavelos, Lisbon")
(application as BeachApplication).applicationScope.launch {
insert(beach)
}
}
// In the repository for component architecture
suspend fun insert(beach: Beach) {
(application as BeachApplication).database.beachDao()
.insert(beach)
}
onCreate()
de MainActivity
:
showBeach()
addBeach()
addBeach()
, exécuter.Finallement, ce challenge est juste une introduction à la BDD avec Room.
Globalement, l'idée est d'utiliser un RecyclerView pour afficher les plages;
ensuite d'ajouter un bouton étoile pour indiquer une préférence de plage (ou un FAB pour permettre à l'utilisateur d'ajouter des plages en BDD).
Obtenez les codes sources dans les Ressources supplémentaires
de ce thème sur la BDD.