DroidCon Paris 2014
En Septembre dernier se tenait la seconde édition de DroidCon Paris, une conférence sur Android organisée par le Paris Android User Group et BeMyApp. Le planning était particulièrement dense, avec un total de 47 interventions en 2 jours à peine (les vidéos sont disponibles à cette adresse). Globalement, ces présentations étaient très intéressantes et bourrées d’informations techniques pertinentes.
Nous vous proposons de revenir sur une sélection de 3 des talks que nous avons trouvé les plus intéressants. Nous vous recommandons chaudement de regarder les vidéos ou les slides associées, mais ces résumés devraient vous permettre d’avoir une idée globale de leur contenu.
The Death of the Refresh Button, by Mathieu Calba
Commençons par une présentation orientée vers le confort de l’utilisateur. L’idée principale est de proposer des solutions pour limiter le besoin d’une connection internet pendant que l’utilisateur parcourt l’application.
En synchronisant les données en arrière-plan (même quand l’application n’est pas active), il est possible de proposer à l’utilisateur l’ensemble des données nécessaires dès qu’il lance l’application. Évidemment, ce genre de comportement ne s’applique pas à toutes les applications : par exemple, si votre application contient des actualités, peut-être que vous préférerez fournir les données les plus récentes possibles et faire une requête réseau au lancement. Mais pour Capitaine Train (où le speaker travaille), cela permet à l’utilisateur d’avoir des informations sur les trajets qu’il a réservé sans avoir besoin d’un accès au réseau au moment de la consultation.
La meilleure solution disponible est d’utiliser du push pour notifier l’application d’une mise à jour des données. Si le contenu est léger (par exemple, un retard de train), il peut être inclus dans le payload du push. Sinon, le push peut se contenter de notifier l’application qu’un nouveau contenu est disponible, et l’application fera elle-même la requête.
Quand le push n’est pas une solution envisageable, il faut revenir à des requêtes périodiques. La première difficulté est de savoir à quel moment faire une requête, selon la probabilité qu’une mise à jour soit disponible, selon l’état de la connexion, selon le niveau de batterie, … Un autre problème est de trouver une solution qui survivra un redémarrage de l’appareil, donc un simple service ne suffira pas.
La première solution est de se servir de l’AlarmManager pour réveiller l’application. Le problème est que toutes les alarmes sont effacées au redémarrage, donc il faut que l’application soit notifiée quand l’appareil démarre pour relancer les alarmes (ce qui nécessite la permission RECEIVE_BOOT_COMPLETED
).
Une autre solution est de mettre en place un SyncAdapter. C’est la solution que privilégiait Google, avant la sortie de Lollipop. Attention : l’utilisateur peut demander l’arrêt de toutes les synchronisations automatiques (par exemple, à l’aide du widget Power Control). Cette solution nécessite aussi d’écrire une quantité significative de code pour les différentes pièces requises (SyncAdapter
, AccountAuthenticator
, ContentProvider
, …)
La dernière solution est aussi la plus récente : JobScheduler
. Actuellement, il n’est disponible que dans le SDK Android L preview. Le JobScheduler
repose sur un service système (qui est donc redémarré en même temps que l’appareil, en ayant conservé son état) et met à disposition de nombreuses options de configuration pour le déclenchement du réveil de l’application (niveau de batterie, état de la connexion réseau, périodicité, …). Cette solution permet d’écrire un code beaucoup plus simple que pour un SyncAdapter
, et elle permet aussi une plus grande durée de vie pour la batterie vu qu’il n’y a plus qu’un seul service qui tourne et déclenche le réveil de toutes les applications. Par contre, la vraie limitation du JobScheduler
est qu’il ne sera pas disponible sur les appereils dont l’OS est plus vieux que Lollipop.
Vous pouvez trouver la vidéo ici et les diapositives ici
Deep Dive into Android State Restoration, by Cyril Mottier
Sur Android, une partie significative des informations de l’application sont transientes et peuvent disparaître : par exemple sur un changement de configuration (orientation du téléphone, clavier), le comportement par défaut est que votre activité est redémarrée. L’exemple donné pendant la présentation était un formulaire dont les champs s’effacent à la rotation de l’appareil. Il est aussi possible de perdre l’état d’une activité quand vous naviguez sur d’autres ou que l’application passe en arrière-plan. Dans ces cas là, il faut demander au système de persister les données qui vous intéressent.
Le conteneur dans lequel vous allez vouloir stocker cette information est un objet Parcelable
. Créer son propre objet Parcelable
nécessite d’écrire une quantité significative de code répétitif, qu’il faut tenir à jour (ou vous pouvez le générer automatiquement), ou alors, vous pouvez utiliser un Bundle
, qui est un stockage clé-valeur Parcelable
. Certaines APIs Android vous donneront directement un Bundle
au lieu d’un Parcelable
.
Vous pouvez sauvegarder l’état de votre application à 3 endroits. Le plus évident est : via les activités. Quand votre activité est sur le point d’être arrêtée, la méthode onSaveInstanceState(Bundle)
de l’activité est appelée. Elle fournit un Bundle
non-null dans lequel vous pouvez ajouter les informations que vous souhaitez persister. Le comportement par défaut de cette méthode est de stocker dans ce Bundle
la Window
, les Fragments
et les Dialogs
. Ce Bundle" vous sera retourné dans les méthodes @onCreate
et onRestoreInstanceState
de l’activité.
Vous pouvez aussi sauvegarder une partie de l’état de l’application dans les vues associées. Quand votre activité est détruite et que onSaveInstanceState
est appelé, le système commence par appeler saveHierarchyState
sur la fenêtre associée. En plus de persister l’état de l’ Action Bar
, des Panels
et l’id de la vue qui a le focus, saveHierarchyState
appelle saveHierarchyState
sur la vue de contenu actuelle (id: android.R.id.content
). Les vues auront leur état persisté automatiquement par le système si : elles ont un ID unique (pour cette hiérarchie de vues), sont des vues natives Android et sont autorisées à la sauvegarde (oui par défaut). L’ID unique est nécessaire car les données de chaque vue seront stockées dans un SparseArray<Parcelable>
, indexé par les IDs. Toutes les vues natives Android implémentent les méthodes onSaveInstanceState
et onRestoreInstanceState
, mais si vous créez votre propre vue custom, vous devrez les implémenter vous-même. Enfin, vous pouvez décider de quelles vues auront leur état sauvegardé via les méthodes setSaveEnable(boolean)
et setSaveFromParentEnabled(boolean)
methods.
Enfin, vous pouvez aussi persister l’état de vos fragments. Le comportement est très proche de celui des activités. La subtilité est que les fragments attachés contiennent une vue dans la hiérarchie mais ces vues attachées à un fragment doivent être persistées avec le fragment, et non avec la hiérarchie de vues du paragraphe précédent.
La vidéo est disponible ici et les slides sont sur speakerdeck
Robotium vs Espresso, by Thomas Guerin
L’objectif de cette présentation est de comparer deux systèmes de tests fonctionnels : Robotium (inspiré par Selenium) et Espresso (développé par Google). Les tests unitaires sont indépendants (et doivent être traités à part): ces systèmes se concentrent sur les vues et comment interagir avec automatiquement.
L’API de Robotium, sur le modèle de Selenium, repose sur l’utilisation d’un objet Solo
, que vous attachez à votre activité. Cet objet fournit toutes les méthodes nécessaires pour interagir avec votre application (clics, scrolls, swipes, …). Vous écrivez ensuite vos assertions pour tester que votre activité se comporte comme prévu. L’intervenant recommandait l’utilisation de Hamcrest, qui fournit un ensemble d’objets Matcher
, qui servent à simplifier l’écriture de vos assertions.
L’API d’Espresso repose sur 3 concepts: ViewMatchers
, ViewActions
et ViewAssertions
. Vous utilisez un ViewMatcher
pour trouver une vue, et une ViewAction
pour interagir avec (là où Robotium utilise Solo
pour les deux), puis des ViewAssertions
pour vos tests en tant que tel. Les ListViews
sont traitées d’une manière différente : au lieu d’identifier les vues par leurs propriétés (par exemple, le texte qu’elles affichent), vous devez l’identifier par les données qui sont attachées à votre cellule.
Avec Robotium, il est possible d’interagir avec les WebViews
, en utilisant les méthodes de Solo
correspondantes, qui permettent de naviguer le DOM. Malheureusement, Espresso est toujours en développement et les WebViews
ne peuvent actuellement pas être testées directement.
Si aucune vue ne correspond aux critères demandés, Espresso (contrairement à Robotium) log la hiérarchie de vues (et les paramètres de chaque vue), ce qui est d’une grande aide pour comprendre pourquoi les tests échouent.
Une différence significative entre les deux systèmes est dans leur manière d’enchaîner les actions. Robotium nécessite des instructions spécifiques pour attendre le rendu d’une vue ou d’une activité. À l’inverse, Espresso se sert du GoogleInstrumentationTestRunner
: il surveille le thread d’UI et remarque quand il devient inactif, ce qui déclenche la suite du test. Cela est suffisant la plupart du temps, mais parfois vous devez attendre une opération asynchrone (comme une requête réseau). Dans ce cas là, vous pouvez fournir à Espresso une IdlingResources
, dont il attendra que le statut renvoyé par la méthode isIdleNow
renvoie true
.
En conclusion, l’intervenant recommande d’utiliser Espresso, et de le complémenter avec Robotium en cas de WebViews
à tester (tant qu’Espresso ne le fera pas directement). Cependant, attention : le développement d’Espresso n’est pas fini et il peut toujours contenir des bugs qui peuvent être bloquants.
Vous pouvez trouver la vidéo ici et les diapos ici
Conclusion
Ceci représentait juste une faible portion de l’ensemble des présentations de ces deux jours. Encore une fois, nous ne pouvons que vous recommander de regarder vous-même l’ensemble du contenu.
Au final, Droidcon aura été une expérience très positive, avec beaucoup de contenu technique, qui devrait nous occuper quelques temps. Nous y retournerons définitivement l’année prochaine.