Android,
A Complete Course, From
Basics to Enterprise Edition
Android, A Complete Course, From Basics to Enterprise Edition
Composants graphiques complexes Android.
1 Maven et Intégration continue
Cet
article est extraie du livre « Android, A Complete Course »,
disponible sur Android2ee.com.
Les
exemples ou les programmes présents dans cet ouvrage sont fournis pour
illustrer les descriptions théoriques. Ce code est libre de toute utilisation
mais n'est pas distribuable.
La
distribution du code est reservée au site :
La
propriété intellectuelle du code appartient à :
L’utilisation
de ces codes est sous votre unique responsabilité et personne d’autre que vous
ne pourra être tenu responsable des préjudices ou dommages de quelques natures
que ce soit pouvant résulter de son utilisation.
Tous
les noms de produits ou marques cités dans cet ouvrage sont des marques déposés
par leurs propriétaires respectifs.
Publié par http://android2ee.com
Titre Original : Android, A Complete Course, From
Basics to Enterprise Edition. Édition Française.
ISBN : 979-10-90388-00-0
Copyright © 2011 by Mathias Séguy
Aucune
représentation ou reproduction, même partielle, autre que celles prévues à
l’article L. 122-5 2° et 3° a) du code de la propriété intellectuelle ne peut
être faite sans l’autorisation expresse de Mathias Seguy ou, le cas échéant,
sans le respect des modalités prévues à l’article L. 122-10 dudit code
http://www.sonatype.com/books/mvnref-book/reference/android-dev.html
Tout d’abord, il vous faut installer Maven sur votre poste de travail : http://maven.apache.org/download.html et télécharger la dernière distribution de Maven. Il vous faut ensuite, décompresser cette archive puis :
· Rajouter la variable d’environnement : M2_HOME en lui indiquant le chemin de votre dossier décompressé.
· Rajouter dans votre path system la variable %M2_HOME%\bin
La page http://maven.apache.org/download.html explique en détail les opérations à effectuer (en bas de page).
Arrêtons de penser « Android » un instant et revenons à une pensée « Java ».
L'objectif de Maven est de rationaliser au moyen de normes et d'outils le cycle de vie d'un projet. Cela signifie gérer la compilation, le passage des tests unitaires, le packaging, le déploiement, les rapports du projet (java doc, rapports de tests, PMD), la liaison avec un outil de versionning des sources, la liaison avec un outil d'intégration continue....
Pour
cela Maven propose des outils et des normes.
Les outils permettent de s'abstraire de l'écriture de ces taches (MakeFile, Ant). La normalisation permet à tout développeur de passer d'un projet Maven à un autre sans être perdu. Le fichier POM (Project Object Model) est un fichier normalisé qui décrit le projet et permet à Maven de lui faire suivre un cycle de vie.
Mais commençons par le commencement et construisons un premier projet Maven.
Créer
un projet MyProject
Créer un fichier pom.xml dans MyProject et y écrire le contenu détaillé ici :
<project>
<modelVersion>4.0.0</modelVersion>
<groupId>com.mycompany.myproject</groupId>
<artifactId>MyProject</artifactId>
<version>SNAPSHOT</version>
</project>
Créer le dossier src/main/java/com/mycompany/myproject dans MyProject
Écrire le source src/main/java/com/mycompany/myproject/Person.java dedans
package
com.mycompany.myproject;
public class Person {
public String name;
public String telephone;
public String email;
public String toString()
{
return new StringBuilder()
.append(name).append(",")
.append(telephone).append(",")
.append(email).toString();
}
}
Compiler et packager: Effectuez "mvn package" dans le dossier MyProject
[INFO] Building jar:
/MyProject/target/MyProject-SNAPSHOT.jar
[INFO]
------------------------------------------------------------------------
[INFO] BUILD SUCCESSFUL
[INFO]
------------------------------------------------------------------------
Les sources du projet sont compilées dans target/classes
Le projet est packagé dans target/MyProject-SNAPSHOT.jar
Tout d'abord, Maven propose un système de coordonnées sur l'espace de tous les projets informatiques. Ces coordonnées possèdent trois variables (comme la notion de latitude, longitude, altitude) qui sont:
groupId: Identifiant du groupe (organisation, équipe, compagnie, ou toute autre notion de groupe de développement). La convention est de commencer par le nom de domaine de l'organisation (comme pour le package root java du projet) qui est com, net, org... Exemple:net.fr.tlse.sti.android
artifactId: Identifiant du projet au sein de l'ensemble des projets appartenant au groupId. Il ne faut pas utiliser de « . » dans le nom des artifactId.
version: Identifiant de la version à construire. Il y en a de deux types distincts, le premier est celui connu de tous (un numéro de version normal) et le second s'appelle SnapShot. Par convention, pour un numéro de version normal, cet identifiant doit être:
<major version>.<minor version>.<incremental version>-<qualifier>
Ce qui donne par exemple 2.1.0-alpha. Le qualifier n'est pas obligatoire, les autres sont fortement recommandés. Une version de type SnapShot est en fait une syntaxe permettant de construire des versions (typiquement au cours du build de la nuit) nommées en utilisant le jour où a eu lieu le build. Cette syntaxe est à utiliser pour un projet en cours de développement qui n'atteint pas une version mais qui effectue des build réguliers (soit pour une intégration continue, soit pour une mise à disposition du source pour d'autres modules, projets).
Une quatrième notion d'identifiant est la suivante:
Classifier : Permet d'ajouter à une coordonnée (donc un même projet avec une même version i.e. Le même code) une information supplémentaire. Cela peut être le type de JDK utilisé pour le build, ou la plateforme de destination,... C'est une possibilité pour permettre d'ajouter de l'information à un même code qui n'est pas packagé de la même manière. En d'autre termes, un projet qui sera compilé avec le JDK 1.3 et le JDK 1.6 et qui n'a pas de code modifié utilisera la notion de classifier pour distinguer les deux builds.
Ainsi
notre premier POM définit le projet (son nom unique et exact).
Par convention, Maven suppose que la structure du projet est la suivante:
my-app
|-- pom.xml
`-- src
|-- main
| |-- java
| | `-- com
| | `-- mycompany
| | `-- app
| | `-- App.java
| `-- resources
| |-- com
| `-- mycompany
| `-- app
| `-- App.properties
`-- test
|-- java
| `-- com
| `-- mycompany
| `-- app
| `-- AppTest.java
`-- resources
`-- com
`-- mycompany
`-- app
`-- AppTest.properties
Ainsi il n'a pas besoin d'informations supplémentaires pour compiler, packager ou tester un projet. Le respect de cette structure permet à Maven d'effectuer les actions sur le projet. C'est pour cette raison que la classe a été positionnée dans le répertoire src/main/java/com/mycompany/myproject.
Dans
le cas d’Android, cette structure ne peut être respectée, elle est en
opposition avec la structure imposée pour un projet Android, c’est à Maven de
s’adapter et non pas à Android.
Pour maven la construction d'un projet s'effectue toujours étape par étape. Chaque phase étant nécessaire pour que la phase suivante s'effectue. Chaque étape peut se composer de plusieurs sous taches, objectif (goal). La liste des taches pour un build est:
Validate : Valide que le projet est correct et que toutes les informations
nécessaires sont présentes.
Compile :
Compile le code source du projet
Test :
Teste le code source compilé en utilisant le framework de tests disponible. Ces
tests ne nécessitent pas que le code soit déployé ou packagé.
Package :
Package le code compile dans son format de distribution cible (Jar, War, Apk).
Integration-test : Génère
et déploie le package si nécessaire dans un espace d’intégration pour effectuer
les tests d’intégration.
Verify : Lance les vérifications permettant
d’assurer que le package est valide et les critères de qualité rencontrés.
Install :
Installe le package dans le repository local pour être utilisé en tant que
dépendance vers les projets locaux qui le référence.
Deploy:
Effectué dans un espace d’intégration ou de production, elle copie le package
final dans le repository distant permettant son partage avec d’autres développeurs
et projets.
Ainsi pour que maven effectue une tâche, il suffit de le lui indiquer. Ainsi pour lancer:
· la compilation: « mvn:package » suffit,
· les tests, « mvn:verify » suffit,
· le partage avec d'autres projets, « mvn:install » doit être appelé (le package est déployé dans le repository local).
Chaque phase contient un ensemble d'objectifs. Chaque phase est liée à un goal principal, c'est lui qui est appelé lors du mvn:***. Ce mapping est partiellement reproduit ci dessous.
Etape (ou phase) |
But (ou goals) |
process-resources |
resources:resources |
compile |
compiler:compile |
process-test-resources |
resources:testResources |
test-compile |
compiler:testCompile |
test |
surefire:test |
package |
jar:jar |
install |
install:install |
deploy |
deploy:deploy |
Ce paragraphe est une copie du paragraphe 15.1.1
Tout d’abord, il vous faut installer Maven sur votre poste de travail : http://maven.apache.org/download.html et télécharger la dernière distribution de Maven. Il vous faut ensuite, décompresser cette archive puis :
· Rajouter la variable d’environnement : M2_HOME en lui indiquant le chemin vers votre dossier décompressé.
· Rajouter dans votre path system la variable %M2_HOME%\bin
La page http://maven.apache.org/download.html explique en détail les opérations à effectuer (en bas de page).
Un
utilitaire est disponible pour effectuer cette opération à l’adresse
suivante : http://github.com/mosabua/maven-android-sdk-deployer
Il faut télécharger les sources et les décompresser sur votre ordinateur. Ensuite, en ligne de commande, il faut se placer dans le dossier décompressé et lancer la commande mvn clean install. Cela installera l’ensemble des jars Android et Google sous votre repository maven .m2/.
Il ne reste plus qu’à mettre à jour votre fichier setting.xml pour permettre à Maven d’utiliser le plugin Android-Maven.
Il y a deux fichiers setting.xml (voir n). L'un se trouve %M2_HOME%/conf/setting.xml et l'autre sur User/.m2/setting.xml. Le premier sera visible par tous les utilisateurs de la machine, le second uniquement par l'utilisateur User.
Il est recommandé pour les postes de travail sur lequel ne travaille qu'un utilisateur de le mettre dans %M2_HOME%/conf/setting.xml et surtout de ne pas écrire dans l'un ou dans l'autre à moins d'avoir des raisons vraiment valables.
Le
fichier setting possède la structure suivante:
<settings xmlns="http://maven.apache.org/SETTINGS/1.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/SETTINGS/1.0.0
http://maven.apache.org/xsd/settings-1.0.0.xsd">
<localRepository />
<interactiveMode />
<usePluginRegistry />
<offline />
<pluginGroups />
<servers />
<mirrors />
<proxies />
<profiles />
<activeProfiles />
</settings>
Il vous faut rajouter le code suivant dans le bloc pluginGroup :
<pluginGroups>
<pluginGroup>
com.jayway.maven.plugins.android.generation2
</pluginGroup>
</pluginGroups>
Dans Eclipse, faites un clic droit sur votre projet Maven->Enable Depencies. Cela ouvre une boite de dialogue qui vous demande :
· Le GroupId de votre projet, cela correspond au package root de votre projet ( NomDeDomaineDeLOrganisation.Organisation.PackageRoot, exemple : net.stinfoservices.android.tuto.maven),
· L’ArtifactId de votre projet, cela correspond à l’identifiant de votre projet au sein des projets appartenant à votre GroupId (si votre group id est suffisamment précis, le nom de votre projet suffit),
· La version de votre projet,
· Le packaging de votre projet qui est apk (c’est le packaging pour les projets Android, même si ce type n’est pas listé dans le wizard),
· Le nom et la description sont des paramètres optionnels.
Il ne vous reste plus qu’à mettre à jour votre fichier Pom :
<modelVersion>4.0.0</modelVersion>
<groupId>net.stinfoservices.android.tuto.maven</groupId>
<artifactId>MavenAndroidTuto</artifactId>
<version>0.0.1-SNAPSHOT</version>
<packaging>apk</packaging>
<name>MavenAndroidTuto</name>
<dependencies>
<dependency>
<groupId>android</groupId>
<artifactId>android</artifactId>
<version>2.3_r1</version>
<scope>provided</scope>
</dependency>
</dependencies>
<build>
<sourceDirectory>src</sourceDirectory>
<plugins>
<plugin>
<groupId>
com.jayway.maven.plugins.android.generation2
</groupId>
<artifactId>maven-android-plugin</artifactId>
<version>2.8.3</version>
<configuration>
<sdk>
<platform>2.3</platform>
</sdk>
<deleteConflictingFiles>
true
</deleteConflictingFiles>
</configuration>
<extensions>true</extensions>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>2.3.2</version>
<configuration>
<source>1.6</source>
<target>1.6</target>
</configuration>
</plugin>
</plugins>
</build>
</project>
Il vous faut remplacer les versions suivantes dans ce fichier Pom : la version de la dépendance Android, la version du plugin jayway.
La dépendance Android doit avoir le même numéro de version que votre projet (suivi de _r1 ou _r2) et pointe soit vers Android soit vers Google. Pour une dépendance vers Google il faut que vous mettiez le bloc :
<dependency>
<groupId>com.google.android</groupId>
<artifactId>android</artifactId>
<version>2.3.1</version>
</dependency>
Dans ce cas, le numéro de version n’est pas suffixé par _r1 ou _r2.
Le lien entre la plateforme et l’API level est le suivant :
Real Name[1] |
Target name |
Platform (version) |
API Level |
Google Inc.:Google APIs:3 |
Google APIs (Google Inc.) |
1.5 |
3 |
Google Inc.:Google APIs:4 |
Google APIs (Google Inc.) |
1.6 |
4 |
Google Inc.:Google APIs:7 |
Google APIs (Google Inc.) |
2.1-update |
7 |
Google Inc.:Google APIs:8 |
Google APIs (Google Inc.) |
2.2 |
8 |
Google Inc.:Google APIs:9 |
Google APIs (Google Inc.) |
2.3 |
9 |
Google Inc.:Google APIs:10 |
Google APIs (Google Inc.) |
2.3.3 |
10 |
Google Inc.:Google APIs:11 |
Google APIs (Google Inc.) |
3.0 |
11 |
android-3 |
Android 1.5 |
1.5 |
3 |
Android-4 |
Android 1.6 |
1.6 |
4 |
Android-7 |
Android 2.1-update1 |
2.1-update |
7 |
Android-8 |
Android 2.2 |
2.2 |
8 |
Android-9 |
Android 2.3 |
2.3 |
9 |
Android-10 |
Android 2.3.3 |
2.3.3 |
10 |
Android-11 |
Android 3.0 |
3.0 |
11 |
La version du plugin Android (com.jayway.maven.plugins.android.generation2) doit être la dernière version de ce plugin. Pour la connaitre, rendez vous sur http://mvnrepository.com/artifact/com.jayway.maven.plugins.android.generation2/maven-android-plugin et recopiez le numéro le plus récent de la liste.
Il faut que vous rajoutiez à votre build path, le dossier target/generated-source/r en tant que fichier source pour qu’Eclipse puisse retrouver les fichiers de ressources générés et construire le projet.
Ce qui revient à rajouter la ligne suivante au fichier .classpath de votre projet:
<classpathentry kind="src" path="target/generated-sources/r"/>
Pour lancer le build, faites, clic droit sur le projet, Run As->Maven Install.
Pour lancer automatiquement un déploiement, il vous faut enregistrer une configuration de Run dans Eclipse qui lance les goals « mvn clean install android:deploy ». Pour cela, clic droit sur le projet, Run As-> Run Configuration…
Dans la fenêtre qui s’ouvre, vous devez sélectionner « Maven Build » puis appuyer sur le bouton new (icône en haut à droite). Mettez le nom de votre configuration, par exemple « Android Deploy NomDeVotreProjet ». Dans le champ base directory, sélectionnez la racine de votre projet. Dans le champ goal, mettez « clean install android:deploy ».
Dans ce cas, il faut que votre émulateur soit déjà lancé, sinon vous aurez une erreur de type « device not found ».
Enfin, si vous souhaitez pointer vers un Android Sdk spécifique (ou que vous n’avez pas déclaré votre variable d’environnement Android_Home), vous devez ajouter ce chemin dans le bloc de configuration qui déclare la version du SDK Android à utiliser :
<sdk>
<platform>2.3</platform>
<path>D:\Eclipse\eclipsex64_Android_Custo\android-sdk_r10-windows\android-sdk-windows</path>
</sdk>
Il suffit d’ajouter l’une des dépendances suivantes (en fonction
de la version du SDK Android que vous utilisez)
<dependency>
<groupId>com.google.android.maps</groupId>
<artifactId>maps</artifactId>
<version>3_r3</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>com.google.android.maps</groupId>
<artifactId>maps</artifactId>
<version>4_r1</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>com.google.android.maps</groupId>
<artifactId>maps</artifactId>
<version>7_r1</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>com.google.android.maps</groupId>
<artifactId>maps</artifactId>
<version>8_r1</version>
<scope>provided</scope>
</dependency>
Si débute une guerre sans fin entre vous et votre tache mvn:install, vérifiez les éléments suivants :
· La première chose à faire est de vérifier que votre fichier setting.xml est bien à jour.
· Vous devez aussi revérifier que vos numéros de versions sont cohérents.
· Vous devez vérifier que votre variable d’environnement Android_Home est bien positionnée ou que vous avez déclaré votre SDK Android dans votre fichier pom.xml sous la version du SDK Android.
· Et enfin, essayez de faire clic droit sur votre projet Maven->Updates Dependencies
Enfin, si vous obtenez une erreur du type :
C:\Android\android-sdk_r07-windows\android-sdk-windows/platform-tools/dx.bat [--dex, --output=C:\Users\David\Development Workspace\MartiniLocker\target\classes.dex, C:\Users\David\Development Workspace\MartiniLocker\target\android-classes]
[INFO] =C:\Users\David\Development was unexpected at this time.
Ou
[INFO] =C:\Users\David\Development est inattendu.
C’est que votre chemin vers votre fichier Pom possède des espaces, et là, la seule chose qu’il vous reste à faire c’est de pleurer puis de renommer votre chemin en supprimant les espaces (Attention au nom de votre workspace Eclipse) (http://code.google.com/p/maven-android-plugin/issues/detail?id=115&colspec=ID%20Type%20Component%20OpSys%20Status%20Priority%20Milestone%20Owner%20Summary ).
Ce goal permet de générer l’application au format apk.
Ce goal permet de construire l’application et de la déployer sur un émulateur déjà connecté (déjà lancé). Il est lancé automatiquement lors de mvn:install.
Ce goal permet de déployer les dépendances de type apk sur l’émulateur. Son utilisation la plus courante est lors des tests, il permet de déployer l’application à tester.
Il est lancé automatiquement lors de mvn:install.
Ce goal permet de lancer l’émulateur ou de l’arrêter. Nous y revenons dans un chapitre dédié au lancement de l’émulateur.
L’utilisation des archétypes Maven permet de créer des projets pré-structurés. Nous présentons ici les trois archétypes (QuickStart, WithTest et Release) qui permettent de mettre en place un projet Android. L’idée est de commencer sur un projet qui possède déjà toute la structure souhaitée et de pouvoir se concentrer sur le code directement.
Ces archétypes sont donnés à la communauté par akquinet A.G et se trouve à la page suivante :
https://github.com/akquinet/android-archetypes/wiki
Une fois que votre projet est créé, vous pouvez le déplacer dans votre workspace Eclipse et faire menu File ->import->import Maven Project. Votre projet est alors importé dans votre workspace.
Pour tous ces archétypes, il vous faut rajouter dans le manifeste de votre projet la ligne spécifiant la version minimale du SDK à utiliser :
<uses-sdk
android:minSdkVersion="3" />
Dans votre shell, taper la commande :
mvn
archetype:generate
-DarchetypeArtifactId=android-quickstart
-DarchetypeGroupId=de.akquinet.android.archetypes -DarchetypeVersion=1.0.4 -DgroupId=your.company -DartifactId=my-android-application
Il crée un projet Maven élémentaire qu’il vous suffit d’importer dans Eclipse pour avoir le squelette d’une application Android mavenisée. Ce projet est créé là où vous avez lancé la commande dans votre shell (si vous êtes sous myPath>, le projet sera créé sous myPath\).
Le projet généré est le suivant :
Le fichier POM de ce projet est le suivant :
<?xml version="1.0"
encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0
http://maven.apache.org/maven-v4_0_0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>net.stinfoservices.android.tuto.maven</groupId>
<artifactId>myQuickStartTuto</artifactId>
<version>1.0</version>
<packaging>apk</packaging>
<name>myQuickStartTuto</name>
<dependencies>
<dependency>
<groupId>com.google.android</groupId>
<artifactId>android</artifactId>
<version>2.2.1</version>
<scope>provided</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<!-- Simply read properties from file -->
<groupId>org.codehaus.mojo</groupId>
<artifactId>properties-maven-plugin</artifactId>
<version>1.0-alpha-2</version>
<executions>
<execution>
<phase>initialize</phase>
<goals>
<goal>read-project-properties</goal>
</goals>
<configuration>
<files>
<file>android.properties</file>
</files>
</configuration>
</execution>
</executions>
</plugin>
<plugin>
<groupId>com.jayway.maven.plugins.android.generation2</groupId>
<artifactId>maven-android-plugin</artifactId>
<version>2.8.3</version>
<configuration>
<androidManifestFile>${project.basedir}/AndroidManifest.xml</androidManifestFile>
<assetsDirectory>${project.basedir}/assets</assetsDirectory>
<resourceDirectory>${project.basedir}/res</resourceDirectory>
<nativeLibrariesDirectory>${project.basedir}/src/main/native</nativeLibrariesDirectory>
<sdk>
<!-- Defined in the android.properties file -->
<platform>7</platform>
</sdk>
<deleteConflictingFiles>true</deleteConflictingFiles>
<undeployBeforeDeploy>true</undeployBeforeDeploy>
</configuration>
<extensions>true</extensions>
</plugin>
<plugin>
<artifactId>maven-compiler-plugin</artifactId>
<version>2.3.2</version>
<configuration>
<source>1.5</source>
<target>1.5</target>
</configuration>
</plugin>
</plugins>
</build>
</project>
Ce fichier POM définit les éléments suivants :
·
Son identité (Son GroupId, son ArtifactID, Sa
version, son packaging et son nom)
·
Ses dépendances : ici uniquement le jar
Android dans sa version 2.2.1
·
Les plugins utilisés pour la construction de
l’apk :
o Le
plugin properties-maven-plugin qui initialise la lecture des propriétés du
projet
o Le
plugin maven-android-plugin qui compile le projet en un composant apk. Il
utilise la définition des éléments principaux du projet (la localisation du
dossier asset, du dossier ressources, du manifeste Android, ainsi que la
localisation des libs) et définit la version du SDK Android à utiliser.
o
Le plugin de compilation Java qui définit la
version du SDK Java à utiliser.
Si vous
souhaitez modifier la version du SDK Android à utiliser, vous devez aussi le
modifier dans le fichier android.properties de votre projet.
Par
défaut, les librairies à utiliser se trouvent sous ${project.basedir}/libs. En d’autres termes, la valeur de la propriété nativeLibrariesDirectory est ${project.basedir}/libs, celle-ci a été redéfinie, je ne sais pas pourquoi.
Dans votre shell, taper la commande :
mvn
archetype:generate
-DarchetypeArtifactId=android-with-test
-DarchetypeGroupId=de.akquinet.android.archetypes -DarchetypeVersion=1.0.4 -DgroupId=your.company -DartifactId=my-android-application
Il crée deux projets Eclipse en tant que projet Maven de type multi-modules. Ces deux projets sont assez étonnants, le projet de test qui apparaît n’est qu’un duplicata (logique) de celui du projet principal. Le projet principal qui contient votre application, possède :
· Le Pom parent
· Une arborescence pour le projet de l’application,
· Une arborescence pour le projet de test.
Ce projet Eclipse est ainsi complet. Le second projet est le projet de tests spécifique externalisé. Il n’est qu’une vue du projet de tests du projet principal. Il pointe vers le Pom parent du projet principal et est un pointeur vers l’arborescence de test du projet principal.
Les projets sont les suivants :
Et le projet de tests uniquement :
Il est important de bien comprendre que le projet Eclipse de test MonProjet-it est une vue du projet de test qui appartient à votre projet principal. Toute modification de l’un modifie l’autre. Cette structure permet de marier les deux philosophies associées aux tests (projet de tests externalisé ou plongé dans le projet principal).
Dans votre shell, taper la commande :
mvn
archetype:generate
-DarchetypeArtifactId=android-release
-DarchetypeGroupId=de.akquinet.android.archetypes -DarchetypeVersion=1.0.4 -DgroupId=your.company -DartifactId=my-android-application
Le projet généré est une extension du projet associé à l’archétype Android-With-Test. Il met en plus en place un système de profil permettant la génération de l’application finale prête à être déployée sur le Market (elle est signée et optimisée). La structure des projets générés par ces deux archétypes (with-test et release) est identique.
Pour l’utiliser il faut modifier votre fichier setting.xml associé à Maven et rajouter le code suivant :
<profile>
<id>android-release</id>
<properties>
<sign.keystore>/path/to/keystore</sign.keystore>
<sign.alias>key alias</sign.alias>
<sign.storepass>keystore password</sign.storepass>
<sign.keypass>key password</sign.keypass>
</properties>
</profile>
Dans ce profil, vous définissez le chemin vers votre KeyStore, l’alias de la clef à utiliser, le mot de passe du KeyStore ainsi que celui de votre clef. Ce profil sera utilisé pour signer votre application lors du build (mvn:install).
Pour signer automatiquement votre application, il suffit d’ajouter dans votre fichier pom.xml le bloc suivant dans la balise <build><pluginManagement><plugins>:
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-jarsigner-plugin</artifactId>
<version>1.2</version>
<executions>
<execution>
<id>signing</id>
<goals>
<goal>sign</goal>
</goals>
<phase>package</phase>
<inherited>true</inherited>
<configuration>
<includes>
<include>
${project.build.directory}/target/${project.artifactId}-${project.version}.apk
</include>
</includes>
<keystore>${keystore.location}</keystore>
<storepass>${keystore.password}</storepass>
<keypass>${keystore.keypass}</keypass>
<alias>${keystore.alias}</alias>
<verbose>true</verbose>
</configuration>
</execution>
</executions>
</plugin>
Ce plugin permet de spécifier que dans la phase package de l’application, il lance le but sign sur l’apk qui vient d’être construit en utilisant la clef dont le nom est keyStore.alias, le mot de passe keystore.keypass qui se trouve dans le keyStore keyStore.location dont le mot de passe est keyStore.password.
Pour que cela fonctionne, il faut modifier votre fichier setting.xml associé à Maven et rajouter le code suivant :
<profile>
<id>android-release</id>
<properties>
<sign.keystore>/path/to/keystore</sign.keystore>
<sign.alias>key alias</sign.alias>
<sign.storepass>keystore password</sign.storepass>
<sign.keypass>key password</sign.keypass>
</properties>
</profile>
Dans ce profil, vous définissez le chemin vers votre KeyStore, l’alias de la clef à utiliser, le mot de passe du KeyStore ainsi que celui de votre clef. Ce profil sera utilisé pour signer votre application lors du build (mvn:install).
Sinon, vous pouvez aussi remplacer dans votre fichier pom.xml les variables du KeyStore par leurs valeurs, mais dans ce cas, votre projet se construira uniquement sur votre machine, vous pouvez dire au revoir à l’intégration continue.
Pour pouvoir lancer l’émulateur lors de la compilation et ainsi mettre en place le déploiement automatique de votre apk lorsque celui-ci est construit, il faut tout d’abord définir l’émulateur cible dans votre fichier pom.xml. Cette déclaration s’effectue dans la partie configuration du plugin maven-android-plugin :
<plugin>
<groupId>
com.jayway.maven.plugins.android.generation2
</groupId>
<artifactId>maven-android-plugin</artifactId>
<version>2.8.3</version>
<configuration>
<sdk>
<platform>2.3</platform>
<path>D:\Eclipse\android-sdk_r08-windows\android-sdk-windows</path>
</sdk>
<deleteConflictingFiles>
true
</deleteConflictingFiles>
<emulator>
<avd>Android2Dot3Bis</avd>
<wait>6000</wait>
</emulator>
</configuration>
<extensions>true</extensions>
</plugin>
L’AVD que vous déclarez est un AVD que vous avez défini dans votre SDK Android, soit via Eclipse (avec le bouton Android SDK and AVD Manager) soit en ligne de commande. La balise wait permet de définir le temps d’attente avant d’essayer de déployer l’apk sur l’émulateur. Ce temps d’attente correspond au temps de lancement de l’émulateur.
Dans l’exemple, celui-ci pointe vers l’AVD Android2Dot3 défini ci-dessous :
Ensuite, vous pouvez utiliser la tache maven « clean install andoird:start-emulator android:deploy » pour effectuer la compilation et le déploiement. En effet la tache mvn android:start-emulator démarre l’émulateur et la tache mvn android:stop-emulator le stoppe.
Ou vous pouvez modifier votre fichier Pom, en lui ajoutant la commande de lancer l’émulateur au démarrage du build :
<plugin>
<groupId>com.jayway.maven.plugins.android.generation2</groupId>
<artifactId>maven-android-plugin</artifactId>
<version>2.8.3</version>
<configuration>
<sdk>
<platform>2.3</platform>
<path>D:\Eclipse\
android-sdk_r08-windows\android-sdk-windows</path>
</sdk>
<deleteConflictingFiles>
true
</deleteConflictingFiles>
<emulator>
<avd>Android2Dot3Bis</avd>
<wait>6000</wait>
</emulator>
</configuration>
<executions>
<execution>
<id>startEmulator</id>
<phase>initialize</phase>
<goals>
<goal>emulator-start</goal>
</goals>
</execution>
</executions>
<extensions>true</extensions>
</plugin>
Dans ca cadre, votre émulateur sera lancé lors de la phase d’initialisation du cycle de vie de Maven. Vous pouvez aussi vouloir arrêter l’émulateur quand le passage des tests est terminé (la phase « instrumentation tests »), dans ce cas il suffit d’ajouter le bloc :
<executions>
<execution>
<id>startEmulator</id>
<phase>initialize</phase>
<goals>
<goal>emulator-start</goal>
</goals>
</execution>
<execution>
<id>stopEmulator</id>
<phase>install</phase>
<goals>
<goal>emulator-stop</goal>
</goals>
</execution>
</executions>
L’utilisation des profils Maven a pour but :
· de pouvoir lancer des campagnes de tests sur plusieurs émulateurs de manière automatique avec Hudson,
· de pouvoir signer ou pas son application et de l’optimiser en fonction du contexte « développement » ou « production »,
· d’utiliser des variables spécifiques à votre environnement de développement.
Oublions Android quelques instants et penchons nous sur la notion des profils Maven.
Les
profils permettent de spécifier le comportement d'un fichier Pom en fonction
d'un profil. Ainsi, dans le fichier POM on peut ajouter
des Profils. Chaque Profil surchargera
un ou plusieurs blocs du POM qui le contient de manière à spécifier un
comportement lors du build. Ce comportement spécifique sera attaché au profil.
Chaque profil possède un identifiant unique: son id. Pour lancer un build avec
un profil spécifique il suffit d'utiliser la commande :mvn **:** -PmonProfilId.
La notion de Profil s'hérite entre fichier POM.
Un
exemple typique est l'utilisation d'un profil dev (pour développement) et d'un
profil prod (pour production). L'un mappant avec la base
de données utilisée pour le développement avec des
options de compilation non optimisées en debug, l'autre avec la base de données
de production et dont la compilation sera optimisée sans le mode debug.
Ce qui
s'implémente (sans mettre les bases de données) dans le fichier Pom de la
manière suivante:
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0
http://maven.apache.org/maven-v4_0_0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>monGroupId</groupId>
<artifactId>simple</artifactId>
<packaging>jar</packaging>
<version>1.0</version>
<name>simple</name>
<profiles>
<profile>
<id>prod</id>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<debug>false</debug>
<optimize>true</optimize>
</configuration>
</plugin>
</plugins>
</build>
</profile>
<profile>
<id>dev</id>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<debug>true</debug>
<optimize>false</optimize>
</configuration>
</plugin>
</plugins>
</build>
</profile>
</profiles>
</project>
Les
profils
permettent de surcharger un ensemble de bloc du fichier POM initial, ces blocs
sont les suivants:
<project>
<profiles>
<profile>
<id>monProfilId</id>
<build>
<defaultGoal>...</defaultGoal>
<finalName>...</finalName>
<resources>...</resources>
<testResources>...</testResources>
<plugins>...</plugins>
</build>
<reporting>...</reporting>
<modules>...</modules>
<dependencies>...</dependencies>
<dependencyManagement>...</dependencyManagement>
<distributionManagement>...</distributionManagement>
<repositories>...</repositories>
<pluginRepositories>...</pluginRepositories>
<properties>...</properties>
</profile>
</profiles>
</project>
Les
profils peuvent être définis dans un fichier profils.xml (qui doit se trouver à
côté du fichier Pom). Cela permet de ne pas avoir un fichier Pom trop
illisible. Mais le processus est le même, à la lecture du fichier POM, Maven
merge les deux documents (ce qui peut avoir des effets déroutants si les
profils sont définis dans le fichier POM aussi).
Un profil
peut s'activer (et même s'auto activer) en utilisant des conditions
d'activation, ces conditions portent sur:
·
la valeur d'une propriété (JDK, l'os, une propriété
spécifiée dans le POM parent...)
·
l'absence de l'existence d'une propriété
·
l'absence ou l'existence d'un fichier
·
la définition des profils actifs définis
dans le fichier setting.xml
Ainsi le
profil suivant :
<profile>
<id>jdk16</id>
<activation>
<jdk>1.6</jdk>
</activation>
<modules>
<module>simple-script</module>
</modules>
</profile>
S’activera
automatiquement dès que le jdk1.6 sera détecté par Maven (plus besoin de
faire mvn **:** -Pjdk16).
Les
propriétés natives qui peuvent être testées par l'activation sont les
suivantes:
<activation>
<activeByDefault>false</activeByDefault>
<jdk>1.5</jdk>
<os>
<name>Windows
XP</name>
<family>Windows</family>
<arch>x86</arch>
<version>5.1.2600</version>
</os>
<property>
<name>mavenVersion</name>
<value>2.0.5</value>
</property>
<file>
<exists>file2.properties</exists>
<missing>file1.properties</missing>
</file>
</activation>
Pour
tester l'existence d'une propriété pour l'activation :
<activation>
<property>
<name>!environment.type</name>
</property>
</activation>
Comme
nous l'avons vu dans le précédent chapitre, le fichier setting permet de
centraliser certaines informations qui seront partagées par tous les fichiers
POM pour un même utilisateur (ou pour tous les utilisateurs) d'une machine.
Parmi ces informations les Profils ont une place importante.
En effet,
reprenons l'exemple du profil dev et du profil prod. Il paraît naturel que tous
les projets (du poste de travail) partagent ces profils sans avoir à les
répéter dans chaque fichier POM. Pour cela, il faut les définir dans le fichier
setting.xml. Le problème étant qu’au sein du fichier setting, aucun élément du
build ne peut-être redéfini. Il faut donc n’y mettre que des propriétés :
<setting>
<profiles>
<profile>
<id>dev</id>
<properties>
<environment.type>dev</environment.type>
<compiler.debug>true</compiler.debug>
<compiler.optimize>false</compiler.optimize>
</properties>
</profile>
<profile>
<id>prod</id>
<properties>
<environment.type>dev</environment.type>
<compiler.debug>false</compiler.debug>
<compiler.optimize>true</compiler.optimize>
</properties>
</profile>
</setting>
Ces profils et leurs propriétés sont visibles par tous les projets (de l'utilisateur ou des utilisateurs du poste de travail). Nous utilisons ces propriétés pour spécifier le comportement du build dans chacun des Pom :
<build>
<pluginManagement>
<plugins>
<!--
Plugin de la compilation Java du projet -->
<plugin>
<artifactId>maven-compiler-plugin</artifactId>
<version>2.3.2</version>
<inherited>true</inherited>
<configuration>
<source>${java.source.version}</source>
<target>${java.target.version}</target>
<debug>${compiler.debug}</debug>
<optimize>${compiler.optimize}</optimize>
</configuration>
</plugin>
…
Pour
activer par défaut un profil défini dans le fichier setting, il suffit de
mettre:
<activeProfiles>
<activeProfile>IdDuProfilActif</activeProfile>
</activeProfiles>
Une
seconde manière d'activer un profil par défaut est de le faire dans sa
définition en utilisant la balise activeByDefault:
<settings>
<profiles>
<profile>
<id>dev</id>
<activation>
<activeByDefault>true</activeByDefault>
</activation>
</profile>
</profiles>
</settings>
Ce profil
par défaut définira la variable environment.type à dev. Il sera alors facile
d'utiliser cette variable pour surcharger le profil dans les fichiers POM des
projets (si besoin):
<project>
...
<profiles>
<profile>
<id>development</id>
<activation>
<property>
<name>environment.type</name>
<value>dev</value>
</property>
</activation>
<properties>
<database.driverClassName>com.mysql.jdbc.Driver
</database.driverClassName>
<database.url>
jdbc:mysql://localhost:3306/app_dev
</database.url>
<database.user>
development_user
</database.user>
<database.password>
development_password
</database.password>
</properties>
</profile>
</profiles>
</project>
Dans la même optique que les exemples précédents deux profils typiques apparaissent pour Android : le profil « développement » et le profil « production ». L’un aura pour but de signer (ou pas) votre application avec la clef debug sans optimisation de l’apk et l’autre aura pour but de signer l’application avec votre clef de production en optimisant votre apk.
Quand seulement deux profils de ce type apparaissent, il suffit de déclarer le moins courant des deux en tant que profil (ici nous mettons en place le profil « production »). L’autre profil sera celui du comportement par défaut qui est instancié dans le fichier pom.xml.
<profiles>
<profile>
<id>production</id>
<build>
<plugins>
<plugin>
<groupId>com.jayway.maven.plugins.android.generation2</groupId>
<artifactId>maven-android-plugin</artifactId>
<executions>
<execution>
<id>alignApk</id>
<phase>install</phase>
<goals>
<goal>zipalign</goal>
</goals>
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-jarsigner-plugin</artifactId>
<version>1.2</version>
<executions>
<execution>
<id>signing</id>
<goals>
<goal>sign</goal>
</goals>
<phase>package</phase>
<inherited>true</inherited>
<configuration>
<archiveDirectory></archiveDirectory>
<includes>
<include>target/*.apk</include>
</includes>
<keystore>path/to/keystore</keystore>
<storepass>storepasword</storepass>
<keypass>keypassword</keypass>
<alias>key-alias</alias>
</configuration>
</execution>
</executions>
</plugin>
<plugin>
<groupId>com.jayway.maven.plugins.android.generation2</groupId>
<artifactId>maven-android-plugin</artifactId>
<inherited>true</inherited>
<configuration>
<sign>
<debug>false</debug>
</sign>
</configuration>
</plugin>
</plugins>
</build>
</profile>
</profiles>
Ce profil surcharge le comportement de 3 plugins :
Le plugin android-maven-plugin mappe la tache ZipAlign, qui permet d’optimiser votre application pour les téléphones, avec la phase d’installation du cycle de vie Maven. Cela permet d’automatiser cette optimisation au niveau de la phase de déploiement de l’apk. Cette méthode n’est applicable qu’à partir de la version 2.5 de l’Android-Maven-Plugin.
Le plugin maven-jar-signer est customiser pour signer votre application en utilisant la clef définie dans le KeyStore dont le mot de passe est storepass pour la clef nommée key-alias dont le mot de passe est keypassword. C’est à vous de renseigner les valeurs correctes de ces 4 variables.
Enfin, le plugin maven-android-plugin est customisé de manière à ne pas signer par défaut votre application avec la clef de debug.
Imaginons que vous factorisiez du code au sein d’un projet central qui sera utilisés par d’autres projets. Comment mettre en place les dépendances entre ces projets en utilisant Maven ?
Projet
Core Projet
A Projet
B Utilise
Dans le fichier POM de votre projet core, il vous faut spécifier :
· Le packaging de type APK
· L’inclusion des sources dans votre application finale (cela se configure dans le bloc build/plugin/ maven-android-plugin/configuration).
Pour cela, il vous faut rajouter les lignes en gras suivantes :
<modelVersion>4.0.0</modelVersion>
<groupId>Mon.Group.ID</groupId>
<artifactId>MyArtifactID</artifactId>
<version>MyVersion</version>
<packaging>apk</packaging>
<name>MyApplicationName</name>
…
<plugin>
<groupId>com.jayway.maven.plugins.android.generation2</groupId>
<artifactId>maven-android-plugin</artifactId>
<version>2.8.3</version>
<configuration>
<attachSources>true</attachSources>
<androidManifestFile>${project.basedir}/AndroidManifest.xml</androidManifestFile>
<assetsDirectory>${project.basedir}/assets</assetsDirectory>
<resourceDirectory>${project.basedir}/res</resourceDirectory>
<nativeLibrariesDirectory>${project.basedir}/src/main/native</nativeLibrariesDirectory>
<sdk>
<!-- Defined
in the android.properties file -->
<platform>7</platform>
</sdk>
<deleteConflictingFiles>true</deleteConflictingFiles>
<undeployBeforeDeploy>true</undeployBeforeDeploy>
</configuration>
<extensions>true</extensions>
</plugin>
Ensuite, il vous suffit de rajouter la dépendance vers ce projet dans les fichiers POM du projet A et du projet B dans le bloc dependencies (lignes en gras) :
<dependencies>
<dependency>
<groupId>com.google.android</groupId>
<artifactId>android</artifactId>
<version>2.2.1</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>TheGroupIdOfTheCoreProject</groupId>
<artifactId>TheArtifactIdOfTheCoreProject</artifactId>
<type>apksources</type>
</dependency>
</dependencies>
Cela s’effectue assez simplement, il suffit de rajouter la dépendance dans votre fichier Pom comme toujours avec Maven.
Là où le problème s’envenime est que même si votre application compile, il n’est en aucun cas évident que votre application se lance. En effet, Android possède une JVM légère, Dalvik, qui a été expurgée d’un certain nombre de composants usuels pour le JSE, le JME ou le J2EE (typiquement Swing et AWT). Ainsi, votre Jar peut être dépendant de certains composants Java natifs qui n’existent pas dans la JVM Dalvik d’Android… et là… il n’y a pas de solutions clef en main.
[1] Ce nom est celui utilisé par Android pour définir la plateforme cible. Il apparaît dans le fichier default.properties pour spécifier cette cible.