Vous recevez un e-mail de la part de Margaret vous expliquant un peu plus le projet :
Bienvenue à bord de "Planète Exploration" !
En tant que Dev Senior sur ce projet palpitant, je suis ravie de t'accueillir dans notre équipe dédiée à l'observation des étoiles. Notre mission est aussi vaste que l'univers lui-même, et tu as maintenant un rôle clé à jouer.
Voici un bref aperçu des étapes fondamentales de notre mission :
Récupération des données
Ton premier défi consiste à mettre en place un système robuste pour récupérer les données météorologiques. Assure-toi que notre application dispose d'un accès fiable aux informations météorologiques actuelles.
Analyse des données en cas de ciel dégagé
Quand le ciel est dégagé, c'est notre opportunité d'or ! Développe une logique qui analyse les données météorologiques pour déterminer si les conditions sont idéales pour l'observation astronomique.
Affichage des données
Le moment tant attendu ! Travaille sur l'interface utilisateur de l'application pour présenter de manière conviviale les résultats de notre analyse. Les utilisateurs doivent pouvoir visualiser rapidement les conditions météorologiques et planifier leurs séances d'observation.
Chacune de ces étapes contribue à notre objectif majeur : offrir aux passionnés d'astronomie une application fiable, intuitive et capable d'anticiper les moments propices à l'exploration du ciel.
Voici un schéma récapitulatif des grandes étapes :
Cordialement,
Margaret Hamilton, Dev Senior – Projet "Planète Exploration"
Initialisez le projet
Maintenant que vous maîtrisez l’utilisation des API, nous allons pouvoir passer à notre projet Android et à sa configuration, en particulier l’interception des données.
Lorsqu'une requête réseau est effectuée, le processus d'interception commence. Les données renvoyées par le serveur au format JSON nécessitent une manipulation. C'est ici que des outils tels que la bibliothèque Moshi entrent en jeu. Moshi simplifie la désérialisation des données JSON en objets Kotlin, offrant ainsi une manière simple de traiter les réponses du serveur. La désérialisation du JSON devient cruciale à ce stade, transformant une séquence de caractères JSON en une structure de données utilisable par notre application.
Créez le projet Android
Créez un projet StellarForecast avec l’interface d’Android Studio, comme vous l’avez déjà fait dans le chapitre précédent. Nous nommerons notre package “com.openclassrooms.openweather” pour ce projet.
Nous allons ajouter plusieurs dépendances à notre projet. Ajoutez les lignes suivantes dans le fichier build.gralde (Module::
plugins {
id("com.android.application")
id("org.jetbrains.kotlin.android")
kotlin("kapt")
id("com.google.dagger.hilt.android")
}
Modifions aussi le SDK de référence à utiliser :
compileSdk = 34
targetSdk = 34
Après l’accolade fermante de buildTypes, activons la fonctionnalité de ViewBinding en ajoutant ceci :
buildFeatures {
viewBinding = true
}
Nous définissons ici plusieurs librairies que nous allons utiliser durant le cours. Remplacez le code déjà existant pour dependencies. Les premières sont purement liées au fonctionnement d’Android. La dépendance com.squareup.moshi:moshi-kotlin fournit le support pour l'utilisation de Moshi avec Kotlin alors que retrofit2 fournit le support de Retrofit. Nous ajoutons quelques libraires de tests pour finir.
dependencies {
// AndroidX Core Components
implementation("androidx.core:core-ktx:1.12.0")
implementation("androidx.appcompat:appcompat:1.6.1")
implementation("com.google.android.material:material:1.11.0")
implementation("androidx.lifecycle:lifecycle-runtime-ktx:2.6.2")
// RecyclerView and Activity Components
implementation("androidx.recyclerview:recyclerview:1.3.2")
implementation("androidx.constraintlayout:constraintlayout:2.1.4")
implementation("androidx.activity:activity-ktx:1.8.2")
// Dagger Hilt Dependency Injection
implementation("com.google.dagger:hilt-android:2.50")
kapt("com.google.dagger:hilt-android-compiler:2.50")
// Moshi JSON Library
implementation("com.squareup.moshi:moshi-kotlin:1.15.0")
// Retrofit for Network Requests
implementation("com.squareup.retrofit2:retrofit:2.9.0")
implementation("com.squareup.okhttp3:logging-interceptor:4.10.0")
implementation("com.squareup.retrofit2:converter-moshi:2.9.0")
// Testing Dependencies
testImplementation("junit:junit:4.13.2")
androidTestImplementation("androidx.test.ext:junit:1.1.5")
androidTestImplementation("androidx.test.espresso:espresso-core:3.5.1")
}
kapt {
correctErrorTypes = true
}
Dans le fichier build.gradle (Project :StellarForecast), ajoutez ceci :
plugins {
// Il y a déjà des plugins pré-configurés, n'y touchez pas.
//A rajouter
id("com.google.dagger.hilt.android") version "2.50" apply false
}
Une fois que vous avez rajouté ces lignes, n’oubliez pas de synchroniser votre projet. Cela permet au projet de récupérer les librairies qui se trouvent en ligne. On peut voir les librairies comme des extensions fournies par la communauté, et que vous pouvez ajouter facilement à votre projet.
Configurez Hilt
Créez une classe nommée “StellarForecastApp” en faisant un clic droit sur le dossier nommé “com.openclassrooms.stellarforecast”, et copiez :
import dagger.hilt.android.HiltAndroidApp
import android.app.Application
@HiltAndroidApp
class StellarForecastApp : Application()
En annotant la classe d'application Android avec @HiltAndroidApp, on indique à Hilt de générer le code nécessaire pour créer un composant spécifique à l'application, et gérer le cycle de vie des dépendances. Cela simplifie l'intégration de Hilt dans l'application, facilitant ainsi l'injection de dépendances à l'échelle de l'application en automatisant la configuration et la gestion des composants.
Puis nous devons rajouter cette classe dans le manifeste de l’application ; nous pouvons rajouter le paramètre name
juste en dessous de l’ouverture de l’application.
<application
android:name=".StellarForecastApp"
Interceptez les données
Une fois que notre projet est configuré, nous allons pouvoir désérialiser les réponses de notre requête réseau.
L'objectif est de créer des classes qui interceptent la structure des réponses provenant de l'API. En utilisant Kotlin, un langage orienté objet, il est plus avantageux de modéliser ces données en objets plutôt que de manipuler de longues chaînes de texte.
Créez une classe nommée data.response.OpenWeatherForecastsResponse
, de type Data Class, et copiez le code suivant :
import com.squareup.moshi.Json
import com.squareup.moshi.JsonClass
@JsonClass(generateAdapter = true)
data class OpenWeatherForecastsResponse(
@Json(name = "list")
val forecasts: List<ForecastResponse>,
) {
@JsonClass(generateAdapter = true)
data class ForecastResponse(
@Json(name = "dt")
val time: Int,
@Json(name = "main")
val temperature: TemperatureResponse,
@Json(name = "weather")
val weather: List<WeatherResponse>,
) {
@JsonClass(generateAdapter = true)
data class TemperatureResponse(
@Json(name = "temp")
val temp: Double,
)
@JsonClass(generateAdapter = true)
data class WeatherResponse(
@Json(name = "id")
val id: Int,
@Json(name = "main")
val title: String,
@Json(name = "description")
val description: String
)
}
//TODO 1 : Implement toDomainModel function
}
Cette classe permet de désérialiser les réponses de l'API OpenWeather. Ces classes sont annotées avec l'annotation @JsonClass
pour indiquer à la bibliothèque Moshi de générer automatiquement les adaptateurs nécessaires. L'annotation @JsonClass(generateAdapter = true)
indique la génération automatique d'un adaptateur JSON pour ces classes.
OpenWeatherForecastsResponse :
Cette classe principale représente la réponse complète de l'API OpenWeather pour les prévisions météorologiques. Elle contient une liste de prévisions (forecasts
), chaque prévision étant représentée par la classe interneForecastResponse
.
ForecastResponse :
Cette classe interne représente la prévision météorologique pour une heure donnée. Elle contient des informations telles que l'horaire (time
), la température (temperature
) et les détails météorologiques (weather
).
TemperatureResponse :
Cette classe interne deForecastResponse
représente les données de température pour une prévision. Elle contient la température réelle en Kelvin (temp
).WeatherResponse :
Cette classe interne deForecastResponse
représente les détails météorologiques pour une prévision. Elle contient l'identifiant (id
), le titre (title
) et la description (description
) de l'état météorologique.
En résumé, cette classe Kotlin fournit une représentation structurée des données reçues de l'API OpenWeather, et l'utilisation des annotations @JsonClass
permet de simplifier la sérialisation et la désérialisation JSON via Moshi.
Nous reviendrons plus tard sur la ligne //TODO
.
Transformez les données en objet de confiance
Maintenant que nous disposons de notre objet OpenWeatherForecastsResponse, nous devons nous assurer de la qualité des informations en les transformant en un objet dans lequel nous aurons confiance. Jusqu'à maintenant, nous n’avons fait qu’intercepter les données transmises par l’API sans les contrôler.
La transformation des données est cruciale pour garantir la cohérence, la compréhension et la pertinence des informations reçues d'une source externe. Les données provenant du serveur peuvent être dans un format spécifique, mais pour les utiliser de manière optimale dans l'application, il est souvent nécessaire de les transformer en une structure plus adaptée, souvent appelée le "DomainModel".
Cette transformation a pour but de convertir les données brutes provenant du serveur en une représentation interne plus adaptée à l'application. Par exemple, dans le contexte d'une application météo, la transformation des données peut inclure la conversion des unités de température ou la gestion des valeurs null.
Les objets ditsModel
ne représentent pas à l’identique les objetsResponse
car nous n’avons pas toujours besoin des mêmes informations.
Nous pouvons créer un objet nommé domain.model.WeatherReportModel
qui est une data class, et qui ne comporte que les données dont nous avons besoin :
import java.util.Calendar
data class WeatherReportModel(
val isGoodForStargazing: Boolean,
val date: Calendar,
val temperatureCelsius: Int,
val weatherTitle: String,
val weatherDescription: String
)
Notre objet Model est plus simple et possède des paramètres plus explicites que notre objet Response, car nous avons un contrôle total dessus. Nous ne gardons que ce dont nous aurons besoin dans notre application.
Maintenant que nous avons défini notre objetWeatherReportModel
, nous devons le lier à notre objetOpenWeatherForecastsResponse
. Pour ce faire, nous allons utiliser une méthode interne à notre classe OpenWeatherForecastsResponse
, que nous allons nommertoDomainModel()
. Copiez le code suivant à la place de la ligne //TODO 1 : Implement toDomainModel function
:
fun toDomainModel(): List<WeatherReportModel> {
return forecasts.map { forecast ->
val calendar = Calendar.getInstance().apply { timeInMillis = forecast.time * 1000L }
// Check if the sky is clear (IDs 800 to 802 indicate clear sky conditions)
val isClearSky = forecast.weather.isNotEmpty() && forecast.weather[0].id in 800..802
// Get the hour of the date and determine if it's night
val hourOfDay = calendar.get(Calendar.HOUR_OF_DAY)
val isNight = hourOfDay < 6 || hourOfDay >= 18
// Convert temperature to Celsius
val temperatureCelsius = (forecast.temperature.temp - 273.15).toInt()
WeatherReportModel(
isGoodForStargazing = isClearSky && isNight,
date = calendar,
temperatureCelsius = temperatureCelsius,
weatherTitle = forecast.weather[0].title,
weatherDescription = forecast.weather[0].description
)
}
}
La fonction toDomainModel()
effectue la transformation des données de prévisions météorologiques provenant d'une source externe en une liste d'objets WeatherReportModel
. À l'aide de la fonction map
, chaque objet de prévision ( forecast
) dans la liste d'origine ( forecasts
) est converti en un nouvel objet WeatherReportModel
.
Il nous reste plus qu’à intégrer Retrofit dans notre application pour effectuer des requêtes réseau. C’est ce que nous verrons dans le chapitre suivant. Une fois cette étape réalisée, notre application sera parfaitement en mesure d'établir des communications avec le réseau, ouvrant ainsi la porte à l'échange de données !
À vous de jouer
Contexte
Alors que les températures descendent, votre supérieur vous confie la mission de préparer ce projet pour la saison de ski imminente. Pour ce faire, nous devons collecter des informations à partir d'une API météorologique, et c'est là que commence notre aventure. Nous aurons besoin de créer un objet Response et de développer son pendant, l'objet Model, pour faciliter la manipulation des données météorologiques.
L'algorithme que nous utiliserons pour décider du moment idéal pour faire fonctionner nos canons à neige exigera que notre objet Model contienne certaines valeurs cruciales :
l'heure de la donnée météorologique ;
la température ;
l'identifiant du type de météo actuel.
Consignes
Votre mission actuelle consiste à :
Créer un nouveau projet nommé "IceRush" en y ajoutant les dépendances nécessaires.
Implémenter une classe pour intercepter les données requises dans le format JSON.
Concevoir une classe modèle qui agit en tant qu'intermédiaire entre les données brutes et leur utilisation dans notre algorithme. Votre modèle devra contenir les variables suivantes :
Corrigé
La correction est publiée à cette adresse sur un projet GitHub :
La configuration du projet.
La déclaration de l’objet Response.
La déclaration de l’objet Model.
En résumé
L'étape d'interception des données intervient après chaque requête réseau. Son objectif est de récupérer et de traiter les données renvoyées par le serveur. Pour ce faire, on utilise la désérialisation avec Moshi, qui simplifie ce processus.
L'injection de dépendance avec Hilt vise à simplifier la gestion des dépendances dans l'application. Cela se fait à travers l'annotation @HiltAndroidApp, la création de modules avec @Module et @Provides, permettant à Hilt de générer automatiquement le code nécessaire.
La transformation des données intervient après la désérialisation. Son objectif est d'assurer la cohérence des données en les convertissant en objets adaptés et fiables. Par exemple, la création de l'objet WeatherModel pour représenter les données météorologiques de manière plus claire.
Utilisez des plugins comme "JSON to Kotlin Class" pour simplifier le codage. Ces outils automatisent des tâches fastidieuses, économisent du temps et assurent une précision accrue.
Dans la continuité de notre exploration, plongeons maintenant dans la configuration de Retrofit et la création des endpoints pour renforcer notre capacité à faire des appels réseau et à échanger des données avec les serveurs.