image/svg+xml $ $ ing$ ing$ ces$ ces$ Res Res ea ea Res->ea ou ou Res->ou r r ea->r ch ch ea->ch r->ces$ r->ch ch->$ ch->ing$ T T T->ea ou->r

Une API en constante évolution

Ajout de nouvelles fonctionnalités :

Modification d'usage de fonctionnalités existantes :

Les anciennes méthodes et/ou classes sont notées deprecated dans l'API.

Compatibilité du code avec des versions plus récentes de l'API

Normalement du code écrit pour une version N de l'API doit fonctionner sur un appareil doté d'une API de version > N sans modification (compatibilité ascendante).
Les méthodes et classes dépréciées restent utilisables.

Le comportement de certaines fonctionnalités peut évoluer au cours des versions. Par exemple certains thèmes par défaut peuvent être introduits par de nouvelles versions de l'API : l'application de ces thèmes sur des applications conçues pour des versions plus anciennes de l'API pourrait poser certains problèmes. A l'exécution, la propriété targetSdkVersion indiquée dans le manifeste est consultée : si celle-ci est inférieure à la version actuelle de l'API, certains comportements de rétro-compatibilité peuvent être adaptés (comme la non-adoption de nouveaux thèmes graphiques).

⚠ Il est important d'indiquer comme targetSdkVersion la version la plus élevée sur laquelle l'application a été testée (et pas la version minimale supportée).

Il est possible d'interdire l'installation de l'application sur un appareil utilisant une version de l'API trop élevée avec la propriété maxSdkVersion pour indiquer la version maximale de l'API supportée. L'utilisation de cette propriété est plutôt à éviter dans le cas général car il est souhaitable d'assurer une compatibilité avec les futures versions de l'API. Cela pourrait néanmoins être utile si l'on compile différentes versions d'une application, chacune destinée à des versions spécifiques (utilisation de saveurs de version).

Compatibilité du code avec des versions plus anciennes de l'API

Du code écrit pour une version N de l'API ne fonctionnera sur un appareil doté d'une version < N de l'API si on utilise des classes ou méthodes nouvellement introduites.

Comment utiliser de nouvelles fonctionnalités de l'API tout en garantissant une compatibilité descendante ?

Comment gérer l'absence d'une fonctionnalité sur une version inférieure ?

On indique la version minimale de l'API supportée avec la propriété minSdkVersion dans le fichier build.gradle pour la génération du manifeste final. Un IDE ou le programme de vérification lint signale les éventuels appels à des fonctionnalités non supportées par la minSdkVersion ; les laisser expose à des exceptions lors de l'exécution sur des appareils de niveau d'API trop faible.

Une application ne peut être installée sur un appareil utilisant une API inférieure au minSdkVersion déclaré.

Rétro-portage de pans récents de l'API

Il existe des bibliothèques rétroportant des fonctionnalités nouvelles de l'API sur des appareils plus anciens.

L'utilisation des bibliothèques de rétro-portage est plus ou moins transparente avec un IDE Android (en fonction du niveau minimum d'API supporté indiqué). Les bibliothèques sont automatiquement embarquées.

Les bibliothèques officielles rétroportées sont regroupées dans le projet AndroidX (paquetage androidx).

Test du niveau d'API

Le niveau d'API actuel de l'appareil peut être obtenu avec le champ statique Build.VERSION.SDK_INT. On peut alors choisir de n'exécuter certaines portions de code que si le niveau actuel d'API dépasse une certaine valeur (et implanter éventuellement une solution de repli si ce n'est pas le cas).

Si l'on teste correctement le niveau d'API avant de réaliser les appels nécessitant une API supérieure au minSdkVersion, les avertissements de lint deviennent inutiles : on peut alors les supprimer avec l'annotation @TargetAPI(apiLevel) (en remplaçant apiLevel par le niveau maximum d'API géré par la méthode).

Introspection

L'introspection permet de vérifier dynamiquement à l'exécution si certaines classes ou méthodes sont disponibles. On peut charger dynamiquement une classe avec la méthode statique Class.forName(String className) qui lèvera une exception si la classe n'est pas disponible. On peut ensuite instantier la classe ou récupérer une instance singleton, appeler Method getMethod(String name) ou Field getField(String name) pour obtenir une méthode ou un champ... Il faut toujours prévoir la possibilité de l'échec de l'introspection sur des niveaux inférieurs de l'API en capturant correctement les exceptions levées.

L'introspection est assez flexible mais présente l'inconvénient de devoir écrire un code assez verbeux et peu élégant. Son usage doit donc être parcimonieux.

Design du code pour la gestion de différents niveaux d'API

Si l'on est amené à appeler des fonctionnalités sur des versions d'API supérieures au minSdkVersion par test du niveau d'API ou introspection, il est recommandé de créer des classes spécifiques dédiées à ce travail afin de bien séparer cette problématique du reste du code. Idéalement on pourra écrire une bibliothèque distincte du module principal du projet.