Cet atelier de programmation fait partie du cours Principes de base d'Android en Kotlin. Vous tirerez pleinement parti de ce cours en suivant les ateliers de programmation dans l'ordre. Tous les ateliers de programmation du cours sont listés sur la page de destination des ateliers de programmation Principes de base d'Android en Kotlin.
Introduction
Presque toutes les applications Android que vous créez devront se connecter à Internet à un moment donné. Dans cet atelier de programmation et ceux qui suivent, vous allez créer une application qui se connecte à un service Web pour récupérer et afficher des données.  Vous vous appuierez également sur ce que vous avez appris dans les ateliers de programmation précédents sur ViewModel, LiveData et RecyclerView. 
Dans cet atelier de programmation, vous utiliserez des bibliothèques développées par la communauté pour créer la couche réseau. Cela simplifie grandement la récupération des données et des images, et permet également à l'application de respecter certaines bonnes pratiques d'Android, comme le chargement des images sur un thread d'arrière-plan et la mise en cache des images chargées. Pour les sections asynchrones ou non bloquantes du code, comme la communication avec la couche de services Web, vous allez modifier l'application pour qu'elle utilise les coroutines Kotlin. Vous allez également mettre à jour l'interface utilisateur de l'application si Internet est lent ou indisponible afin d'informer l'utilisateur de ce qui se passe.
Ce que vous devez déjà savoir
- Vous savez créer et utiliser des fragments.
 - Naviguer entre les fragments et utiliser 
safeArgspour transmettre des données entre les fragments - Utiliser des composants d'architecture, y compris les transformations 
ViewModel,ViewModelProvider.Factory,LiveDataetLiveData. - Utiliser des coroutines pour les tâches de longue durée
 
Points abordés
- Qu'est-ce qu'un service Web REST ?
 - Se connecter à un service Web REST sur Internet à l'aide de la bibliothèque Retrofit et obtenir une réponse.
 - Utiliser la bibliothèque Moshi pour analyser la réponse JSON dans un objet de données.
 
Objectifs de l'atelier
- Modifier une application de démarrage pour effectuer une requête API de service Web et gérer la réponse.
 - Implémenter une couche réseau pour votre application à l'aide de la bibliothèque Retrofit.
 - Analyser la réponse JSON du service Web dans les données Live de votre application avec la bibliothèque Moshi.
 - Utiliser la prise en charge des coroutines de Retrofit pour simplifier le code.
 
Dans cet atelier de programmation (et les suivants), vous allez utiliser une application de démarrage appelée MarsRealEstate, qui affiche les propriétés à vendre sur Mars. Cette application se connecte à un service Web pour récupérer et afficher les données des propriétés, y compris des informations telles que le prix et si la propriété est disponible à la vente ou à la location. Les images représentant chaque propriété sont de vraies photos de Mars prises par les rovers de la NASA.

La version de l'application que vous allez créer ne présente pas beaucoup de flashs visuels. Elle se concentre sur la partie réseau de l'application pour se connecter à Internet et télécharger les données brutes de la propriété via un service Web. Pour vous assurer que les données sont correctement récupérées et analysées, il vous suffit d'imprimer le nombre de propriétés sur Mars dans une vue de texte :

.
L'architecture de l'application MarsRealEstate comporte deux modules principaux :
- Fragment de présentation, qui contient une grille de vignettes d'images de propriétés, construite avec un 
RecyclerView. - Fragment de vue détaillée contenant des informations sur chaque propriété.
 

L'application comporte un ViewModel pour chaque fragment. Pour cet atelier de programmation, vous allez créer une couche pour le service réseau, et ViewModel communiquera directement avec cette couche réseau.  Ce processus est semblable à celui que vous avez effectué dans les ateliers de programmation précédents lorsque ViewModel communiquait avec la base de données Room.
La vue d'ensemble ViewModel est responsable de l'appel réseau pour obtenir les informations sur l'immobilier sur Mars. Le détail ViewModel contient des informations sur le bien immobilier martien affiché dans le fragment de détail.  Pour chaque ViewModel, vous utilisez LiveData avec une liaison de données compatible avec le cycle de vie pour mettre à jour l'interface utilisateur de l'application lorsque les données changent.  
Vous utilisez le composant Navigation pour naviguer entre les deux fragments et pour transmettre la propriété sélectionnée en tant qu'argument.
Dans cette tâche, vous allez télécharger et exécuter l'application de démarrage pour MarsRealEstate, et vous familiariser avec la structure du projet.
Étape 1 : Explorer les fragments et la navigation
- Téléchargez l'application de démarrage MarsRealEstate et ouvrez-la dans Android Studio.
 - Examinez 
app/java/MainActivity.kt. L'application utilise des fragments pour les deux écrans. La seule tâche de l'activité est donc de charger la mise en page de l'activité. - Examinez 
app/res/layout/activity_main.xml. La mise en page de l'activité est l'hôte des deux fragments, définis dans le fichier de navigation. Cette mise en page instancie unNavHostFragmentet son contrôleur de navigation associé avec la ressourcenav_graph. - Ouvrez 
app/res/navigation/nav_graph.xml. Vous pouvez voir ici la relation de navigation entre les deux fragments. Le graphique de navigationStartDestinationpointe versoverviewFragment. Le fragment de vue d'ensemble est donc instancié au lancement de l'application. 
Étape 2 : Explorer les fichiers sources Kotlinet la liaison de données
- Dans le volet Project (Projet), développez app > java. Notez que l'application MarsRealEstate comporte trois dossiers de package : 
detail,networketoverview. Ils correspondent aux trois principaux composants de votre application : les fragments de présentation et de détails, ainsi que le code de la couche réseau.
   - Ouvrez 
app/java/overview/OverviewFragment.kt.OverviewFragmentinitialiseOverviewViewModelde manière différée, ce qui signifie queOverviewViewModelest créé la première fois qu'il est utilisé. - Examinez la méthode 
onCreateView(). Cette méthode augmente la mise en page defragment_overviewà l'aide de la liaison de données, définit le propriétaire du cycle de vie de la liaison sur lui-même (this) et définit la variableviewModeldans l'objetbinding. Étant donné que nous avons défini le propriétaire du cycle de vie, touteLiveDatautilisée dans la liaison de données sera automatiquement observée pour toute modification, et l'UI sera mise à jour en conséquence. - Ouvrez 
app/java/overview/OverviewViewModel. Comme la réponse est unLiveDataet que nous avons défini le cycle de vie de la variable de liaison, toute modification apportée à celle-ci mettra à jour l'UI de l'application. - Examinez le bloc 
init. Lorsque leViewModelest créé, il appelle la méthodegetMarsRealEstateProperties(). - Examinez la méthode 
getMarsRealEstateProperties(). Dans cette application de démarrage, cette méthode contient une réponse d'espace réservé. L'objectif de cet atelier de programmation est de mettre à jour laLiveDatade réponse dans leViewModelà l'aide de données réelles issues d'Internet. - Ouvrez 
app/res/layout/fragment_overview.xml. Il s'agit de la mise en page du fragment d'aperçu avec lequel vous travaillez dans cet atelier de programmation. Elle inclut la liaison de données pour le ViewModel. Il importe leOverviewViewModel, puis associe la réponse duViewModelà unTextView. Dans les prochains ateliers de programmation, vous remplacerez la vue de texte par une grille d'images dans unRecyclerView. - Compilez et exécutez l'application. La version actuelle de cette application n'affiche que la réponse de démarrage : "Définissez la réponse de l'API Mars ici !"
 
 
Les données immobilières de Mars sont stockées sur un serveur Web, sous la forme d'un service Web REST. Les services Web qui utilisent l'architecture REST sont conçus à l'aide de protocoles et de composants Web standards.
Vous envoyez une requête à un service Web de manière standardisée, via des URI. L'URL Web standard correspond à un type d'URI. Les deux termes sont utilisés de manière interchangeable tout au long de ce cours. Par exemple, dans l'application de cette leçon, vous récupérez toutes les données du serveur suivant :
https://android-kotlin-fun-mars-server.appspot.com
Si vous saisissez l'URL suivante dans votre navigateur, vous obtenez la liste de tous les biens immobiliers disponibles sur Mars.
https://android-kotlin-fun-mars-server.appspot.com/realestate
La réponse d'un service Web est généralement mise en forme au format JSON, un format d'échange permettant de représenter des données structurées. Vous en apprendrez davantage sur le format JSON dans la tâche suivante. Pour faire simple, un objet JSON est une collection de paires clé-valeur, parfois appelée dictionnaire, table de hachage ou tableau associatif. Une collection d'objets JSON est un tableau JSON. C'est le tableau que vous obtenez en réponse d'un service Web.
Pour intégrer ces données dans l'application, celle-ci doit établir une connexion réseau et communiquer avec ce serveur, puis recevoir et analyser les données de réponse dans un format qu'elle peut utiliser. Dans cet atelier de programmation, vous utiliserez une bibliothèque de client REST appelée Retrofit pour établir cette connexion.
Étape 1 : Ajoutez les dépendances Retrofit à Gradle
- Ouvrez build.gradle (Module: app).
 - Dans la section 
dependencies, ajoutez les lignes suivantes pour les bibliothèques Retrofit : 
implementation "com.squareup.retrofit2:retrofit:$version_retrofit"
implementation "com.squareup.retrofit2:converter-scalars:$version_retrofit"
Notez que les numéros de version sont définis séparément dans le fichier Gradle du projet. La première dépendance est pour la bibliothèque Retrofit 2 elle-même, et la seconde pour le convertisseur scalaire Retrofit.  Ce convertisseur permet à Retrofit de renvoyer le résultat JSON sous forme de String. Les deux bibliothèques fonctionnent ensemble.
- Cliquez sur Synchroniser pour créer à nouveau le projet avec les nouvelles dépendances.
 
Étape 2 : Implémenter MarsApiService
Retrofit crée une API réseau pour l'application en fonction du contenu du service Web. Les données du service Web sont récupérées et acheminées via une bibliothèque de conversions distincte qui sait comment décoder les données et les renvoyer sous la forme d'objets utiles. Retrofit est compatible avec les formats de données Web courants, tels que XML et JSON. Au final, Retrofit crée la majeure partie de la couche réseau à votre place, y compris les détails essentiels tels que l'exécution des requêtes sur des threads en arrière-plan.
La classe MarsApiService contient la couche réseau de l'application. Il s'agit de l'API que votre ViewModel utilisera pour communiquer avec le service Web.  Il s'agit de la classe dans laquelle vous implémenterez l'API de service Retrofit.  
- Ouvrez 
app/java/network/MarsApiService.kt. Pour le moment, le fichier ne contient qu'une seule chose : une constante pour l'URL de base du service Web. 
private const val BASE_URL = 
   "https://android-kotlin-fun-mars-server.appspot.com"- Juste en dessous de cette constante, utilisez un compilateur Retrofit pour créer un objet Retrofit.  Importez 
retrofit2.Retrofitetretrofit2.converter.scalars.ScalarsConverterFactorylorsque vous y êtes invité. 
private val retrofit = Retrofit.Builder()
   .addConverterFactory(ScalarsConverterFactory.create())
   .baseUrl(BASE_URL)
   .build()
Pour créer une API de services Web, Retrofit a besoin d'au moins deux éléments : l'URI de base du service Web et une fabrique de conversions.  Le convertisseur indique à Retrofit comment traiter les données qu'il récupère auprès du service Web.  Dans ce cas, vous souhaitez que Retrofit récupère une réponse JSON à partir du service Web et la renvoie sous forme de String.  La mise à niveau comporte un ScalarsConverter (convertisseur scalaire) compatible avec les chaînes et autres types primitifs. Vous appelez donc addConverterFactory() sur le compilateur avec une instance de ScalarsConverterFactory.  Enfin, vous appelez build() pour créer l'objet Retrofit.
- Juste en dessous de l'appel au compilateur Retrofit, définissez une interface qui définit la façon dont Retrofit communique avec le serveur Web à l'aide de requêtes HTTP.  Importez 
retrofit2.http.GETetretrofit2.Calllorsque vous y êtes invité. 
interface MarsApiService {
    @GET("realestate")
    fun getProperties():
            Call<String>
}Pour l'instant, l'objectif est d'obtenir la chaîne de réponse JSON du service Web. Vous n'avez besoin que d'une seule méthode pour cela : getProperties().  Pour indiquer à Retrofit ce que cette méthode doit faire, utilisez une annotation @GET et spécifiez le chemin d'accès ou le point de terminaison de cette méthode de service Web.  Dans ce cas, le point de terminaison est appelé realestate. Lorsque la méthode getProperties() est appelée, Retrofit ajoute le point de terminaison realestate à l'URL de base (que vous avez définie dans le compilateur Retrofit) et crée un objet Call. Cet objet Call permet de lancer la requête.
- Sous l'interface 
MarsApiService, définissez un objet public appeléMarsApipour initialiser le service Retrofit. 
object MarsApi {
    val retrofitService : MarsApiService by lazy { 
       retrofit.create(MarsApiService::class.java) }
}La méthode Retrofit create() crée le service Retrofit lui-même avec l'interface MarsApiService.  Étant donné que cet appel est coûteux et que l'application n'a besoin que d'une seule instance de service Retrofit, vous exposez le service au reste de l'application à l'aide d'un objet public appelé MarsApi et initialisez le service Retrofit de manière différée. Maintenant que tout est configuré, chaque fois que votre application appelle MarsApi.retrofitService,  elle obtient un objet singleton Retrofit qui implémente MarsApiService.
Étape 3 : Appelez le service Web dans OverviewViewModel
- Ouvrez 
app/java/overview/OverviewViewModel.kt. Faites défiler la page jusqu'à la méthodegetMarsRealEstateProperties(). 
private fun getMarsRealEstateProperties() {
   _response.value = "Set the Mars API Response here!"
}Il s'agit de la méthode dans laquelle vous allez appeler le service Retrofit et gérer la chaîne JSON renvoyée. Pour le moment, seule une chaîne de caractères de substitution est disponible pour la réponse.
- Supprimez la ligne de code fictif qui définit la réponse sur "Set the Mars API Response here!" (Définissez ici la réponse de l'API Mars).
 - Dans 
getMarsRealEstateProperties(), ajoutez le code ci-dessous. Importezretrofit2.Callbacketcom.example.android.marsrealestate.network.MarsApilorsque vous y êtes invité.
La méthodeMarsApi.retrofitService.getProperties()renvoie un objetCall. Vous pouvez ensuite appelerenqueue()sur cet objet pour démarrer la requête réseau sur un thread d'arrière-plan. 
MarsApi.retrofitService.getProperties().enqueue( 
   object: Callback<String> {
})- Cliquez sur le mot 
object, qui est souligné en rouge. Sélectionnez Code > Implémenter des méthodes. SélectionnezonResponse()etonFailure()dans la liste.
Android Studio ajoute le code avec des TODO dans chaque méthode : 
override fun onFailure(call: Call<String>, t: Throwable) {
       TODO("not implemented") 
}
override fun onResponse(call: Call<String>, 
   response: Response<String>) {
       TODO("not implemented") 
}- Dans 
onFailure(), supprimez l'élément TODO et définissez_responsesur un message d'échec, comme indiqué ci-dessous._responseest une chaîneLiveDataqui détermine ce qui est affiché dans la vue de texte. Chaque état doit mettre à jour_responseLiveData.
Le rappelonFailure()est appelé lorsque la réponse du service Web échoue. Pour cette réponse, définissez l'état_responsesur"Failure: "concaténé avec le message de l'argumentThrowable. 
override fun onFailure(call: Call<String>, t: Throwable) {
   _response.value = "Failure: " + t.message
}- Dans 
onResponse(), supprimez l'élément TODO et définissez_responsesur le corps de la réponse. Le rappelonResponse()est appelé lorsque la requête aboutit et que le service Web renvoie une réponse. 
override fun onResponse(call: Call<String>, 
   response: Response<String>) {
      _response.value = response.body()
}Étape 4 : Définissez l'autorisation Internet
- Compilez et exécutez l'application MarsRealEstate.  Notez qu'elle se ferme immédiatement avec une erreur. 
   - Cliquez sur l'onglet Logcat dans Android Studio et notez l'erreur dans le journal, qui commence par une ligne semblable à celle-ci :
 
Process: com.example.android.marsrealestate, PID: 10646 java.lang.SecurityException: Permission denied (missing INTERNET permission?)
Le message d'erreur indique que votre application ne dispose peut-être pas de l'autorisation INTERNET. La connexion à Internet pose un problème de sécurité, c'est pourquoi les applications n'ont pas de connexion Internet par défaut. Vous devez indiquer explicitement à Android que l'application a besoin d'accéder à Internet.
- Ouvrez 
app/manifests/AndroidManifest.xml. Ajoutez cette ligne juste avant la balise<application>: 
<uses-permission android:name="android.permission.INTERNET" />- Compilez et exécutez à nouveau l'application.  Si votre connexion Internet fonctionne correctement, vous verrez le texte JSON contenant les données de la propriété Mars.

 - Appuyez sur le bouton Retour de votre appareil ou de votre émulateur pour fermer l'application.
 - Mettez votre appareil ou votre émulateur en mode Avion, puis rouvrez l'application depuis le menu "Applications récentes" ou redémarrez-la depuis Android Studio.
 

- Désactivez à nouveau le mode Avion.
 
Vous recevez maintenant une réponse JSON du service Web Mars, ce qui est un bon début. Mais, en réalité, vous avez besoin d'objets Kotlin, et non d'une grande chaîne JSON. Il existe une bibliothèque appelée Moshi, qui est un analyseur JSON Android qui convertit une chaîne JSON en objets Kotlin. Le convertisseur Retrofit est compatible avec Moshi, ce qui en fait une bibliothèque idéale pour notre exercice.
Dans cette tâche, vous allez utiliser la bibliothèque Moshi avec Retrofit pour analyser la réponse JSON du service Web dans des objets Kotlin utiles de propriété Mars. Vous allez modifier l'application de sorte qu'au lieu d'afficher le fichier JSON brut, l'application affiche le nombre de propriétés de Mars renvoyées.
Étape 1 : Ajouter des dépendances de la bibliothèque Moshi
- Ouvrez build.gradle (Module: app).
 - Dans la section sur les dépendances, ajoutez le code ci-dessous pour inclure les dépendances Moshi. Comme pour Retrofit, 
$version_moshiest défini séparément dans le fichier Gradle au niveau du projet. Ces dépendances ajoutent la compatibilité avec la bibliothèque JSON Moshi de base et avec la compatibilité Kotlin de Moshi. 
implementation "com.squareup.moshi:moshi:$version_moshi"
implementation "com.squareup.moshi:moshi-kotlin:$version_moshi"- Repérez la ligne du convertisseur scalaire Retrofit dans le bloc 
dependencies: 
implementation "com.squareup.retrofit2:converter-scalars:$version_retrofit"- Remplacez cette ligne par 
converter-moshi: 
implementation "com.squareup.retrofit2:converter-moshi:$version_retrofit"- Cliquez sur Synchroniser pour créer à nouveau le projet avec les nouvelles dépendances.
 
Étape 2 : Implémenter la classe de données MarsProperty
Un échantillon d'entrée de réponse JSON provenant du service Web ressemble à ceci :
[{"price":450000,
"id":"424906",
"type":"rent",
"img_src":"http://mars.jpl.nasa.gov/msl-raw-images/msss/01000/mcam/1000ML0044631300305227E03_DXXX.jpg"},
...]La réponse JSON ci-dessus est un tableau, indiqué entre crochets.  Le tableau contient des objets JSON, qui sont entourés d'accolades.  Chaque objet contient un ensemble de paires nom/valeur, séparées par des deux-points. Les noms sont entourés de guillemets. Les valeurs peuvent être des nombres ou des chaînes. Les chaînes sont également entourées de guillemets. Par exemple, la valeur price de cette propriété est de 450 000 $,et img_src est une URL, qui correspond à l'emplacement du fichier image sur le serveur. 
Dans l'exemple ci-dessus, notez que chaque entrée de propriété sur Mars comporte les paires clé/valeur suivantes :
price: prix de la propriété Mars, sous forme de nombre.id: ID de la propriété, sous forme de chaîne.type:"rent"ou"buy".img_src: URL de l'image sous forme de chaîne.
Moshi analyse ces données JSON et les convertit en objets Kotlin. Pour ce faire, il doit disposer d'une classe de données Kotlin pour stocker les résultats analysés. L'étape suivante consiste donc à créer cette classe.
- Ouvrez 
app/java/network/MarsProperty.kt. - Remplacez la définition de classe 
MarsPropertyexistante par le code suivant : 
data class MarsProperty(
   val id: String, val img_src: String,
   val type: String,
   val price: Double
)Notez que chacune des variables de la classe MarsProperty correspond à un nom de clé dans l'objet JSON.  Pour faire correspondre les types dans le JSON, utilisez des objets String pour toutes les valeurs, à l'exception de price, qui est un Double.  Un Double peut être utilisé pour représenter n'importe quel nombre JSON.
Lorsque Moshi analyse le fichier JSON, il met en correspondance les clés en fonction des noms et remplit les objets de données avec les bonnes valeurs.
- Remplacez la ligne de la clé 
img_srcpar celle indiquée ci-dessous. Lorsque vous y êtes invité, importezcom.squareup.moshi.Json. 
@Json(name = "img_src") val imgSrcUrl: String,Dans les réponses JSON, les noms de clés peuvent parfois rendre les propriétés Kotlin déroutantes, ou ne pas correspondre à votre style de codage. Par exemple, dans le fichier JSON, la clé img_src utilise un underscore (ou tiret du bas), tandis que les propriétés Kotlin utilisent généralement des lettres majuscules et minuscules ("camel case"). 
Pour utiliser dans votre classe de données des noms de variables différents des noms de clés de la réponse JSON, utilisez l'annotation @Json. Dans cet exemple, la variable de la classe de données s'appelle imgSrcUrl. La variable est mappée avec l'attribut JSON img_src à l'aide de @Json(name = "img_src").
Étape 3 : Mettez à jour MarsApiService et OverviewViewModel
Maintenant que la classe de données MarsProperty est en place, vous pouvez mettre à jour l'API réseau et ViewModel pour inclure les données Moshi.  
- Ouvrez 
network/MarsApiService.kt. Vous pouvez rencontrer des erreurs de classe manquante pourScalarsConverterFactory. Cela est dû à la modification de la dépendance Retrofit que vous avez effectuée à l'étape 1. Vous allez bientôt corriger ces erreurs. - En haut du fichier, juste avant le compilateur Retrofit, ajoutez le code suivant pour créer l'instance Moshi.  Importez 
com.squareup.moshi.Moshietcom.squareup.moshi.kotlin.reflect.KotlinJsonAdapterFactorylorsque vous y êtes invité. 
private val moshi = Moshi.Builder()
   .add(KotlinJsonAdapterFactory())
   .build()Comme vous l'avez fait avec Retrofit, vous allez créer un objet moshi à l'aide du compilateur Moshi.  Pour que les annotations de Moshi fonctionnent correctement avec Kotlin, ajoutez KotlinJsonAdapterFactory, puis appelez build().  
- Modifiez le compilateur Retrofit pour utiliser 
MoshiConverterFactoryau lieu deScalarConverterFactory, et transmettez l'instancemoshique vous venez de créer. Lorsque vous y êtes invité, importezretrofit2.converter.moshi.MoshiConverterFactory. 
private val retrofit = Retrofit.Builder()
   .addConverterFactory(MoshiConverterFactory.create(moshi))
   .baseUrl(BASE_URL)
   .build()- Supprimez également l'importation pour 
ScalarConverterFactory. 
Code à supprimer :
import retrofit2.converter.scalars.ScalarsConverterFactory- Mettez à jour l'interface 
MarsApiServicepour que Retrofit renvoie une liste d'objetsMarsPropertyau lieu de renvoyer uneCall<String>. 
interface MarsApiService {
   @GET("realestate")
   fun getProperties():
      Call<List<MarsProperty>>
}- Ouvrez 
OverviewViewModel.kt. Faites défiler la page jusqu'à l'appel àgetProperties().enqueue()dans la méthodegetMarsRealEstateProperties(). - Remplacez l'argument 
Callback<String>parCallback<List<MarsProperty>>dansenqueue(). Lorsque vous y êtes invité, importezcom.example.android.marsrealestate.network.MarsProperty. 
MarsApi.retrofitService.getProperties().enqueue( 
   object: Callback<List<MarsProperty>> {- Dans 
onFailure(), remplacez l'argumentCall<String>parCall<List<MarsProperty>>: 
override fun onFailure(call: Call<List<MarsProperty>>, t: Throwable) {- Apportez la même modification aux deux arguments de 
onResponse(): 
override fun onResponse(call: Call<List<MarsProperty>>, 
   response: Response<List<MarsProperty>>) {- Dans le corps de 
onResponse(), remplacez l'attribution existante à_response.valuepar celle indiquée ci-dessous. Commeresponse.body()est désormais une liste d'objetsMarsProperty, la taille de cette liste correspond au nombre de propriétés analysées. Ce message de réponse affiche le nombre de propriétés : 
_response.value = 
   "Success: ${response.body()?.size} Mars properties retrieved"- Assurez-vous que le mode Avion est désactivé.  Compilez et exécutez l'application. Cette fois, le message devrait indiquer le nombre de propriétés renvoyées par le service Web :

 
Le service d'API Retrofit est maintenant en cours d'exécution, mais il utilise un rappel avec deux méthodes de rappel que vous avez dû implémenter. Une méthode gère la réussite et l'autre gère l'échec. Le résultat de l'échec signale les exceptions. Votre code serait plus efficace et plus facile à lire si vous pouviez utiliser des coroutines avec gestion des exceptions, au lieu d'utiliser des rappels. Retrofit dispose d'une bibliothèque qui intègre les coroutines.
Dans cette tâche, vous allez convertir votre service réseau et le ViewModel pour utiliser des coroutines.  
Étape 1 : Ajoutez des dépendances de coroutine
- Ouvrez build.gradle (Module: app).
 - Dans la section des dépendances, ajoutez la prise en charge des bibliothèques de coroutines Kotlin de base et de la bibliothèque de coroutines Retrofit :
 
implementation "org.jetbrains.kotlinx:kotlinx-coroutines-core:$version_kotlin_coroutines" implementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:$version_kotlin_coroutines" implementation "com.jakewharton.retrofit:retrofit2-kotlin-coroutines-adapter:$version_retrofit_coroutines_adapter"
- Cliquez sur Synchroniser pour créer à nouveau le projet avec les nouvelles dépendances.
 
Étape 2 : Mettez à jour MarsApiService et OverviewViewModel
- Dans 
MarsApiService.kt, mettez à jour le compilateur Retrofit pour utiliserCoroutineCallAdapterFactory. Le générateur complet ressemble maintenant à ceci : 
private val retrofit = Retrofit.Builder()
        .addConverterFactory(MoshiConverterFactory.create(moshi))
        .addCallAdapterFactory(CoroutineCallAdapterFactory())
        .baseUrl(BASE_URL)
        .build()Les adaptateurs d'appel permettent à Retrofit de créer des API qui renvoient autre chose que la classe Call par défaut.  Dans ce cas, CoroutineCallAdapterFactory nous permet de remplacer l'objet Call renvoyé par getProperties() par un objet Deferred.
- Dans la méthode 
getProperties(), remplacezCall<List<MarsProperty>>parDeferred<List<MarsProperty>>. Lorsque vous y êtes invité, importezkotlinx.coroutines.Deferred. La méthodegetProperties()complète se présente comme suit : 
@GET("realestate")
fun getProperties():
   Deferred<List<MarsProperty>>L'interface Deferred définit un job de coroutine qui renvoie une valeur de résultat (Deferred hérite de Job). L'interface Deferred inclut une méthode appelée await(), qui permet à votre code d'attendre sans bloquer jusqu'à ce que la valeur soit prête, puis cette valeur est renvoyée. 
- Ouvrez 
OverviewViewModel.kt. Juste avant le blocinit, ajoutez un job de coroutine : 
private var viewModelJob = Job()- Créez un champ d'application de coroutine pour ce nouveau job à l'aide du répartiteur principal :
 
private val coroutineScope = CoroutineScope(
   viewModelJob + Dispatchers.Main )Le répartiteur Dispatchers.Main utilise le thread UI pour son travail.  Étant donné que Retrofit effectue tout son travail sur un thread d'arrière-plan, il n'y a aucune raison d'utiliser un autre thread pour le champ d'application. Cela vous permet de mettre à jour facilement la valeur de MutableLiveData lorsque vous obtenez un résultat.
- Supprimez tout le code dans 
getMarsRealEstateProperties(). Vous utiliserez ici des coroutines au lieu de l'appel àenqueue()et des rappelsonFailure()etonResponse(). - Dans 
getMarsRealEstateProperties(), lancez la coroutine : 
coroutineScope.launch { 
}
Pour utiliser l'objet Deferred que Retrofit renvoie pour la tâche réseau, vous devez vous trouver dans une coroutine. Vous allez donc lancer la coroutine que vous venez de créer.  Vous exécutez toujours du code sur le thread principal, mais vous laissez désormais les coroutines gérer la concurrence.
- Dans le bloc de lancement, appelez 
getProperties()sur l'objetretrofitService: 
var getPropertiesDeferred = MarsApi.retrofitService.getProperties()L'appel de getProperties() à partir du service MarsApi crée et démarre l'appel réseau sur un thread en arrière-plan, en renvoyant l'objet Deferred pour cette tâche. 
- Toujours dans le bloc de lancement, ajoutez un bloc 
try/catchpour gérer les exceptions : 
try {
} catch (e: Exception) {
  
}- Dans le bloc 
try {}, appelezawait()sur l'objetDeferred: 
var listResult = getPropertiesDeferred.await()L'appel de await() sur l'objet Deferred renvoie le résultat de l'appel réseau lorsque la valeur est prête. La méthode await() n'est pas bloquante. Le service de l'API Mars récupère donc les données du réseau sans bloquer le thread actuel, ce qui est important, car nous sommes dans le champ d'application du thread UI. Une fois la tâche terminée, votre code continue de s'exécuter là où il s'était arrêté, à l'intérieur de try {} pour que vous puissiez intercepter les exceptions.  
- Toujours dans le bloc 
try {}, après la méthodeawait(), mettez à jour le message de réponse pour la réponse réussie : 
_response.value = 
   "Success: ${listResult.size} Mars properties retrieved"- Dans le bloc 
catch {}, gérez la réponse de l'échec : 
_response.value = "Failure: ${e.message}"
Une fois terminée, la méthode getMarsRealEstateProperties() se présente comme suit :
private fun getMarsRealEstateProperties() {
   coroutineScope.launch {
       var getPropertiesDeferred = 
          MarsApi.retrofitService.getProperties()
       try {          
           _response.value = 
              "Success: ${listResult.size} Mars properties retrieved"
       } catch (e: Exception) {
           _response.value = "Failure: ${e.message}"
       }
   }
}- En bas de la classe, ajoutez un rappel 
onCleared()avec ce code : 
override fun onCleared() {
   super.onCleared()
   viewModelJob.cancel()
}Le chargement des données doit s'arrêter lorsque le ViewModel est détruit, car le OverviewFragment qui utilise ce ViewModel disparaîtra. Pour arrêter le chargement lorsque le ViewModel est détruit, vous remplacez onCleared() pour annuler le job.
- Compilez et exécutez l'application. Vous obtenez le même résultat que lors de la tâche précédente (un rapport sur le nombre de propriétés), mais avec un code et une gestion des erreurs plus simples.
 
Projet Android Studio : MarsRealEstateNetwork
Services Web REST
- Un service Web est un service sur Internet qui permet à votre application d'effectuer des requêtes et d'obtenir des données.
 - Les services Web courants utilisent une architecture REST. Les services Web qui proposent une architecture REST sont appelés services RESTful. Les services Web RESTful sont conçus à l'aide de protocoles et de composants Web standards.
 - Vous envoyez une requête à un service Web REST de manière standardisée, via des URI.
 - Pour utiliser un service Web, une application doit établir une connexion réseau et communiquer avec le service. L'application doit ensuite recevoir et analyser les données de réponse dans un format qu'elle peut utiliser.
 - La bibliothèque Retrofit est une bibliothèque cliente qui permet à votre application d'envoyer des requêtes à un service Web REST.
 - Utilisez des convertisseurs pour indiquer à Retrofit comment traiter les données qu'il envoie au service Web et qu'il récupère.  Par exemple, le convertisseur 
ScalarsConvertertraite les données du service Web comme uneStringou tout autre type primitif. - Pour autoriser votre application à établir une connexion à Internet, ajoutez l'autorisation 
"android.permission.INTERNET"dans le fichier manifeste Android. 
Analyse JSON
- La réponse d'un service Web est souvent au format JSON, un format d'échange courant pour représenter des données structurées.
 - Un objet JSON est une collection de paires clé-valeur. Cette collection est parfois appelée dictionnaire, table de hachage ou tableau associatif.
 - Une collection d'objets JSON est un tableau JSON. Vous obtenez un tableau JSON en tant que réponse d'un service Web.
 - Les clés d'une paire clé-valeur sont comprises entre guillemets. Les valeurs peuvent être des nombres ou des chaînes. Les chaînes sont également entourées de guillemets.
 - La bibliothèque Moshi est un analyseur JSON Android qui convertit une chaîne JSON en objets Kotlin. Le convertisseur Retrofit est compatible avec Moshi.
 - Moshi met en correspondance les clés d'une réponse JSON avec les propriétés d'un objet de données portant le même nom.
 - Pour utiliser un nom de propriété différent pour une clé, annotez cette propriété avec 
@Jsonet le nom de la clé JSON. 
Retrofit et coroutines
- Les adaptateurs d'appel permettent à Retrofit de créer des API qui renvoient autre chose que la classe 
Callpar défaut. Utilisez la classeCoroutineCallAdapterFactorypour remplacer leCallpar une coroutineDeferred. - Utilisez la méthode 
await()sur l'objetDeferredpour que votre code de coroutine attende sans bloquer jusqu'à ce que la valeur soit prête, puis la valeur est renvoyée. 
Cours Udacity :
Documentation pour les développeurs Android :
Documentation Kotlin :
- Atelier de programmation sur les coroutines
 - Coroutines, documentation officielle
 - Répartiteurs et contextes de coroutines
 
Autre :
Cette section répertorie les devoirs possibles pour les élèves qui suivent cet atelier de programmation dans le cadre d'un cours animé par un enseignant. Il revient à l'enseignant d'effectuer les opérations suivantes :
- Attribuer des devoirs si nécessaire
 - Indiquer aux élèves comment rendre leurs devoirs
 - Noter les devoirs
 
Les enseignants peuvent utiliser ces suggestions autant qu'ils le souhaitent, et ne doivent pas hésiter à attribuer d'autres devoirs aux élèves s'ils le jugent nécessaire.
Si vous suivez cet atelier de programmation par vous-même, n'hésitez pas à utiliser ces devoirs pour tester vos connaissances.
Répondre aux questions suivantes
Question 1
Quels sont les deux éléments dont Retrofit a besoin pour créer une API de services Web ?
▢ L'URI de base du service Web et une requête GET.
▢ L'URI de base du service Web et une fabrique de conversion.
▢ Une connexion réseau au service Web et un jeton d'autorisation.
▢ Une fabrique de conversion et un analyseur pour la réponse.
Question 2
À quoi sert la bibliothèque Moshi ?
▢ Récupérer des données auprès d'un service Web.
▢ Interagir avec Retrofit afin d'envoyer une requête de service Web.
▢ Analyser une réponse JSON à partir d'un service Web dans des objets de données Kotlin.
▢ Renommer les objets Kotlin de sorte qu'ils correspondent aux clés de la réponse JSON.
Question 3
À quoi servent les adaptateurs d'appel Retrofit ?
▢ Ils permettent à Retrofit d'utiliser des coroutines.
▢ Ils adaptent la réponse du service Web aux objets de données Kotlin.
▢ Ils convertissent un appel Retrofit en appel de service Web.
▢ Ils permettent de renvoyer un élément autre que la classe Call par défaut dans Retrofit.
Passez à la leçon suivante : 
Pour obtenir des liens vers d'autres ateliers de programmation de ce cours, consultez la page de destination des ateliers de programmation Principes de base d'Android en Kotlin.