Accueil / Articles PiApplications. / La plate-forme Android

Points particuliers utiles au développement d'applications Android.

La plate-forme Android nécessite une connaissance relativement étendue en raison des nombreux concepts manipulés. Nous recensons ici quelques points dont la connaissance devrait faciliter la réalisation des applications Android.

La gestion des traces.

La mise au point d'un programme s'exécutant sur une machine distante (ici sur un périphérique Android) est toujours compliquée. Cela l'est d'autant plus avec Android, que les moyens habituels comme l'infrastructure Log4J ou l'emploi de fichiers exploitables à posteriori ne sont pas vraiment adaptés. Fort heureusement, Android fournit son propre système de traces (classe android.util.Log). La classe de journalisation contient de nombreuses méthodes pour émettre des traces. Si vous utilisez Android Studio ces traces apparaissent dans l'onglet "Android Monitor" et le sous-onglet "Logcat". La possibilité d'ajouter une étiquette à chaque trace en facilite le filtrage.

Attention aux traces sur plusieurs lignes (présence de \n). L'outil de présentation des traces d'Android Studio les tronques au premier double \n rencontré.

Les adaptateurs de données.

Pour faire le lien entre une liste de données et leur présentation, Android partage le même concept que JavaFX : les adaptateurs. Un adaptateur (classe Adapter) est un gestionnaire de collection qui retourne chaque objet de cette dernière sous la forme d'une "vue " (classe View). Cette vue peut elle-même dépendre de la classe stockée par la collection. Bien qu'il existe de nombreux adaptateurs, vous pouvez créer les vôtres. Chaque vue utilisée par l'adaptateur peut également être définie via un descripteur XML (layout). L'adaptateur permet d'ajouter à un conteneur (objet de classe Spinner par exemple) une vue adaptée à chaque classe d'objet contenue par la collection.

Les menus.

Android ne supporte qu'une seule forme de menu. Ce dernier est invoqué par un bouton standard du périphérique (bouton en général situé en bas et à gauche de ce périphérique). L'accès à ce menu (standard Android) se fait grâce à un gestionnaire d'évènement de l'activité (Activity.onCreateOptionsMenu). Cette méthode permet de fixer le libellé du choix de menu, son icône et son "identifiant". La capture des évènements de menu se fait via un autre gestionnaire standard de l'activité : Activity.onMenuItemSelected. Le choix de menu peut alors être facilement identifié grâce à son identifiant.

Stockage de données globales à l'application.

Comme l'objet dérivé de la classe Application reste en mémoire tant que l'application est "vivante", on peut s'en servir pour stocker des données d'état globales à cette application. Dans cette optique, le plus simple est de créer une nouvelle classe dérivée de la classe Application et de lui ajouter des méthodes de gestion des données stockées. Il faut alors déclarer cette classe dans le manifeste de l'application. Cela se fait via l'attribut "android:name" de l'élément <application>. Ce mode permet d'échanger aisément des données entre activités et permet à l'application de servir de modèle global. Pour retrouver les données, la classe Activity dispose de la méthode getApplicationdont le résultat doit alors être transtypé vers la classe dérivée de la classe Application.

L'emploi d'une classe dérivée ne convient pas pour stocker des données qui ont besoin d'être persistantes lorsque l'application ne s'exécute pas. On peut alors utiliser des attributs ou des méthodes statiques, des "intentions" transportant des objets de classe Bundle, des données stockées dans une base SQLite ou votre propre pourvoyeur de données hérité de la classe ContentProvider.

Modèle de conception "Builder".

De nombreuses classe Android mettent en oeuvre le modèle de conception "Constructeur" (Builder pattern). Dans ce modèle, les méthodes retournent this de façon à pouvoir chaîner les méthodes les unes aux autres : AlertDialog.Builder(this).setTitle(R.string.alert_label).setMessage(R.string.location_not_supplied_message) .setPositiveButton(...). Ce modèle est fréquemment utilisé sur cette plate-forme. Il évite des constructeurs avec trop de paramètres ou la répétition de la classe de référence pour invoquer chaque méthode une à une.

Cycle de vie d'une application.

Le cycle de vie des processus Android est particulier. Chaque processus est placé sur une pile. Dès qu'une activité est invoquée, son processus passe sur la haut de la pile afin d'être exécuté tandis que le processus précédent est placé immédiatement au-dessous. Ce fonctionnement permet à Android de conserver le plus longtemps possible le processus actif tout en limitant la consommation de ressources en interdisant aux autre processus de l'être. Lorsqu'Android est à cours de ressources, il doit supprimer des processus. Le choix s'effectue de la manière suivante :

  1. le processus sur le haut de la pile est le plus important ;
  2. les processus suivant dans l'ordre d'importance sont ceux qui manipulent des objets graphiques ;
  3. viennent ensuite ceux qui s'exécutent en arrière-plan ;
  4. les processus ne manipulant aucune activité sont au plus bas de l'ordre d'importance.

L'outil adb (commande adb shell dumpsys activity) permet de lister l'importance des processus en cours d'exécution.

Comme Android peut stopper un processus a tout moment, les activités doivent être en mesure d'être interrompue ou arrêtée n'importe quand. C'est pour cela que toute activité dispose d'un jeu de gestionnaires d'évènement qui peuvent être directement invoqués par la plate-forme. Une méthode en particulier doit toujours être surchargée : onCreate car elle est le point de début d'exécution de l'activité (et donc parmi elles de l'application). Il est également recommandé de surcharger la méthode onPause invoquée par la plate-forme dès que le processus est empilé au profit d'un autre. Les divers évènements sont :

Il existe également les gestionnaires onPostCreate et onPostResume mais en principe, on ne surcharge pas ces méthodes.

Il faut également garder à l'esprit que l'activité peut être immédiatement supprimée après l'invocation de onPauseen cas de faiblesse générale des ressources et que les autres gestionnaires ne le sont pas nécessairement dans ce cas.

Au-delà de la sauvegarde de l'état des données via le gestionnaire onPause, il est également possible de sauvegarder l'état de l'instance elle-même de l'activité. Cela se fait via le gestionnaire onSaveInstanceState. En général, il n'est pas nécessaire de surcharger cette méthode. Toutefois, il est important de savoir que l'objet de classe Bundle qu'il sauvegarde est celui récupéré par le gestionnaire onCreate ultérieurement lorsque l'activité est restaurée. Il est possible d'intervenir ici pour modifier la vue avant la restauration.

Tout écran Android est constituée d'une arborescence de "vues" (contrôles héritant de la classe View). Les vues peuvent être créées et manipulées par programmation ou via un descripteur XML.

Une cryptographie comme Java mais....

Le premier constat est de s'apercevoir qu'Android, contrairement à Java, n'utilise pas l'alias de l'entrée dans le magasin pour retrouver son contenu mais le code SHA1 de l'entrée souhaitée (chaîne avec les octets exprimés en hexadécimal utilisant des minuscules). Si par exemple, vous fabriquez un magasin PKCS12 avec OpenSSL pour stocker un certificat et sa clef privée, l'alias du certificat sera "1" par défaut. Pour Java vous devez transmettre cet alias "1" pour référencer le certificat mais pour Android, vous devrez fournir en guise d'alias son code SHA1 (9716bcc5b70e409417829e5cf3d9f16ce84e017e par exemple). N'ayant que peu ou pas de documentation sur ce genre "d'astuce", cela nous a pris quelques temps pour le comprendre.

Ceci ne fut pas notre seule "mauvaise" surprise. Si d'aventure, vous utilisez sous Java la classe javax.crypto.Cipher et un certificat X.509 pour chiffrer de façon asymétrique un tampon d'octets puis que vous transmettiez ce tampon à une application Android et utilisez la même classe pour le déchiffrer avec la clef privée correspondante au certificat de chiffrement, vous obtiendrez une exception. Pourtant sur Java comme sur Android, vous avez utilisé le même magasin PKCS12 (certificat et clef privée), la même classe et les algorithmes duals. Où est le problème ?

Là encore, il faut observer attentivement les classes concrètes manipulées par Android. On se rend compte alors que le fournisseur par défaut n'est pas le même. Sur la plate-forme Java, le fournisseur par défaut est "Sun" (JCE) alors que sur Android, c'est le projet libre Bouncy Castle.

Si vous chiffrez des flux sous Java pour qu'ils soient ensuite déchiffrés sous Android (ou vice-versa), vous devez utilisez le fournisseur Bouncy Castle. Ce fournisseur est identifié grâce au bigramme "BC". Contrairement à Android, il faut indiquer à la plate-forme Java que vous allez utiliser ce fournisseur (avant toute autre opération). Voici un exemple de code qui le réalise :

if (Security.getProvider("BC") == null)
  Security.addProvider(new BouncyCastleProvider());
String sAlgorithm = "RSA/ECB/PKCS5Padding";
Cipher cph = Cipher.getInstance(sAlgorithm, "BC");

Simple mais encore faut-il le savoir !

Enfin, la plate-forme Android a un avantage sur la plate-forme Java : elle autorise le chiffrement fort (256 bit et au-delà) sans être obligé d'installer des fichiers de stratégie spécifiques pour y parvenir.

(c) PiApplications 2015