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 :D

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 ! :D

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

Déjà 6 avis pertinents dans Faire du Groovy avec Android

Les commentaires sont fermés.