Faire du Groovy avec Android
Note : La solution technique décrite dans ce billet n’est plus à jour. Pour avoir la version à jour, veuillez lire le billet suivant de la série.
Ceux qui me connaissent et ont déjà développé avec moi savent à quel point je déteste Java.
Hmm… Déjà vu… :/
Bref. J’aime pas beaucoup Java, je l’avoue. Il faut reconnaitre à ce langage d’avoir été une petite révolution quand il est arrivé : il a été parmi les premiers langages à présenter une bibliothèque standard qui couvre pratiquement tous les besoins, jusqu’à fournir par défaut des composants graphiques. Mais le pauvre a mal vieilli. Son créateur est tellement devenu taré qu’il a affirmé que s’il devait réécrire le langage aujourd’hui, il supprimerai la notion d’héritage et ne laisserai que la possibilité de créer des interfaces — le truc le plus inutile de l’histoire de l’humanité. On peut voir cette folie de l’interface partout dans la bibliothèque standard de Java qui est composée pour une moitié d’interfaces et pour l’autres moitié de leur implémentation. D’ailleurs, les créateurs de Java se sont rendus compte que leur machin était tellement inutile qu’ils ont rajouté aux interfaces la possibilité de donner des implémentations aux méthodes des interfaces ce qui en fait… Bah des classes abstraites toutes connes. Quand on sait qu’ils avaient créé les interfaces pour éviter d’avoir à choisir entre plusieurs implémentations d’une même méthode dans un héritage multiple…
Donc, Java a mal vieilli et même s’il a fait des efforts pour perdre du poids, il est devenu laid et verbeux. Là où des langages comme Python ou Ruby saupoudrent leur syntaxe de sucre pour la rendre plus digeste, plus courte, Java se trimbale encore des
MakerBuilderFactory
qui obligent à instancier des cascades d’objets pour calculer une bête suite de Fibo.
Petit exemple pour ouvrir un putain de fichier :
import java.io.BufferedReader; import java.io.FileReader; import java.io.IOException; public class BufferedReaderExample { public static void main(String[] args) { BufferedReader br = null; try { String line; br = new BufferedReader(new FileReader("path/to/file")); while((line = br.readLine()) != null) { System.out.println(line); } } catch (IOException e){ e.printStackTrace(); } finally { try{ if (br != null)br.close(); } catch (IOException ex) { ex.printStackTrace(); } } } }
C’est moche. Faut instancier un
FileReader
pour le passer à un
BufferedReader
, lire le tout dans un
try
, foutre un
finally
à la fin, qui contient lui aussi un
try
, et ne pas oublier
BlaBlaBla
…
J’ai lu quelque part qu’ils avaient appris dans Java 8 :
Files.lines().forEach(s -> System.out.Println(s));
Je ne suis pas très sûr de la syntaxe, pour le coup mais ça me semble être un bon point. Vous remarquerez tout de même cette constance dans la complication avec, bien sûr, la persistance la classe
Files
maintenue à côté de la classe
File
.
Java 8 semble marquer un bon pas en avant avec l’arrivée des closures pour faire du fonctionnel — et j’aime le fonctionnel — et quelques autres jolies choses qui ne sont pas du luxe, mais cette version arrive définitivement trop tard. Crosoft a bouffé le marché avec son langage foutrement bien conçu, le C♯.
Mais je m’éloigne de mon propos de base.
Il se trouve qu’à l’école, j’ai des projets à rendre et, parmis ceux-ci, un projet d’appli Android. J’ai donc remis — après avoir réussi à l’éviter pendant près de deux ans — les pattes dans le Java. Et j’ai souffert. Du coup, quand j’ai appris qu’il faudrait que je développe une appli complète en Java pur, j’ai pleuré sa mère. Et puis, je me suis souvenu qu’il y a peu, j’avais trouvé un moyen de développer un plug-in Maven en Groovy sans taper une ligne de Java.
Présentation de Groovy
Groovy, c’est un langage qui poutre sa race.
Voilà.
Nan, je déconne.
Groovy est un langage qui a été créé par un mec que je sais pas qui c’est et qui est un surcouche de Java. Il est donc parfaitement compatible, une fois compilé avec la plateforme Java, tourne nativement sous la JVM et la syntaxe Java pure est acceptée sans problèmes par le compilo Groovy. Mais ce qui m’intéresse, c’est à quel point Groovy est peu verbeux. C’est un langage syntaxiquement très sucré, qui intègre de très jolies choses comme le typage dynamique :
def string = "Ceci est une chaîne"
La définition de dictionnaire inline :
def map= ['id':'FX-11', 'name':'Radish', 'no':1234, 99:'Y']
Les closures, ce qui rend la lecture de fichiers ligne à ligne très sexy :
myFile = new File("path/to/file") myFile.eachLine({it -> println "File line: " + it }) // Closuuuuuuuuuuuuuuuure ! :D
Les chaînes templates et multilignes :
def value = 6 def string = "La valeur vaut ${value}"
Notons que les classes, en Groovy sont en fait elles mêmes considérées comme des dictionnaires ce qui fait qu’il est parfaitement valide de faire :
class LaClasse { def value = 16 } def key = 'value' def laClasse = new LaClasse() println(laClasse[key]) // Affichera '16'
On peut même écrire :
class LaClasse { def value = 16 } def key = 'value' def laClasse = new LaClasse() println(laClasse."${key}") // Ouep ! Ça marche ;)
Voire :
class LaClasse { def value = 16 } def laClasse = new LaClasse() println(laClasse["value"]) // Hou ! J'ai la trique ! println(laClasse."value") // Oups ! Pollution... Désolé, je vais me changer
C’est super pratique parce que ça permet d’accéder à des données d’une classe comme on accède à du JSON et donc d’accéder à des valeurs via des variables. :p
Bref, j’aime Groovy et pas Java.
Donc je me suis dit que vu comment le développeur de ce langage est un taré de génie, il avait probablement prévu le coup avec Android. Et il se trouve que j’ai eu beaucoup de chance, parce qu’à quelques mois près, Groovy n’était pas encore dispo sur Android
Comment qu’on fait ?
Alors je précise que j’ai testé la manip sur AndroidStudio et pas sur Eclipse avec le plugin à la con.
Et c’est assez simple, en fait. Les projets Android compilent avec Gradle, moteur de compilation dont le fonctionnement est assez similaire à Maven. Je n’ai donc pas été dépaysé. Gradle possède ses propres scripts de compilation, des équivalent du
pom.xml
pour Maven : les fichiers
build.gradle
.
Dans un projet Android tout neuf construit par AndroidStudio, il y en a généralement deux : un à la racine du projet, l’autre dans le dossier
app/
qui contient aussi le code source du projet.
Par confort, appelons-les
build.gradle
et
app.gradle
.
Le contenu du
build.gradle
est le suivant, par défaut avec AS 0.8.0 :
buildscript { repositories { jcenter() } dependencies { classpath 'com.android.tools.build:gradle:0.11.+' } }
Dans ce fichier, il faut passer la ligne dans le bloc
dependancies
à
classpath 'com.android.tools.build:gradle:0.12.+'
Puis, on va modifier le fichier
app.gradle
. De base, son contenu est le suivant :
apply plugin: 'com.android.application' android { compileSdkVersion 20 buildToolsVersion "20.0.0" defaultConfig { applicationId "augier.fr.test" minSdkVersion 19 targetSdkVersion 20 versionCode 1 versionName "1.0" } buildTypes { release { runProguard false proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' } } } dependencies { compile fileTree(dir: 'libs', include: ['*.jar']) }
En tête de fichier, avant la première ligne, on va rajouter le bloc suivant :
buildscript { repositories { mavenLocal() jcenter() } dependencies { classpath 'com.android.tools.build:gradle:0.12.+' classpath 'me.champeau.gradle:gradle-groovy-android-plugin:0.2.1' // On rajoute la dépendance vers le compilo groovy } }
Dans le bloc android{}, on va rajouter à la fin :
packagingOptions { exclude 'META-INF/LICENSE.txt' exclude 'META-INF/groovy-release-info.properties' }
Et enfin, à la toute fin du fichier, on rajoute :
repositories { mavenLocal() jcenter() } dependencies { compile 'org.codehaus.groovy:groovy:2.4.0:grooid' compile ('org.codehaus.groovy:groovy-json:2.4.0') { transitive = false } } android.applicationVariants.all { task "groovy${name}Compile"(type: GroovyCompile) { source = javaCompile.source + fileTree('src/main/java').include('**/*.groovy') destinationDir = javaCompile.destinationDir classpath = javaCompile.classpath groovyClasspath = classpath sourceCompatibility = '1.6' targetCompatibility = '1.6' doFirst { def runtimeJars = plugins.findPlugin("android").getBootClasspath() classpath = files(runtimeJars) + classpath } } javaCompile.dependsOn("groovy${name}Compile") javaCompile.enabled = false }
Le contenu final du fichier est le suivant :
buildscript { repositories { mavenLocal() jcenter() } dependencies { classpath 'com.android.tools.build:gradle:0.12.+' classpath 'me.champeau.gradle:gradle-groovy-android-plugin:0.2.1' } } apply plugin: 'com.android.application' android { compileSdkVersion 20 buildToolsVersion "20.0.0" defaultConfig { applicationId "augier.fr.test" minSdkVersion 19 targetSdkVersion 20 versionCode 1 versionName "0.0.1" } buildTypes { release { runProguard false proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' } } packagingOptions { exclude 'META-INF/LICENSE.txt' exclude 'META-INF/groovy-release-info.properties' } } dependencies { compile fileTree(dir: 'libs', include: ['*.jar']) } repositories { mavenLocal() jcenter() } dependencies { compile 'org.codehaus.groovy:groovy:2.4.0:grooid' compile ('org.codehaus.groovy:groovy-json:2.4.0') { transitive = false } } android.applicationVariants.all { task "groovy${name}Compile"(type: GroovyCompile) { source = javaCompile.source + fileTree('src/main/java').include('**/*.groovy') destinationDir = javaCompile.destinationDir classpath = javaCompile.classpath groovyClasspath = classpath sourceCompatibility = '1.6' targetCompatibility = '1.6' doFirst { def runtimeJars = plugins.findPlugin("android").getBootClasspath() classpath = files(runtimeJars) + classpath } } javaCompile.dependsOn("groovy${name}Compile") javaCompile.enabled = false }
Et c’est tout !
Ensuite, Gradle va se charger lui-même de télécharger toutes les dépendances kivonbien et faire tourner le tout. Vous pouvez alors taper votre code Groovy dans des fichiers
.groovy
placés au même endroit que votre code Java (
src/main/java
) et vos fichiers Groovy devraient être compilés comme des fichiers Java classiques.
J’ai pas encore essayer d’écrire un fichier Groovy et l’utiliser dans un fichier Java, mais au moins, ça compile. Si jamais j’ai des soucis avec la compilation des fichiers Groovy, promis, je vous tiens au courant
Edit : Je viens d’essayer et ça marche vachement bien ! J’ai la trique !
Déjà 6 avis pertinents dans Faire du Groovy avec Android
Les commentaires sont fermés.
Sinon, merci pour l’info. Il est possible que je refasse un article sur la question. J’ai essayé ces deux derniers jours quelques combinaisons avec les bibliothèques SwissKnife (que j’ai pas réussi à finir d’installer avant d’aller me coucher hier soir) et AndroidAnnotation, que j’ai pas réussi à faire fonctionner.
Pour écrire ce post, je me suis appuyé sur ton post qui, effectivement, décrivait la procédure pour la version SNAPSHOT de Groovy 2.4. J’ai fais moi-même les quelques adaptations après-coup.
Par contre, si tu peux m’expliquer cette partie, dans mon projet, les fichiers dans
src/main/groovy
ne sont pas pris en compte, je suis obligé de les placer danssrc/main/java
.J’ai encore une question : dans ce billet, tu dis que Groovy est capable de faire du passage de message. J’ai toujours interprété le passage de messages comme un système équivalent aux signaux/slots de Qt ou aux events handlers de C#. En Java, il faut implémenter soit-même le design patter observer/observable. Est-ce que Groovy possède un système équivalent aux signaux/slots de Qt ?
Un article paraîtra demain pour rectifier celui-ci. Si tu as le temps de faire des remarques, je serait à l’écoute.
Il traite aussi de l’utilisation de SwissKnife, un peu.