Retrofit

Sommaire

La librairie Retrofit2

Retrofit est une librairie qui simplifie la consommation d’api

On peut la trouver sur github

Nous allons l’ajouter a un projet gradle en ajoutant simplement compile 'com.squareup.retrofit2:retrofit:2.8.1' à nos dépendances ainsi qu’un convertisseur compile 'com.squareup.retrofit2:converter-gson:2.8.1' qui transformera nos objets json en objets Java.

Pour notre exemple, je vais utiliser une api toute faite jsonplaceholder

Ce site permet d’intérroger directement une api avec l’url. Par exemple si vous allez à l’adresse https://jsonplaceholder.typicode.com/todos/1 vous obtiendrez le todo 1 en json

{
  "userId": 1,
  "id": 1,
  "title": "delectus aut autem",
  "completed": false
}

Quelques réglages dans notre application

pour rappel: votre build.gradle doit ressembler à ça

plugins {
    id 'java'
    id 'application'
    id 'org.openjfx.javafxplugin' version '0.0.8'
}

group 'org.afpa'
version '1.0-SNAPSHOT'
compileJava.options.encoding = 'UTF-8'
sourceSets.main.resources.srcDir("src/main/java").includes.addAll(["**/*.fxml", "**/*.css","**/*.png"])
sourceSets.main.resources.srcDirs("src/main/resources").includes.addAll(["**/*.*"])

repositories {
    mavenCentral()
}

dependencies {
    testCompile group: 'junit', name: 'junit', version: '4.12'

    compile 'com.squareup.retrofit2:retrofit:2.8.1'
    compile 'com.squareup.retrofit2:converter-gson:2.8.1'
}

javafx {
    version = "14"
    modules = ['javafx.controls','javafx.base','javafx.graphics','javafx.fxml']
}

mainClassName = "org.afpa.App"

Nous allons d’abord créer un POJO (objet java) pour réceptionner nos données. J’ai d’abord choisi les posts.

L’objet que je récupère en json ressemble à ceci:

{
    "userId": 1,
    "id": 1,
    "title": "sunt aut facere repellat provident occaecati excepturi optio reprehenderit",
    "body": "quia et suscipit\nsuscipit recusandae consequuntur expedita et cum\nreprehenderit molestiae ut ut quas totam\nnostrum rerum est autem sunt rem eveniet architecto"
  }

Donc mon objet java est:


package org.afpa.DAL;

import com.google.gson.annotations.SerializedName;

public class Post {

    private Long id;
    private Long userId;
    private String title;
    @SerializedName("body")
    private String text;

    public Long getId() {
        return id;
    }

    public Long getUserId() {
        return userId;
    }

    public String getTitle() {
        return title;
    }

    public String getText() {
        return text;
    }
}

J’ai placé cet objet dans un package DAL et j’ai renommé le membre body en text grace à l’annotation @SerializedName("body").

Retrofit a besoin que l’on lui crée une interface où nous mettons notre “query”

package org.afpa.DAL;

import retrofit2.Call;
import retrofit2.http.GET;

import java.util.List;

public interface ApiService {
    @GET("posts")
    Call<List<Post>> getPosts();
}

Dans cette interface nous avons déclaré la méthode getPosts() qui va récupérer une liste de Post à partir de la liste json envoyée par l’API.

Mise en place dans notre programme

Vous avez déjà fais du JavaFX, donc je vais aller un peu plus vite.

Je crée un fxml et son contrôleur dans un package GUI:

retroexemple.fxml

<?xml version="1.0" encoding="UTF-8"?>

<?import javafx.geometry.*?>
<?import javafx.scene.control.*?>
<?import javafx.scene.layout.*?>
<?import javafx.scene.text.*?>

<AnchorPane prefHeight="400.0" prefWidth="600.0" xmlns="http://javafx.com/javafx/10.0.2-internal" xmlns:fx="http://javafx.com/fxml/1" fx:controller="org.afpa.GUI.RetroexempleController">
   <children>
      <VBox prefHeight="400.0" prefWidth="600.0" spacing="10.0" AnchorPane.bottomAnchor="0.0" AnchorPane.leftAnchor="0.0" AnchorPane.rightAnchor="0.0" AnchorPane.topAnchor="0.0">
         <children>
            <Label text="Retrofit2 et JavaFX">
               <font>
                  <Font size="36.0" />
               </font>
            </Label>
            <TableView fx:id="receptionTab" prefHeight="357.0" prefWidth="600.0">
              <columns>
                <TableColumn fx:id="idCol" prefWidth="75.0" text="Id" />
                <TableColumn fx:id="userIdCol" prefWidth="75.0" text="UserID" />
                  <TableColumn fx:id="titreCol" prefWidth="75.0" text="Titre" />
                  <TableColumn fx:id="texteCol" prefWidth="75.0" text="Texte" />
              </columns>
               <columnResizePolicy>
                  <TableView fx:constant="CONSTRAINED_RESIZE_POLICY" />
               </columnResizePolicy>
            </TableView>
            <Label fx:id="messageErreur" layoutX="10.0" layoutY="10.0" textFill="#c91212">
               <font>
                  <Font size="18.0" />
               </font>
            </Label>
         </children>
      </VBox>
   </children>
   <padding>
      <Insets bottom="10.0" left="10.0" right="10.0" top="10.0" />
   </padding>
</AnchorPane>


RetroExempleController.java

package org.afpa.GUI;

import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.fxml.Initializable;
import javafx.scene.control.TableColumn;
import javafx.scene.control.TableView;
import javafx.scene.control.cell.PropertyValueFactory;
import org.afpa.DAL.ApiService;
import org.afpa.DAL.Post;
import retrofit2.Call;
import retrofit2.Callback;
import retrofit2.Response;
import retrofit2.Retrofit;
import retrofit2.converter.gson.GsonConverterFactory;

import java.net.URL;
import java.util.List;
import java.util.ResourceBundle;

public class RetroexempleController implements Initializable {
    public TableView<Post> receptionTab;
    public TableColumn idCol;
    public TableColumn userIdCol;
    public TableColumn titreCol;
    public TableColumn texteCol;
    public Label messageErreur;

    ObservableList<Post> observableList = FXCollections.observableArrayList();

    @Override
    public void initialize(URL location, ResourceBundle resources) {

        chargeTableau();
    }


}

Pour le moment nous n’avons pas créé la méthode de chargement. Que nous faut-il?

Il faut d’abord créer une instance de Retrofit

Retrofit retrofit = new Retrofit.Builder()
                .baseUrl("https://jsonplaceholder.typicode.com/")
                .addConverterFactory(GsonConverterFactory.create())
                .build();

Dans celle-ci nous indiquons l’adresse de l’api, le convertisseur qui va convertir les objets json en objet java et on oublie pas le .build().

On définie ensuite le Call généré par l’interface que nous avons écrite.

Call<List<Post>> call = apiService.getPosts();

Enfin il nous faut utiliser le .enqueue() de Retrofit

call.enqueue(new Callback<List<Post>>() {
            @Override
            public void onResponse(Call<List<Post>> call, Response<List<Post>> response) {

            }

            @Override
            public void onFailure(Call<List<Post>> call, Throwable t) {

            }
        });

Le enqueue prend en paramètre un Callback et implémente deux méthodes. La première est onResponse si tout se passe comme prévu. C’est ici que nous remplissons le tableau à partir du json contenu dans response.body() et en créant une ObservableList<Post> observableList. La seconde est onFailure(), si ça se passe mal et on peut par exemple envoyer une alerte à l’utilisateur.

Finalement chargeTableau() ressemble à ça:

private void chargeTableau() {
        Retrofit retrofit = new Retrofit.Builder()
                .baseUrl("https://jsonplaceholder.typicode.com/")
                .addConverterFactory(GsonConverterFactory.create())
                .build();

        ApiService apiService = retrofit.create(ApiService.class);
        Call<List<Post>> call = apiService.getPosts();

        call.enqueue(new Callback<List<Post>>() {
            @Override
            public void onResponse(Call<List<Post>> call, Response<List<Post>> response) {
                observableList = FXCollections.observableArrayList(response.body());
                idCol.setCellValueFactory(new PropertyValueFactory<>("id"));
                userIdCol.setCellValueFactory(new PropertyValueFactory<>("userId"));
                titreCol.setCellValueFactory(new PropertyValueFactory<>("title"));
                texteCol.setCellValueFactory(new PropertyValueFactory<>("text"));
                receptionTab.setItems(observableList);
            }

            @Override
            public void onFailure(Call<List<Post>> call, Throwable t) {
               Platform.runLater(() -> {
                    Alert alert = new Alert(AlertType.WARNING);
                    alert.setContentText("Erreur de connection!!!");
                    alert.showAndWait();
                });
            }
        });
    }

Attention, vous remarquerez que la gestion de l’alerte est encadrée par un Platform.runLater(). C’est parce que Java n’aime pas mélanger les processus et que nous devons lui indiquer que l’alerte est dans javaFX.

ok

et

down