devfest-marseille-2014



devfest-marseille-2014

0 0


devfest-marseille-2014


On Github tguerin / devfest-marseille-2014

Une usine logicielle sur mesure pour Android

Usine logicielle

Une usine logicielle est un ensemble d’outils et de pratiques permettant d’automatiser les différentes étapes du développement d’un logiciel

Intégration continue

L'intégration continue est un ensemble de pratiques consistant à vérifier à chaque modification de code source que le résultat des modifications ne produit pas de régression dans l'application

Déploiement continu

Le déploiement continu, prolongement de l'intégration continue, est une pratique visant à réduire le plus possible le temps écoulé entre l'écriture d'une nouvelle ligne de code et l'utilisation réelle de ce même code par des utilisateurs finaux.

Plan

  • Bonnes pratiques
  • IDE
  • Build
  • Tests
  • Intégration continue
  • Métriques
  • Déploiement continu

Bonnes pratiques

Bonnes pratiques

  • Etablir des conventions de codage
  • Feature branching (spécifique Git)
  • Master/trunk toujours stable
  • Petits commits
  • Pair programming
  • Code review

IDE

Les qualités d'un bon IDE

  • Productif
  • Rapide

Quel IDE choisir?

Eclipse

  • Premier IDE officiel d'Android
  • Des défauts gênants
    • Des lenteurs récurrentes
    • Mauvais support des systèmes de build

Quel IDE choisir?

Android Studio

  • Annoncé lors du Google I/O 2013
  • Utilise IntelliJ comme base
  • Propose des fonctionnalités intéressantes :
    • Amélioration de l'interface de design
    • Meilleur auto-complétion

Build

Quel systeme de build?

  • Configuration -> xml
  • Plugin créé par la communauté
  • Bonne intégration dans les IDE

Quel systeme de build?

  • Configuration -> scripting groovy
  • Nouveau système de build “officiel” pour Android
  • Très vite adopté
buildscript {
    repositories {
        mavenCentral()
    }
    dependencies {
        classpath 'com.android.tools.build:gradle:0.9.+'
    }
}

apply plugin: 'android'

android {
    compileSdkVersion 19
    buildToolsVersion "19.0.3"

    compileOptions {
        sourceCompatibility JavaVersion.VERSION_1_7
        targetCompatibility JavaVersion.VERSION_1_7
    }

    buildTypes {
        release {
            runProguard false
        }
    }
}

dependencies {
    ..
}
<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/xsd/maven-4.0.0.xsd">
    <modelversion>4.0.0</modelversion>
    <groupid>fr.devfest.marseille</groupid>
    <artifactid>Devfest</artifactid>
    <version>1.0.0.0</version>
    <packaging>apk</packaging>

    <properties>
        <project.build.sourceencoding>UTF-8</project.build.sourceencoding>
    </properties>

    <dependencies>
        ...
    </dependencies>

    <build>
        <finalname>${project.artifactId}-${project.version}</finalname>
        <sourcedirectory>src</sourcedirectory>
        <testsourcedirectory>test</testsourcedirectory>

        <plugins>

            <plugin>
                <groupid>com.jayway.maven.plugins.android.generation2</groupid>
                <artifactid>android-maven-plugin</artifactid>
                <version>3.6.0</version>
                <extensions>true</extensions>

                <configuration>
                    <!-- <sdk> <platform>16</platform> </sdk> -->

                    <sdk>
                        <path>${env.ANDROID_HOME}</path>
                        <!-- <platform>2.2</platform> -->
                        <platform>17</platform>
                    </sdk>


                    <dex>
                        <jvmarguments>
                            <argument>-Xms512m</argument>
                            <argument>-Xmx1024m</argument>
                        </jvmarguments>
                    </dex>

                </configuration>
                <executions>
                    <!-- <execution> <id>startEmulator</id> <phase>initialize</phase> <goals>
                        <goal>emulator-start</goal> </goals> </execution> -->
                    <execution>
                        <id>alignApk</id>
                        <phase>package</phase>
                        <goals>
                            <goal>zipalign</goal>
                        </goals>
                    </execution>
                </executions>
            </plugin>
            <plugin>
                <artifactid>maven-compiler-plugin</artifactid>
                <configuration>
                    <source>1.7
                    <target>1.7</target>
                </configuration>
            </plugin>

            <plugin>
                <groupid>org.apache.maven.plugins</groupid>
                <artifactid>maven-jarsigner-plugin</artifactid>
                <executions>
                    <execution>
                        <id>signing</id>
                        <goals>
                            <goal>sign</goal>
                            <goal>verify</goal>
                        </goals>
                        <phase>package</phase>
                        <inherited>true</inherited>
                        <configuration>
                            <removeexistingsignatures>true</removeexistingsignatures>
                            <archivedirectory>
                            <includes>
                                <include>${project.build.directory}/${project.artifactId}-${project.version}.apk</include>
                            </includes>
                            ...
                        </archivedirectory></configuration>
                    </execution>
                </executions>
            </plugin>
            <!-- the signed apk then needs to be zipaligned -->
            <plugin>
                <groupid>com.jayway.maven.plugins.android.generation2</groupid>
                <artifactid>maven-android-plugin</artifactid>

                <inherited>true</inherited>
                <configuration>
                    <sign>
                        <debug>false</debug>
                    </sign>
                    <zipalign>
                        <verbose>true</verbose>
                        <inputapk>${project.build.directory}/${project.artifactId}-${project.version}.apk</inputapk>
                        <outputapk>${project.build.directory}/${project.artifactId}-${project.version}-signed-aligned.apk
                        </outputapk>
                    </zipalign>
                    <skip>false</skip>
                </configuration>
                <executions>
                    <execution>
                        <id>alignApk</id>
                        <phase>package</phase>
                        <goals>
                            <goal>zipalign</goal>
                        </goals>
                    </execution>
                </executions>
            </plugin>

        </plugins>


    </build>

    <profiles>
        <profile>
            <id>release</id>
            <properties>
                <android.debuggable>false</android.debuggable>
                <android.release>true</android.release>
            </properties>
        </profile>
    </profiles>
</project>

Gradle tips

  • buildTypes
  • productFlavors
  • buildConfigField

Tests

Tests unitaires

JUnit ou Robolectric ?

  • JUnit :
    • Difficile de mocker les composants du sdk
    • Utile pour tester la logique du modèle métier
  • Robolectric :
    • Plus de “java.lang.RuntimeException: Stub!”
    • Exécution des tests directement dans la JVM
    • Quelques limitations (gridlayout, menudrawer...)

Tests fonctionnels

  • Framework d’instrumentation :
    • Intégrer nativement au sein de la plate-forme
    • Permet d’intéragir avec les composants d’une application
    • Faible niveau d’abstraction

Tests fonctionnels

  • Robotium ou Espresso :
    • Surcouche du framework d’instrumentation
    • Meilleur niveau d’abstraction
    • Écriture des tests simplifiés
  • Espresso != Robotium ...
  • ... mais parfois complémentaires

Monkey

  • Très pratique pour les stress tests
  • Simple à lancer
  • Difficilement automatisable
adb shell monkey -p your.package.name -v 500

Intégration continue

Serveur d'intégration continue

  • Utilisation de Jenkins CI
  • L’unité est le job
  • Nombreux plugins disponibles
  • 3 jobs essentiels :
    • Build du projet et exécution des tests unitaires
    • Exécution des tests fonctionnels
    • Déploiement du livrable

Métriques

Sonar

  • Suivi des principales métriques du code (couverture, duplication...)
  • Adapter le profil qualité

spoon

  • Exécution distribuée des tests fonctionnels
  • Génération de rapports d’exécution clairs
  • Format HTML

Déploiement continu

Déploiement continu

  • Build, tests, analyse et .... déploiement
    • A chaque instant, mettre à disposition ses applications aux utilisateurs tests
    • Récupération des retours utilisateurs au plus tôt
  • Utilisation d'un Mobile Application Management Software (HockeyApp, Knappsack, ...)
  • Déploiement via un plugin jenkins

MAM, quel intérêt?

  • Plus de fonctionnalités que la console play
  • Manager plusieurs builds
  • Gestion fine des groupes d'utilisateurs
  • Remonté de crashs
  • Feedbacks

Et après ?

Et après ?

  • Suivi des crashs (console play, Crashlytics, ...)
  • Connaître le comportement de ses utilisateurs (analytics, flurry, ...)
  • Progressive rollout

Conclusion