Android Annotations, des nouvelles du front !


Pour ceux qui ne connaissent pas Android Annotations, je vous laisse checker le googlecode, ou encore la présentation très objective de son auteur.

Le projet est aujourd’hui sponsorisé officiellement par eBusiness, et trois nouveaux contributeurs ont rejoint le projet : Romain Sertelon, Alexandre Thomas, et moi-même. On a déjà apporté certaines évolutions, notamment @EViewGroup et @Rest qui seront disponibles dans la version 2.2, prévue très prochainement ! On a aussi une présentation au PAUG qui pourrait se faire dans la foulée !

Je viens vous parler plus précisément d’une évolution majeure de la structure du framework, qui pourrait arriver dans une version 3, et sur laquelle j’ai passé mes deux derniers “AA Days” ! Ça pourra répondre aux questions que se posent certains en donnant un aperçu de ce qu’on peut bien faire dans cette salle tous les jeudis !

L’objectif ?

L’objectif initial et principal de cette évolution est de faire disparaître le “_” que l’utilisateur est actuellement forcé d’utiliser, par exemple dans son AndroidManifest.xml, ou dans ses Intents :

Un objectif secondaire est de pouvoir se détacher de l’Activity ; la quasi-totalité des fonctionnalités existantes requièrent de se trouver dans une Activity pour fonctionner. Or, les utilisateurs se retrouvent souvent à vouloir externaliser du code et utiliser @Background / @UIThread, @SystemService, @StringRes, etc… dans des classes Java simples. Dans la version actuelle du framework, cela n’est pas permis. Et si on voulait le faire avec le fonctionnement actuel, l’utilisateur devrait utiliser des “_” partout !

Avant de chercher à changer quoi que ce soit, il faut comprendre le principe de fonctionnement actuel. Tout est basé sur de l’Annotation Processing. On repère les Activity annotées @EActivity de l’utilisateur et on en génère des sous-classes qui portent le même nom suivi du underscore dont on veut se débarrasser. Le code de ces sous-classes est généré en fonction des annotations qui sont rencontrées dans la classe de l’utilisateur.

Les solutions ?

On veut que l’utilisateur puisse utiliser directement ses propres classes, et ne pas avoir à penser à ajouter un “_”. La solution la plus triviale est de ne pas créer de sous-classe, et de mettre directement le code généré dans les classes de l’utilisateur. Hem, comment dire…


… Dehors.

Une autre solution, beaucoup plus élégante, est de se tourner vers la toute puissance de l’AOP (Aspect-Oriented Programming) ! Si vous avez déjà utilisé l’AOP made-in Spring, vous aurez probablement fait du tissage dynamique : une sous-classe appelée communément “Proxy”, générée au runtime par réflexion, que Spring vous injecte à la place de vos propres classes, et qui intercepte les appels de méthodes (par simple polymorphisme) pour exécuter du code avant ou après. Finalement, ça ressemble beaucoup à ce qui est fait sur Android Annotations, sauf que c’est fait au compile-time, et que l’utilisateur doit explicitement indiquer qu’il utilise la sous-classe.

Bref, il existe un autre type de tissage, le tissage statique. Le principe, c’est de toucher directement au bytecode. Ça revient grosso modo à retoucher au code de l’utilisateur sans qu’il ne le voit. Pour ça, on utilise AspectJ. Avec AspectJ, on code nos aspects en lui disant quel code on veut attacher à quel endroit, et il s’occupe de tisser le tout avec son compilateur, ajc. Voilà pour le principe.

Passons à la pratique. Prenons un exemple simple, l’annotation @Background, et essayons de la faire fonctionner avec un aspect. Pour rappel, le but de cette annotation est de rendre transparente la création d’un Thread. Elle est dispo depuis la version 0.4 d’Android Annotations.

Dans le cas de cette annotation, on peut avoir un aspect totalement générique, qui serait compilé à l’avance et qu’on fournirait à l’utilisateur pour être tissé avec son propre code :

Ça marche parfaitement, et ça s’utilise de la même manière, mais en mieux ! Mieux car ça fonctionne dans n’importe quelle classe, qu’elle soit annotée avec @EActivity ou non, et sans avoir à gérer des underscores.

C’est donc un premier essai très concluant de migration du framework sur des aspects.

Mais…
Oui, il y a un mais, et c’est pas le dernier. Cet exemple est en fait un des seuls qui se prête bien à l’utilisation des aspects.

Prenons l’exemple de @ViewById. Rappelons le principe :

Dans l’idéal, il faudrait se greffer à la méthode onCreate() pour faire le “findViewById()” depuis l’aspect. Admettons qu’on le fasse, comment connaître les attributs concernés depuis cet aspect ? Il faut parcourir les attributs de la classe de l’utilisateur pour savoir quels sont ceux annotés avec @ViewById. On appelle ça de la réflexion, et il faut savoir que Dalvik, la JVM utilisée par Android, a beaucoup de mal avec la réflexion. Il est conseillé de l’utiliser le moins possible. C’est d’ailleurs la force d’AndroidAnnotations comparé à d’autres frameworks comme RoboGuice : il utilise la réflexion au compile-time uniquement, pour que les classes générées sachent déjà quels attributs sont à injecter, quelles méthodes sont à appeler, etc… ce qui n’a donc aucune conséquence sur les perfs au runtime.

Vous l’aurez compris, on ne peut pas se permettre d’utiliser la réflexion à tout va pour injecter les attributs au runtime à partir de l’Aspect. Il s’avère que pour @ViewById, on peut néanmoins ruser. On peut supposer que l’utilisateur n’essaiera pas d’accéder à l’attribut tant que la vue n’est pas créée. A partir de là, on peut se permettre d’injecter la valeur au moment où il accède à cette valeur.

Cet aspect est juste un POC, il fonctionne mais fait un “findViewById()” à chaque accès à l’attribut, il est donc grandement améliorable. Bon point pour lui, l’aspect ne semble pas utiliser la réflexion, il récupère l’annotation / l’activity / etc.. grâce à ce qu’on appelle le context-binding. Mais il fait très certainement une certaine part de réflexion au runtime. On lui accorde le bénéfice du doute, il faudrait décompiler le code tissé pour en avoir le cœur net.

Ce qui est sûr, c’est que cette histoire commence à sentir mauvais !

Je décide quand même de continuer, mais un dernier problème vient m’achever : @Click. Oui, @Click, qui est un des use case les plus courants, n’est simplement pas réalisable avec un aspect générique comme on l’a fait jusque là.

Pourquoi ? Prenons cet exemple simpliste qui fonctionne très bien avec Android Annotations.

Quelque part après l’appel à setContentView(), il faut pouvoir récupérer le TextView avec un findViewById(), et y ajouter un OnClickListener. Mais où accrocher notre aspect ? Comment définir les JoinPoints ? L’Activity ne présente aucune méthode ! Si l’utilisateur avait redéfini la méthode onCreate(), on pourrait définir un JoinPoint dessus, mais il ne l’a pas fait, et on ne veut pas le forcer à le faire.

Cette méthode onCreate() est pourtant bien appelée, oui, mais elle est appelée par le code d’Android, sur la classe Activity qui est également dans le code d’Android. On ne peut pas définir un JoinPoint dans ce code, puisqu’il ne sera à aucun moment tissé avec nos aspects.

La seule solution, c’est d’ajouter à la compilation la méthode onCreate() à la classe de l’utilisateur à travers l’aspect :

On pourrait ensuite utiliser cette même méthode comme JoinPoint pour y exécuter du code. C’est possible, ça s’appelle une Inter-type declaration. Mais (toujours ce mais…) pour ça il faut connaître le nom de la classe à laquelle on ajoute la méthode, au moment où on écrit l’Aspect. C’est une restriction d’AspectJ. Ce n’est donc même pas réalisable par réflexion, qu’on utiliserait de toute façon pas. Dead end.

Conclusion : on ne peut pas transformer Android Annotations en une librairie d’aspects.

On a alors deux solutions :

  • rester sur de l’Annotation Processing, et continuer d’être embêté par ce underscore.
  • utiliser l’Annotation Processing pour générer des aspects plutôt que de générer des sous-classes !

Il est encore trop tôt pour se prononcer sur cette 2ème solution. On sait que c’est possible, c’est ce que fait Spring-Roo. Il y a encore quelques sujets à creuser pour pouvoir prendre une décision, notamment en terme de facilité d’installation dans l’environnement de développement.

Personnellement, je suis très optimiste sur l’avenir d’un Android Annotation basé sur de la génération d’Aspects, et je vais m’y consacrer, en espérant avoir un POC fonctionnel rapidement.

Stay tuned !

VN:R_U [1.9.22_1171]
Rating: 0 (from 0 votes)
Share
Ce contenu a été publié dans Android, Non classé, Outils, Trucs & astuces, avec comme mot(s)-clef(s) , , , . Vous pouvez le mettre en favoris avec ce permalien.

2 réponses à Android Annotations, des nouvelles du front !

  1. Ping : Revue de l’actu Android pro du 13 novembre 2011 | Paris Android User Group

  2. Ping : Android Annotations - I Love Android

Laisser un commentaire