Le 02/01/18

Code natif et développement multi-plateformes mobile [2/2] Retour d'expérience

Après vous avoir présenté les différentes solutions existantes pour la création d'applications mobiles multi-plateformes dans un précédent article. Nous vous proposons dans cette article un retour d'expérience, de certaines solutions pour créer des applications multi-plateformes, appliqué à une problématique technique concrète.

Dans le cadre d'une étude technique récente nous nous sommes demandé comment exécuter du code natif dans le cadre d'une application mobile cross-plateforme.

Qu'entend-t-on par "code natif" ?

Par code natif on entend : un code très souvent développé dans un langage bas niveau (proche de la machine) tel que le C++. Ce type de code est employé principalement pour répondre à des contraintes de performance ou d'accès à des composants spécifiques tels que la mémoire ou la carte graphique. Bien que l'utilisation de code natif soit fréquente lors d'un développement iOS avec le langage Objective-C, cette tendance est peu commune sur Android avec le langage Java.

L'introduction récente par Apple du langage Swift et l'utilisation de Java sur Android tend à limiter l'utilisation de code natif afin de simplifier le développement en déléguant à la machine les problématiques de gestion de la mémoire.

En quoi exécuter du code natif dans une application mobile multi-plateforme est une problématique ?

Cette question est tout à fait légitime car les outils de développement proposés par Google et Apple permettent ce type d'exécution, rien ne semble donc freiner cette possibilité. Cependant dans le cadre d'un développement mobile multi-plateformes l'utilisation d'un Framework tend à organiser le développement autour d'un langage haut niveau, tels que C# ou Javascript, et ce afin de faciliter la génération de code spécifique. C'est pourquoi il est moins trivial d'appeler un code natif lorsque le langage utilisé n'est pas C++.

Indépendamment d'un développement multi plateformes comment développe-t-on du code natif pour mobile ?

Sur la plateforme iOS le code natif se présente de diverses manières il peut être proposé via une librairie dynamique (fichier .dylib), un module objective-C (fichier .mm) ou encore une librairie statique (fichier .a).

Sur Android il faut passer par le Native Development Kit (NDK) pour compiler la librairie en fichier .so (librairie dynamique unix). Ensuite il faut effectuer une liaison (binding) via l'interface java native (JNI) entre la librairie dynamique et le code Java métier.

Ce que l'on a cherché à valider

Lors de notre étude technique nous avons cherché à valider l'exécution de code natif via divers frameworks de développement multi-plateformes (Unity, React-Native et Xamarin). Le code natif que nous avons cherché à exécuter se veut très simple : une fonction qui effectue une somme ou une fonction qui retourne une chaine de caractères. Nous avons chercher à exécuter une fonction simple afin de nous concentrer sur la problématique d'appel. 

La fonction est encapsulée dans une librairie native que nous aurons compilée via le compilateur C++ adéquat de la plateforme cible.

En pratique

Dans cette section nous allons voir comment nous avons réussi à appeler du code natif à partir des différents frameworks ciblés. 

1. Unity

Pour les outils, tout se passe avec les IDE Unity et MonoDevlop.

Unity étant destiné au monde du jeu vidéo, il est donc nécessaire de créer une scene (environnement 3D) associé à un asset de type script (C#).

Ensuite il faut ajouter au projet sous forme d'asset les librairies. Pour Android les fichiers .so dans le dossier Plugins/Android/<ABI>/fichier.so et pour iOS Plugins/iOS/fichier.mm et les configurer comme suis :

Enfin il suffit de charger la librairie en associant une directive de compilation et une directive P/Invoke.

Une fois les fonctions exposées de manière statique il suffit de les appeler dans le code.

La génération de code JNI est transparente avec Unity, néanmoins la compilation des librairies se fera en dehors du projet.

1. React-Native

Au niveau des outils, React-Native ne propose pas d'IDE dédié il faudra passer par un éditeur/IDE supportant au minimum ES6 et au mieux ES6, Java, Objective-C.

Pour React-Native l'appel de code natif sera moins trivial que pour Unity il sera d'abord nécessaire de passer par un outil intermédiaire (Djinni) qui va automatiser la compilation du code C++ et la génération de module (iOS) / code JNI (Android).

Ensuite contrairement à Unity il faudra développer les ponts (bridge/binding) entre le code généré par Djinni et le code généré par React Native.

Enfin dans le code javascript React-Native il suffira de charger le module via le package React-Native NativeModules.

3. Xamarin

En utilisant la dernière version de Visual Studio (PC/Mac) il suffira de créer un projet Xamarin.Forms afin de cibler plusieurs plateformes mobiles.

A partir de ce projet il sera nécessaire de créer dans chaque sous projet cible créé par Xamarin un DependencyService qui implémente une interface définit dans le projet global Xamarin. C'est ensuite dans les implémentations cibles que l'appel à une librairie native se fera, via une directive P/Invoke. Le projet global lui ne fera que des appels de haut niveau en demandant l'interface et non l'implémentation.

La compilation des librairies se fera en dehors du projet Xamarin pour Android, pour iOS il faudra ajouter une directive de compilation au projet.

Retour d'expérience

Comme on a pu le voir au cour de ce dernier article nous avons obtenu un résultat concluant sur chacune des plateformes avec tous les frameworks testés. 

Pour arriver à un résultat avec Unity il nous aura fallu moins d'un jour, une des difficultés rencontrées a été d'utiliser ce frameworks (et ses outils) dans un contexte indépendant du jeu vidéo.

React-Native est clairement l'exemple le plus clé en main que nous ayons produit lors de cette analyse, la génération du code pour une plateforme cible générant également la librairie dynamique. Cependant cette simplicité est coûteuse en temps de configuration (1.5 jour pour arriver à un résultat), nous avons du créer des bindings parfois complexes pour obtenir un tel résultat. De plus, la documentation étant actuellement limitée, aller plus loin que le simple exemple de faisabilité pourra prendre un temps non négligeable.

Avec Xamarin la réalisation d'un exemple aura été assez rapide (1/2 journée) néanmoins la compilation des librairies iOS avec le projet Xamarin n'a pas été évidente à mettre en oeuvre.

Pour aller plus loin

Les projets Unity/React-Native/Xamarin réalisés lors de cette analyse sont disponibles en open source sur notre compte github : https://github.com/smartorigin/Cross-platform-mobile-development-samples N'hésitez pas à les manipuler/tester, des README sont là pour vous aider dans la configuration/prise en main de ces différentes technologies.

Smart/Origin vous accompagne dans la création d'applications mobiles innovantes, de l'étude de faisabilité à la livraison sur les différents store.

 

Article rédigé par Marc-Alexandre Blanchard.