Introduction à la librairie Retrofit – webservices REST

retrofit_long

Une application android a souvent besoin d’une source de donnée externe, par exemple un serveur avec lequel elle va échanger des données. Prenons l’exemple de l’application youtube, qui va interagir avec les serveurs de google afin de récupérer une liste de vidéos.

Pour ceux qui l’ignorent, l’application n’accède pas directement aux données via des requêtes SQL où que sais-je…

Pour vous expliquer simplement, le serveur met à disposition une liste d’url par lesquels les applications vont pouvoir accéder aux donnée. Le format des données échangées se fait le plus souvent en JSON ou en XML.

On dit alors que le serveur fournit un webservice, dans notre cas une API REST.

Retrofit

Retrofit est une librairie permettant de réaliser des appels webservices REST de la façon la plus simple.

Elle a été développée par le groupe Square, dont je vous ai déjà parlé sur le tutoriel concernant Picasso, la librairie de chargement asynchrone d’images. Vous pouvez d’ailleurs retrouver le github de Retrofit à l’adresse suivante : https://github.com/square/retrofit

Et comme toute bonne librairie, le grand Jake Wharton a participé à son développement 😀

Avant Retrofit

L’utilisation de webservices ne date pas de l’arrivée de la librairie Retrofit, si l’on décompose bien les actions à effectuer, nous devions :

  • Construire et appeler URL
  • Récupérer le contenu de la page
  • Analyser / Lire les données

Il existe des composants présents depuis la première version d’Android permettants de réaliser ces actions, nous pouvions :

  • Construire l’url en avec des Strings
  • Utiliser le HttpClient de apache afin d’appeler cette URL, puis récupérer le contenu de la page en format String avec EntityUtils
  • Créer nos parsers

Ce qui une fois terminé nous avait déjà couté 3 mois de développement et avait complètement démotivé toute l’équipe…

Avec Retrofit

Comme toute librairie, il est nécéssaire de l’importer dans notre projet en utilisant gradle (je vous invite à suivre le tutoriel maitriser-gradle-partie-1 si vous n’êtes pas habitué à l’outil). Pour cela il vous suffit d’ajouter la ligne suivante à vos dépendances :

compile 'com.squareup.retrofit:retrofit:1.9.0'

Pour la suite du tutoriel, je vais interagir avec l’API de github qui a comme avantage de proposer une version publique, ne nécessitant pas d’authentification.
Retrofit se base sur un fichier de description de notre API REST :

import java.util.List;
import retrofit.http.GET;
import retrofit.http.Path;
import retrofit.http.Query;

public interface GithubService {

    public static final String ENDPOINT = "https://api.github.com";

    @GET("/users/{user}/repos")
    List<Repo> listRepos(@Path("user") String user);

    @GET("/search/repositories")
    List<Repo> searchRepos(@Query("q") String query);
}

Cette interface décrit un webservice contenant 2 méthodes :

  • listRepos affiche la liste des dépots d’un utilisateur
  • searchRepos recherche des dépôts en fonction de mots clés

Regardons maintenant les informations associées à ces fonctions :

En premier lieu, nous devons définir l’url du webservice à appeler :

public static final String ENDPOINT = "https://api.github.com";

Méthode d’appel

Pour chaque fonction, ajouter une annotation définissant la méthode d’appel :

@GET("/users/{user}/repos")
<List> listRepos(@Path("user") String user);

Les méthodes suivantes sont disponibles :

  • @GET
  • @POST
  • @PUT
  • @DELETE
  • @PATCH

Url de l’appel

Puis l’url de la méthode à appeler :

@GET("/users/{user}/repos")
List<Repo> listRepos(@Path("user") String user);

Cette url viendra s’ajouter à notre ENDPOINT, donc éviter de mettre l’url complète

Il nous faut ensuite la possibilité de paramétrer nos appels, deux méthodes nous sont offertes :

Injecter des arguments à notre url

@GET("/users/{user}/repos")
List<Repo> listRepos(@Path("user") String user);

Ce qui va utiliser l’url suivante pour l’appel avec l’utilisateur florent37 : https://api.github.com/users/florent37/repos

Ajouter des arguments en paramètre de notre url

@GET("/search/repositories")
List<Repo> searchRepos(@Query("q") String query);

Ce qui va construire l’url suivante pour l’appel avec le mot picasso : https://api.github.com/search/repositories?q=picasso

Envoyer des paramètres de formulaire (en POST)

@FormUrlEncoded @POST("/search/repositories")
List<Repo> searchRepos(@Field("q") String query);

Interpréter le retour du webservice

Retrofit facilite la lecture des données issues par le webservice en vous retournant directement le résultat dans des objets Java. Par exemple dans le cas de searchRepos, vous aurez pu remarquer que j’ai écrit List<Repo> avant le nom de la méthode, ici Retrofit va me retourner le résultat automatiquement sous forme d’ArrayList de Repos.

La déserialisation de l’objet est réalisée par GSON, le nom des attributs privés doivent donc être identique aux champs de l’objet JSON afin d’être injectés.

@GET("/search/repositories")
List<Repo> searchRepos(@Query("q") String query);

Il ne reste qu’à définir l’objet Repo, clickez sur l’url suivante : https://api.github.com/users/florent37/repos. La page affichée contient le retour du webservice. Je ne vais ici que retenir que les attributs id, name, full_name et html_url, ce qui donne l’objet :

public class Repo {
    private int id;
    private String name;
    private String full_name;
    private String html_url;

    //getters & setters
}

Réaliser nos appels

Il nous faut maintenant fournir notre interface à un objet nommé RestAdapter qui va nous retourner une implémentation de notre webservice :

GithubService githubService = new RestAdapter.Builder()
                .setEndpoint(GithubService.ENDPOINT)
                .build()
                .create(GithubService.class);

Nous pouvons ensuite réaliser nos appels de webservice en utilisant l’objet créé par le RestAdapter, pour cela Retrofit nous offre plusieurs possibilités : synchrone ou asynchrone.

Pensez à ajouter la permission android.permission.INTERNET dans votre manifest

<!--?xml version="1.0" encoding="utf-8"?-->
<uses-permission android:name="android.permission.INTERNET"/>

Appel synchrone

Les appels synchrone permettent d’effectuer une requête et d’obtenir le retour du webservice directement depuis l’appel de la méthode de notre GithubService.

List<Repo> repoList = githubService.listRepos(user);

Attention, ces appels sont bloquants, il est donc interdit de l’effectuer depuis le thread principal (UI Thread), à vous de le placer dans un thread ou une tache asynchrone.

public class MainActivity extends Activity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        new ListReposTask().execute("florent37");
    }

    public void afficherRepos(List<Repo> repos) {
        Toast.makeText(this,"nombre de dépots : "+repos.size(),Toast.LENGTH_SHORT).show();
    }

    class ListReposTask extends AsyncTask<String,Void,List<Repo>>{

        @Override
        protected List<Repo> doInBackground(String...params) {
            GithubService githubService = new RestAdapter.Builder()
                    .setEndpoint(GithubService.ENDPOINT)
                    .build()
                    .create(GithubService.class);

            String user = params[0];
            List<Repo> repoList<Repo> = githubService.listRepos(user);

            return repoList;
        }

        @Override
        protected void onPostExecute(List<Repo> repos) {
            super.onPostExecute(repos);
            afficherRepos(repos);
        }
    }

}

Appel asynchrone

L’appel asynchrone peux être effectué “depuis” le thread principal de façon assez simple, le retour du webservice s’effectue par un callback, un objet dont les méthodes seront appelées suite à la réception de données du webservice :

public class MainActivity extends ActionBarActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        GithubService githubService = new RestAdapter.Builder()
                .setEndpoint(GithubService.ENDPOINT)
                .build()
                .create(GithubService.class);

        githubService.listReposAsync("florent37",new Callback<List>() {
            @Override
            public void success(List<Repo> repos, Response response) {
                afficherRepos(repos);
            }

            @Override
            public void failure(RetrofitError error) {
            }
        });
    }

    public void afficherRepos(List<Repo> repos) {
        Toast.makeText(this,"nombre de dépots : "+repos.size(),Toast.LENGTH_SHORT).show();
    }
}

Pour effectuer les appels de cette façon, il est nécessaire de modifier notre interface de service afin d’ ajouter les callbacks :

public interface GithubService {

    ...

    @GET("/users/{user}/repos")
    void listReposAsync(@Path("user") String user, Callback<List<Repo>> callback);

    ...
}

Fonctions supplémentaires de Retrofit

Logger les actions

Retrofit permet d’afficher toutes les actions qu’il effectue, ainsi que les données qu’il envoie et qu’il reçoit, pour cela il suffit de lui demander d’afficher les Logs :

GithubService githubService = new RestAdapter.Builder()
                .setEndpoint(GithubService.ENDPOINT)
                .setLog(new AndroidLog("retrofit"))
                .setLogLevel(RestAdapter.LogLevel.FULL)
                .build()
                .create(GithubService.class);

Ajouter des headers

Pour certaines requêtes, vous devez jouer avec les headers, voici comment retrofit les utilisent :

@Headers("Cache-Control: max-age=640000")
@GET("/users/{user}/repos")
List<Repo> listRepos(@Path("user") String user);

Effectuer la même action à chaque appel

Il est souvent nécessaire d’effectuer une même action à chaque appel de notre webservice (dues dans beaucoup de cas à une authentification auprès du webservice). N’ayez crainte, Retorit le gère aussi ! Il suffit de lui fournir un RequestInterceptor, qui sera appelé avant chaque appel :

GithubService githubService = new RestAdapter.Builder()
                    .setEndpoint(GithubService.ENDPOINT)
                    .setRequestInterceptor(new RequestInterceptor() {
                        @Override
                        public void intercept(RequestFacade request) {
                            //ajoute "baerer: 1234567890" en header de chaque requête
                            request.addHeader("bearer","1234567890");
                        }
                    })
                    .build()
                    .create(GithubService.class);

Gérer les codes d’erreur

Le webservice fournir souvent un code HTTP en cas d’erreur de requête, par exemple 405 Method Not Allowed. Voici comment les récupérer avec Retrofit :

GithubService githubService = new RestAdapter.Builder()
                    .setEndpoint(GithubService.ENDPOINT)
                    .setErrorHandler(new ErrorHandler() {
                        @Override
                        public Throwable handleError(RetrofitError cause) {
                            Response r = cause.getResponse();
                            if (r != null && r.getStatus() == 405) {
                                MainActivity.this.notAllowed();
                            }
                            return cause;
                        }
                    })
                    .build()
                    .create(GithubService.class);

Vous savez maintenant effectuer des appels à un webservice facilement avec la librairie Retrofit, à utiliser sans modération 😉

Un commentaire


  1. Salut,
    Très bon tuto!! je débute sous android ya de cela 3 mois et maintenant je me penche vers les appels réseau.
    J’aimerais savoir si retrofit gère le cache de façon automatique comme Picasso?
    Merci d’avance pour ta réponse!
    Cordialement

    Répondre

Laisser un commentaire

Votre adresse de messagerie ne sera pas publiée. Les champs obligatoires sont indiqués avec *