• 20 heures
  • Difficile

Ce cours est visible gratuitement en ligne.

course.header.alt.is_video

course.header.alt.is_certifying

J'ai tout compris !

Mis à jour le 12/12/2019

Améliorez l’implémentation de vos requêtes réseaux

En tant que développeur Android, un de vos objectifs principal est de créer une application fonctionnelle. Cependant, vous devez penser également à créer du code maintenable dans le temps et surtout le plus compréhensible possible !

Une règle d'or : Ne jamais réinventer la roue ! Votre temps est précieux.

Ainsi, je vais vous proposer maintenant de découvrir une libraire qui va vous permettre de créer BEAUCOUP plus facilement des requêtes réseau : Retrofit. D'ailleurs son développeur est Jake Wharton, le créateur de la librairie Butterknife (et de bien d'autres !).

Introduction

Retrofit est un client REST développé par Jake Wharton ainsi que l'ensemble du staff de Square. Cette librairie est basée elle-même sur le client REST OKHttp (développé encore par Square !).

Notre objectif est donc de réaliser la même chose que le précédent chapitre, à savoir récupérer la liste des personnes que suit Jake Wharton sur Github, mais maintenant en utilisant cette nouvelle librairie, Retrofit.

1 - Sérialiser et désérialiser des données JSON

Comme nous l'avons vu précédemment, le résultat que nous retourne l'API correspond à un large objet JSON. Vous pouvez d'ailleurs visualiser le résultat brut depuis votre navigateur en cliquant simplement sur l'adresse de l'API en question : https://api.github.com/users/JakeWharton/following.

Pas facile de manipuler ce JSON en format String n'est-ce pas ... ? :p

                                                                             

C'est là qu'interviennent les concepts suivants :

  • La désérialisation : Des données brutes mais structurées vont être converties en un objet plus facilement manipulable.

  • La sérialisation : Un objet sera converti en données brutes mais structurées plus facilement transmissibles.

Vous connaissez déjà normalement ces concepts, puisque que nous désérialisons nos vues XML (Textview, ImageView, etc...) en objets JAVA.

Afin de réaliser ces actions facilement, nous allons utiliser la librairie GSON de Google. Pour cela, retournez dans votre mini-application NetApp (disponible également sur ce commit Github), et installez la librairie GSON dans votre fichier Gradle.

Extrait du fichier build.gradle :

dependencies {
    ...
    compile 'com.google.code.gson:gson:2.8.2'
}

Une fois installée, nous allons maintenant créer un objet JAVA qui représentera la structure de notre donnée JSON. Pour cela, je vais vous donner une petite astuce : copiez tout le JSON affiché sur la page de votre navigateur quand vous cliquez sur le lien https://api.github.com/users/JakeWharton/following.

Puis, un super site va vous générer automatiquement la classe POJO (Plain Old Java Object) correspondante à ce JSON, qu'utilisera par la suite la librairie GSON pour le désérialiser. Ainsi, accédez au site http://www.jsonschema2pojo.org/ puis collez-y le JSON.

Cochez les cases :

  • Target language : JAVA

  • Source type : JSON

  • Annotation style : Gson

  • Class name : Appelez-la GithubUser

Puis cliquez sur le bouton Preview. La classe JAVA correspondant au JSON est correctement générée et affichée à l'écran. Transférez-là vers une nouvelle classe que vous allez créer (GithubUser.java) dans le package Models de l'application NetApp.

Extrait de la classe Models/GithubUser.java :

package com.openclassrooms.netapp.Models;

import com.google.gson.annotations.Expose;
import com.google.gson.annotations.SerializedName;

public class GithubUser {

    @SerializedName("login")
    @Expose
    private String login;
    
    ...
    
    public String getLogin() {
        return login;
    }

    public void setLogin(String login) {
        this.login = login;
    }
    
    ...
}
    

Explications : Le site nous a généré un model représentant notre JSON spécialement conçu pour être utilisé par la librairie GSON.

  • @SerializedName("login") : Permet de dire à GSON de mapper (cartographier, lier, etc...) la clé JSON "login" avec la variable "login" de notre classe JAVA.

  • @Expose :  Permet de dire à GSON d'exposer cette variable à la désérialisation/sérialisation.

Maintenant que cela est fait, passons à la configuration de Retrofit.

2 - Configurer Retrofit et créer notre premier Endpoint

Vous allez voir, l'utilisation de Retrofit est relativement simple. Avant toute chose, nous allons installer la librairie via Gradle.

Extrait du fichier build.gradle:

dependencies {
    ...
    compile 'com.squareup.retrofit2:retrofit:2.3.0'
    compile 'com.squareup.retrofit2:converter-gson:2.3.0'
}

Déclarons maintenant notre appel réseau. Retrofit nous impose de créer une interface qui listera les différents appels (appelés aussi endpoints) que nous effectuerons sur une API (ici celle de Github).

Pour cela, créons l'interface GithubService dans notre package Utils.

Interface GithubService.java :

public interface GithubService {
    @GET("users/{username}/following")
    Call<List<GithubUser>> getFollowing(@Path("username") String username);
}

Explications : Cette interface contient une seule méthode getFollowing() qui représente l'appel réseau (endpoint) que nous allons réaliser sur l'API Github.

  • @GET("users/{username}/following")  : Permet d'indiquer à Retrofit que nous souhaitons effectuer une requête REST de type GET sur le complément d'URL "users/{username}/following". Nous configurerons l'URL de base bientôt...

  • @Path("username") String username  : En paramètre de notre méthode getFollowing(), nous indiquons une variable de type String représentant le paramètre que Retrofit placera dans l'URL de la méthode GET.

  • Call<List<GithubUser>>  : Le type retourné par cette requête GET correspondant au JSON désérialisé. Dans ce cas-là, cette requête retourne une liste d'objet GithubUser, objet que nous avons créé précédemment.

Nous allons ensuite ajouter à cette interface une variable publique statique nous permettant de créer une instance configurée de Retrofit.

Extrait de l'interface GithubService.java :

public interface GithubService {
    ...
    public static final Retrofit retrofit = new Retrofit.Builder()
            .baseUrl("https://api.github.com/")
            .addConverterFactory(GsonConverterFactory.create())
            .build();
}

Explications : Cette variable retourne un objet Retrofit qui sera utilisé par la suite pour exécuter une requête réseau.

  • .baseUrl("https://api.github.com/")  : Permet de définir à Retrofit une URL racine, utilisée par la suite comme URL de base par nos endpoints.

  • .addConverterFactory(GsonConverterFactory.create())  : Permet de définir le sérialiseur/désérialiseur utilisé par défaut par Retrofit, ici GSON.

3 - Exécuter un appel réseau

Passons maintenant à l'exécution de cette requête réseau en utilisant Retrofit. Petite subtilité ici, vous n'aurez pas besoin d'utiliser une AsyncTask ! Les développeurs de Retrofit ont souhaité vous faciliter la vie un maximum en créant à la place une classe Call, qui se chargera de réaliser les appels en arrière-plan.

Créons une classe dédiée à ces appels, que nous appellerons GithubCalls.java et que nous placerons dans notre package Utils.

Classe Utils/GithubCalls.java :

public class GithubCalls {

    // 1 - Creating a callback
    public interface Callbacks {
        void onResponse(@Nullable List<GithubUser> users);
        void onFailure();
    }

    // 2 - Public method to start fetching users following by Jake Wharton
    public static void fetchUserFollowing(Callbacks callbacks, String username){

        // 2.1 - Create a weak reference to callback (avoid memory leaks)
        final WeakReference<Callbacks> callbacksWeakReference = new WeakReference<Callbacks>(callbacks);

        // 2.2 - Get a Retrofit instance and the related endpoints
        GithubService gitHubService = GithubService.retrofit.create(GithubService.class);

        // 2.3 - Create the call on Github API
        Call<List<GithubUser>> call = gitHubService.getFollowing(username);
        // 2.4 - Start the call
        call.enqueue(new Callback<List<GithubUser>>() {

            @Override
            public void onResponse(Call<List<GithubUser>> call, Response<List<GithubUser>> response) {
                // 2.5 - Call the proper callback used in controller (MainFragment)
                if (callbacksWeakReference.get() != null) callbacksWeakReference.get().onResponse(response.body());
            }

            @Override
            public void onFailure(Call<List<GithubUser>> call, Throwable t) {
                // 2.5 - Call the proper callback used in controller (MainFragment)
                if (callbacksWeakReference.get() != null) callbacksWeakReference.get().onFailure();
            }
        });
    }
}

Explications : Un peu sur le même modèle de ce que nous avons réalisé pour les AsyncTasks, nous avons créé une classe GithubCalls exécutant notre appel vers l'API Github en arrière plan.

  • Ligne 1 : Une interface de callback a été créée afin que le contrôleur (MainFragment) puisse récupérer certains moments de l'exécution de la requête ( onResponse()  et  onFailure() )

  • Ligne 2 : La méthode statique publique qui nous permettra de lancer depuis le contrôleur, notre requête réseau.

    • Ligne 2.1 : Comme d'habitude pour nos callbacks, nous lui créons une référence faible afin de permettre au Garbage Collector de le supprimer au besoin et ainsi éviter les Memory Leaks.

    • Ligne 2.2 : Nous récupérons l'instance de Retrofit déjà pré-configurée, puis nous créons un objet contenant la liste des endpoints de l'interface GithubService.

    • Ligne 2.3 : Nous créons un appel (Call) de notre endpoint getFollowing().

    • Ligne 2.4 : Et enfin, nous exécutons l'appel (donc la requête réseau) nous permettant de récupérer toutes les personnes (une liste de GithubUser) que suit Jake Wharton. 

    • Ligne 2.5 : On pense également à appeler les méthodes de callback au bon moment.

Et voilà ! Il ne reste plus qu'à modifier notre Fragment pour lui dire d'exécuter cette requête au clic de l'utilisateur sur le bouton "GET DATAS FROM GITHUB".

Extrait de MainFragment.java :

public class MainFragment extends Fragment implements NetworkAsyncTask.Listeners, GithubCalls.Callbacks {
    
    // 1 - Implement Callbacks

    ...

    // -----------------
    // ACTIONS
    // -----------------

    @OnClick(R.id.fragment_main_button)
    public void submit(View view) {
        this.executeHttpRequestWithRetrofit();
    }

    // ------------------------------
    //  HTTP REQUEST (Retrofit Way)
    // ------------------------------

    // 4 - Execute HTTP request and update UI 
    private void executeHttpRequestWithRetrofit(){
        this.updateUIWhenStartingHTTPRequest();
        GithubCalls.fetchUserFollowing(this, "JakeWharton");
    }

    // 2 - Override callback methods
    
    @Override
    public void onResponse(@Nullable List<GithubUser> users) {
        // 2.1 - When getting response, we update UI
        if (users != null) this.updateUIWithListOfUsers(users);
    }

    @Override
    public void onFailure() {
        // 2.2 - When getting error, we update UI
        this.updateUIWhenStopingHTTPRequest("An error happened !");
    }

    ...
    
    // ------------------
    //  UPDATE UI
    // ------------------

    ...

    // 3 - Update UI showing only name of users
    private void updateUIWithListOfUsers(List<GithubUser> users){
        StringBuilder stringBuilder = new StringBuilder();
        for (GithubUser user : users){
            stringBuilder.append("-"+user.getLogin()+"\n");
        }
        updateUIWhenStopingHTTPRequest(stringBuilder.toString());
    }

}

Explications : Nous mettons à jour notre fragment MainFragment afin de lui permettre, au clic de l'utilisateur sur le bouton "GET DATAS FROM GITHUB", d'exécuter une requête réseau (propulsée par Retrofit, bien sûr... :D) qui ira récupérer les utilisateurs suivis par Jake Wharton, avant de les afficher dans le TextView.

  • Ligne 1 : Nous implémentons notre interface de callback  GithubCalls.Callbacks.

  • Ligne 2 : On re-déclare les méthodes de callback de notre interface afin de récupérer certains moments de l'exécution de la requête réseau.

    • Ligne 2.1 : Une fois la requête terminée, nous mettons à jour notre TextView afin d'afficher les utilisateurs suivis par Jake Wharton.

    • Ligne 2.2 : Si la requête échoue pour une quelconque raison, nous mettons également à jour notre TextView en y affichant une erreur

  • Ligne 3 : Cette méthode met à jour notre UI en parcourant la liste des utilisateurs suivis par Jake Wharton pour récupérer uniquement leur nom. 

  • Ligne 4 : Cette méthode nous permet d'exécuter la requête réseau, tout simplement. N'oubliez pas de l'appeler dans la méthode onClick du bouton (submit()).

Exécutez votre application NetApp et appuyez sur le bouton "GET DATAS FROM GITHUB".

                                             

Parfait ! Tout fonctionne correctement et le résultat est visuellement plus sympa.

Pour vous amuser un peu, n'hésitez pas à y ajouter des animations plus poussées comme une ProgressBar par exemple... ;)

Exemple de certificat de réussite
Exemple de certificat de réussite