Introduction à Volley et GSON
Hello tout le monde, Olivier au rapport. Je suis développeur Android au sein de l’équipe Webwag Mobile depuis Octobre 2015 et aujourd’hui, je vous propose une petite introduction à deux librairies que nous utilisons beaucoup durant le développement de nos projets : Volley et GSON.
Volley : c’est de la balle !
Késako :
Présenté par Google lors de la conférence Google I/O 2013 qui s’est déroulé du 15 au 17 mai, Volley est une librairie HTTP qui permet facilement de faire des appels réseaux rapides sur Android. Disponible à partir d’Android 2.2 (API Level 8 – Froyo), elle utilise les méthodes GET / POST / PUT / DELETE du protocole HTTP et est particulièrement efficace pour des téléchargements courts et rapides tels que le téléchargement d’images ou de données utilisateurs. Par contre, dans le cas de la mise en place d’outils de streaming ou de téléchargements conséquents, il est préférable de choisir plutôt la solution DownloadManager fournit par Google. En effet, l’un des nombreux avantages de Volley est que tous les résultats des requêtes sont automatiquement sauvegardés en cache (peut-être désactivé) afin de pouvoir être réutilisé, si besoin, plus tard.
De plus, Volley offre la possibilité de :
- Hiérarchiser des requêtes : les prioriser afin de charger leurs contenus en fonction de leur rang,
- Programmer un groupe de requêtes,
- Implémenter ses propres mécanismes de retry / backOff,
- Annuler une ou plusieurs requêtes en cours,
- Support natif du JSON.
Prérequis :
Pour utiliser cette librairie au sein de vos projets, deux solutions s’offrent à vous.
Vous pouvez directement passer par Gradle, qui est intégré à Android Studio et qui permet de gérer les modules et dépendances de vos projets de manière très simple. Pour cela, il vous suffit d’ouvrir le fichier build.gradle du module principale de votre application (et non pas celui de votre projet, attention !) et d’ajouter la ligne suivante dans le nœud dependencies :
compile 'com.android.volley:volley:1.0.0'
Ce qui devrait vous donner :
dependencies { ... compile 'com.android.volley:volley:1.0.0' ... }
Sinon, vous pouvez récupérer le répertoire Volley directement sur Github et l’importer dans votre projet en tant qu’Android library module.
Peu importe l’approche que vous choisirez, il est indispensable d’ajouter la permission Internet dans le manifest de votre application. Voila à quoi devrait ressembler votre AndroidManifest.xml :
<?xml version="1.0" encoding="utf-8"?> <manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.project.article"> <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" /> <!-- Volley --> <uses-permission android:name="android.permission.INTERNET" /> <application android:name=".application.App" android:allowBackup="true" android:icon="@mipmap/ic_launcher" android:label="@string/app_name" android:supportsRtl="true" android:theme="@style/AppTheme"> <activity android:name=".activities.HomeActivity" android:configChanges="keyboardHidden|orientation|screenSize"> <intent-filter> <action android:name="android.intent.action.MAIN" /> <category android:name="android.intent.category.LAUNCHER" /> </intent-filter> </activity> </application> </manifest>
Et sinon, à quoi ça ressemble ?
public static void getString( int method, String url, final Map<String, String> headers, Response.Listener<String> listener, Response.ErrorListener errorListener, boolean putInCache) { MyLog.d("getJSONObject url: " + url); StringRequest jsonObjReq = new StringRequest(method, url, listener, errorListener) { @Override public Map<String, String> getHeaders() throws AuthFailureError { if (headers == null) { return new LinkedHashMap<>(0); } else { return headers; } } }; jsonObjReq.setShouldCache(putInCache); addRequestPolicy(jsonObjReq); // Adding request to request queue App.get().addToRequestQueue(jsonObjReq, "req_object_" + url.hashCode()); }
getString() est la méthode qui sera appelée lors de toutes nos requêtes API. Elle prend en compte 6 paramètres :
- la méthode du protocole HTTP (GET / POST / PUT /DELETE),
- l’url de l’api,
- le header de la requête HTTP,
- un listener qui sera lancé si tout se passe comme prévu : le onResponse,
- un listener qui sera lancé si la requête rencontre un problème : le onErrorResponse,
- si oui ou non, on veut sauvegarder en cache les résultats de la requête.
Ensuite, nous instancions une StringRequest prenant en paramètres tous ceux de notre méthode mise à part le header et le boolean. Concernant le header, il nous suffit de surchargé la méthode getHeaders() de la classe Request (classe étendue par StringRequest). Le boolean, quant à lui, est passé en paramètre de la méthode setSouldCache() de notre StringRequest.
addRequestPolicy() est une méthode créé de toutes pièces qui prends en paramètre une Request et qui lui applique un méchanisme de retry.
public static void addRequestPolicy(Request<> request) { request.setRetryPolicy( new DefaultRetryPolicy( REQUEST_TIMEOUT_MS, // 10 000 DefaultRetryPolicy.DEFAULT_MAX_RETRIES, DefaultRetryPolicy.DEFAULT_BACKOFF_MULT ) ); }
Pour finir, il ne nous reste plus qu’à ajouter notre StringRequest à la RequestQueue de notre application et le tour est joué !
public class App extends Application{ private static App mInstance; private ActivityLifecycle mLifeCycle; private RequestQueue mRequestQueue; @Override public void onCreate() { super.onCreate(); mInstance = this; mRequestQueue = Volley.newRequestQueue(getApplicationContext()); ... } ... public <T> void addToRequestQueue(Request<T> req, String tag) { // set the default tag if tag is empty req.setTag(TextUtils.isEmpty(tag) ? LOG_TAG : tag); mInstance.mRequestQueue.add(req); } }
GSON : Faites de la place au Fils de G.
Késako :
GSON est une bibliothèque open source JAVA développé par Google. Grâce à des méthodes simples tels que toJson() et fromJson(), elle permet de convertir des objets Java en représentations JSON et vice versa. Pour cela, il faut créer un modèle de données.
Prérequis :
Tout comme pour Volley, vous pouvez :
- passer par Gradle :
dependencies { ... compile 'com.google.code.gson:gson:2.8.0' ... }
- récupérer le répertoire GSON directement sur Github (et l’ajouter en tant qu’ Android library module
Et sinon, à quoi ça ressemble ?
Pour cela, nous allons nous baser sur ce lien qui donne accès à un JSON contenant une liste de films et certaines de leurs informations :
- son Id Imdb
- son Titre
- son Année de sortie
- son Réalisateur
- son Casting
- son Poster
public String id; public String title; public String release; public String director; public String mainCast; public String urlPoster;
Afin de bien comprendre et apprécier la simplicité de mise en place d’un modèle de données avec GSON, nous allons tout d’abord voir à quoi ressemble un modèle de données lambda.
package com.project.article.models; import org.json.JSONException; import org.json.JSONObject; public class Movie{ // "idImdb": "tt0475394", // "title": "Smokin' Aces", // "release": "2006", // "director": "Joe Carnahan", // "casting": "Ryan Reynolds, Jeremy Piven, Andy Garcia", // "urlPoster": "https://images-na.ssl-images-amazon.com/images/M/MV5BMTI3NzQwNDEyN15BMl5BanBnXkFtZTcwMDY4ODQ0MQ@@._V1_.jpg" public String id; public String title; public String release; public String director; public String mainCast; public String urlPoster; public Movie() { } /** * METHODES */ public void readFromJson(JSONObject object){ if (object == null) { return; } try { // Store "idImdb" in id id = object.getString("idImdb"); title = object.getString("title"); release = object.getString("release"); director = object.getString("director"); // Store "casting" in mainCast mainCast = object.getString("casting"); urlPoster = object.getString("urlPoster"); } catch (JSONException e) { e.printStackTrace(); } } }
En plus de la déclaration des variables, on remarque une méthode readFromJson() prenant en paramètre un JSONObject. Il est ensuite parsé pour récupérer ses informations et initialiser les propriétés de mon object Movie avec la bonne valeur. Par exemple, l’élément ayant pour clé idImdb dans le JSONObject sera stocké dans la propriété id de mon instantce de Movie.
Cette méthode sera ensuite appelée dans le onResponse de notre requête Volley, donc uniquement si l’appel API s’est bien passée.
public static void getMovies(final BasicListener listener) { getString( Request.Method.GET, Constant.API_URL, // http://dev.webwag.com/olivier/ArticleJson.json headers, new Response.Listener<String>() { @Override public void onResponse(String response) { try { JSONArray jsonArray = new JSONArray(response); for(int i = 0; i < jsonArray.length(); i++){ JSONObject item = jsonArray.getJSONObject(i); Movie movie = new Movie(); movie .readFromJson(item); // ArrayList which contains all ours Movies Constant.listMovies.add(movie); } } catch (JSONException e) { e.printStackTrace(); } listener.onSuccess(); } }, new Response.ErrorListener() { @Override public void onErrorResponse(VolleyError error) { // handle object String code = getErrorCode(error); listener.onError(code); } }, false ); }
Maintenant, voyons voir comment cela se passe avec la mise en place de la librairie GSON.
package com.project.article.models; import com.google.gson.annotations.SerializedName; public class Movie { // "idImdb": "tt0475394", // "title": "Smokin' Aces", // "release": "2006", // "director": "Joe Carnahan", // "casting": "Ryan Reynolds, Jeremy Piven, Andy Garcia", // "urlPoster": "https://images-na.ssl-images-amazon.com/images/M/MV5BMTI3NzQwNDEyN15BMl5BanBnXkFtZTcwMDY4ODQ0MQ@@._V1_.jpg" @SerializedName("idImdb") public String id; public String title; public String release; public String director; @SerializedName("casting") public String mainCast; public String urlPoster; public Movie() { } }
Plus de méthode readFromJson(), elle est maintenant obsolète. En effet, il vous suffit uniquement de déclarer les propriétés de votre objet Movie et GSON s’occupe du reste. Imaginons que, comme dans notre cas, vous souhaitez qu’une (ou plusieurs) de vos propriétés ait un nom diffrent de la clé correspondant à sa valeur, il vous suffit de rajouter l’annotation @SerializedName lors de la déclaration de la dite propriété.
// casting became mainCast @SerializedName("casting") public String mainCast;
(Plus d’informations sur les annotations GSON à ce lien)
Et maintenant, il ne reste plus qu’à récupérer votre liste de Movie directement dans le onResponse() de notre requête Volley. Pour cela, nous utilisons la méthode fromGson() avec comme paramètre :
- la réponse complète de notre requête Volley : ici, response
- la classe à utiliser pour parser le flux obtenu : ici, la classe Movie
public static void getMovies(final BasicListener listener) { getString( Request.Method.GET, Constant.API_URL, // http://dev.webwag.com/olivier/ArticleJson.json headers, new Response.Listener<String>() { @Override public void onResponse(String response) { // ArrayList which contains all ours Movies Constant.listMovies = Arrays.asList(mGson.fromJson(response, Movie[].class)); listener.onSuccess(); } }, new Response.ErrorListener() { @Override public void onErrorResponse(VolleyError error) { // handle object String code = getErrorCode(error); listener.onError(code); } }, false ); }
Maintenant que l’on a à notre disposition une liste complète de Movie, on peut en faire ce que l’on veut. Par exemple, en se basant sur l’article À la découverte du RecyclerView écrit par Nicolas PATIN, on peut obtenir ce type de résultat :