Android NDK

1,176 views
1,089 views

Published on

Ce document est extrait de ma formation “Android - programmation avancée”.
La formation aborde les thèmes suivants : les services, les fournisseurs de contenu, les capteurs, la localisation et évidement le NDK.
Les workshops sont effectués sous Android Studio.

Published in: Technology
0 Comments
1 Like
Statistics
Notes
  • Be the first to comment

No Downloads
Views
Total views
1,176
On SlideShare
0
From Embeds
0
Number of Embeds
1
Actions
Shares
0
Downloads
41
Comments
0
Likes
1
Embeds 0
No embeds

No notes for slide

Android NDK

  1. 1. Android Développement natif avec le NDK
  2. 2. antislashn.org Android avancé - NDK 2 / 97 NDK ● Native Development Kit ● ensemble d'outils permettant de coder en C/C++ pour la plateforme Android ● Google met en avant que ce type dé développement devrait-être exceptionnel source : Google
  3. 3. antislashn.org Android avancé - NDK 3 / 97 NDK ● Le NDK est une suite d'outils ● compilateur – cross-compiler : ARM,x86, MIPS ● Java Native Interface headers ● librairies C – Math, POSIX threads,ZLib ● librairies Android – logging, pixel buffer, native application APIs ● librairies multimédia – OpenGL, OpenSL, OpenMAX
  4. 4. antislashn.org Android avancé - NDK 4 / 97 NDK - support C++ ● Le support de C++ par Android est minimal ● ne prend pas en compte – la librairie standard C++ ● sauf les .h les plus communs ● pas de STL – les exceptions – RTTI (Run-Time Type Information) ● un ensemble de bibliothèques supplémentaires est utilisable – "helper runtimes" – cf. http://developer.android.com/ndk/guides/cpp-support.html
  5. 5. antislashn.org Android avancé - NDK 5 / 97 NDK - support C++ ● Helper Runtimes
  6. 6. antislashn.org Android avancé - NDK 6 / 97 Structure du NDK ● Le répertoire du NDK est soit : ● le répertoire où le NDK a été décompressé ● le répertoire <répertoire-sdk>/ndk-bundle si le NDK a été téléchargé par le SDK Manager
  7. 7. antislashn.org Android avancé - NDK 7 / 97 Structure du NDK ● Composants principaux ● ndk-build : script principal du système de build du NDK ● ndk-gdb : script pour le débogage du code natif, utilise le débogueur gdb ● ndk-stack : exécutable d'analyse des traces de pile créées lors du plantage du code natif
  8. 8. antislashn.org Android avancé - NDK 8 / 97 Structure du NDK ● Répertoires ● build : contient les modules nécessaires pour la chaîne de build ● platforms : contient le fichiers .h pour chaque version Android ● samples : exemples d'application native ● sources : bibliothèques tiers pouvant être importées dans les projets NDK ● toolchains : utilisé par le cross-compiler
  9. 9. antislashn.org Android avancé - NDK 9 / 97 ABI ● Application Binary Interface ● Définit l'interaction du code avec le système ● jeu d'instruction du CPU ● type de mémoire (endian) ● format du binaire ● alignements mémoire, ... ● Un ABI doit être défini pour chaque CPU cible ● actuellement : ARM, x86, MIPS ● cf. : http://developer.android.com/ndk/guides/abis.html
  10. 10. antislashn.org Android avancé - NDK 10 / 97 Android Studio ● Le NDK est géré par le SDK Manager
  11. 11. antislashn.org Android avancé - NDK 11 / 97 JNI ● Java Native Interface ● effectue le lien entre le code natif C/C++ et le code Java ● Pour écrire un code compatible JNI, il faut : ● créer des classes java dont certaines méthodes seront implémentées en C/C++ – ces méthodes ont la signature ● public native return-type method-name (params-list) – chaque méthode correspondra à une fonction C ● qui sera exposée dans une fichier .h
  12. 12. antislashn.org Android avancé - NDK 12 / 97 JNI Application Java Classe Méthodes natives Wrapper C/C++ Bibliothèque C/C++
  13. 13. antislashn.org Android avancé - NDK 13 / 97 Android Studio ● Créer la méthode native public class MainActivity extends AppCompatActivity { static{ System.loadLibrary("hello-jni"); } @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); TextView view = (TextView) findViewById(R.id.jni); String msg = stringFromJNI(); view.setText(msg); } public native String stringFromJNI(); }
  14. 14. antislashn.org Android avancé - NDK 14 / 97 Android Studio ● Créer le fichier .h ● en mode console - dans le terminal d'Android Studio – se positionner dans le répertoire java – lancer la commande javah suivante – le fichier .h a été généré dans le répertoire jni javah -d ..jni org.antislashn.jni.hello.MainActivity
  15. 15. antislashn.org Android avancé - NDK 15 / 97 Android Studio ● Fichier .h généré ● org_antislashn_jni_hello_MainActivity.h /* DO NOT EDIT THIS FILE - it is machine generated */ #include <jni.h> /* Header for class org_antislashn_jni_hello_MainActivity */ #ifndef _Included_org_antislashn_jni_hello_MainActivity #define _Included_org_antislashn_jni_hello_MainActivity #ifdef __cplusplus extern "C" { #endif /* * Class: org_antislashn_jni_hello_MainActivity * Method: stringFromJNI * Signature: ()Ljava/lang/String; */ JNIEXPORT jstring JNICALL Java_org_antislashn_jni_hello_MainActivity_stringFromJNI(JNIEnv *, jobject); #ifdef __cplusplus } #endif #endif
  16. 16. antislashn.org Android avancé - NDK 16 / 97 Android Studio ● Codage en langage C ● le source est mis dans le répertoire jni – nota : dans l'exemple suivant les fichiers .h et .c ont été renommés hello-jni #include "hello-jni.h" #include "string.h" JNIEXPORT jstring JNICALL Java_org_antislashn_jni_hello_MainActivity_stringFromJNI (JNIEnv* env, jobject thiz) { return (*env)->NewStringUTF(env,"Hello from JNI"); }
  17. 17. antislashn.org Android avancé - NDK 17 / 97 Android Studio ● Invocation de la méthode native ● la librairie doit être chargée public class MainActivity extends AppCompatActivity { static{ System.loadLibrary("hello-jni"); } @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); TextView view = (TextView) findViewById(R.id.jni); String msg = stringFromJNI(); view.setText(msg); } public native String stringFromJNI(); } chargement de la librairie seul le nom du module est précisé invocation de la méthode native
  18. 18. antislashn.org Android avancé - NDK 18 / 97 Intégration des librairies C/C++ ● Deux types d'intégrations ● les librairies C/C++ ont déjà été compilées pour les différentes cibles – répertoire libs du module ● contient les sous-répertoires correspondants aux ABIs ● le code source C/C++ doit être compilé – les sources sont dans le répertoire jni du module
  19. 19. antislashn.org Android avancé - NDK 19 / 97 Étapes de création ● Créer un projet Android ● ou modifier un projet existant ● Écriture des méthodes natives Java ● Invocation de l'utilitaire javah pour créer le fichier .h ● Écriture des fonctions C ● Compilation vers les différentes plateformes (ABI) ● avec l'utilitaire ndk-build – script qui utilise la commande make – nécessite des fichiers .mk
  20. 20. antislashn.org Android avancé - NDK 20 / 97 Android Studio ● Structure de répertoire ● ajouter un répertoire jni au projet – passer en vue "Project" pour ajouter le répertoire – répertoire par défaut des sources pour gradle ● les fichiers .h et .c seront placés dans ce répertoire
  21. 21. antislashn.org Android avancé - NDK 21 / 97 Android Studio ● Compilation : plusieurs possibilités ● compilation directe par ndk-build – nécessite les fichiers *.mk – dans le terminal Android Studio ● compilation automatique via Gradle – pas besoin de fichier *.mk – attention aux versions d'Android Studio ● compilation par un l'exécution d'un script Gradle – nécessite les fichiers *.mk – à partir de Android Studio 1.5.1
  22. 22. antislashn.org Android avancé - NDK 22 / 97 Logs ● Utiliser le système de log Android en natif ● inclure android/log.h ● ajouter la librairie au linker ld – Android.mk – build.gradle LOCAL_PATH:=$(call my-dir) include $(CLEAR_VARS) LOCAL_MODULE := hello-jni LOCAL_SRC_FILES := hello-jni.c calcul.c LOCAL_LDLIBS += -llog include $(BUILD_SHARED_LIBRARY) ndk { moduleName "hello-jni" ldLibs "log" }
  23. 23. antislashn.org Android avancé - NDK 23 / 97 Logs ● Niveaux de log ● définis dans un enum – extrait du log.h typedef enum android_LogPriority { ANDROID_LOG_UNKNOWN = 0, ANDROID_LOG_DEFAULT, /* only for SetMinPriority() */ ANDROID_LOG_VERBOSE, ANDROID_LOG_DEBUG, ANDROID_LOG_INFO, ANDROID_LOG_WARN, ANDROID_LOG_ERROR, ANDROID_LOG_FATAL, ANDROID_LOG_SILENT, /* only for SetMinPriority(); must be last */ } android_LogPriority;
  24. 24. antislashn.org Android avancé - NDK 24 / 97 Logs ● L'utilisation du framwork de log nécessite de préciser ● le niveau de log ● le tag du log – identificateur de l'émetteur du log ● le message de log
  25. 25. antislashn.org Android avancé - NDK 25 / 97 Logs ● Fonctions ● __android_log_write : pour un simple message ● __android_log_print : pour un message formaté comme avec le printf du langage C ● __android_log_vprint : pour un message formaté comme avec le printf du langage C – les paramètres sont récupérés par va_list ● __android_log_assert : pour logger des échecs d'assertion
  26. 26. antislashn.org Android avancé - NDK 26 / 97 Logs ● Exemple ● les logs sont affichés dans la vue "logcat" __android_log_write(ANDROID_LOG_INFO,"JNI",">>> simple message"); __android_log_print(ANDROID_LOG_INFO,"JNI",">>> titre : %s",str); __android_log_print(ANDROID_LOG_INFO,"JNI",">>> NB_MEMOS : %i",nbMemos); logInfo("JNI",">>> titre : %s et NB_MEMOS : %d",str,nbMemos); if(!ok) __android_log_assert("!ok","JNI","ok vaut %d",ok); void logInfo(const char* tag, const char* fmt, ...){ va_list args; va_start(args,fmt); __android_log_vprint(ANDROID_LOG_INFO,tag,fmt,args); va_end(args); }
  27. 27. antislashn.org Android avancé - NDK 27 / 97 Déboguer ● Android NDK permet de déboguer une application ● fonctionne à partir de la version Android 2.2 (Froyo) ● Étapes sous Android Studio (version 1.5.1) ● ajouter dans build.gradle le support du debug ● si ce n'est pas fait, créer une configuration Android Native – vérifier que la configuration ne soit pas en erreur ● mettre les points d'arrêt dans le source C ● lancer la configuration native en mode debug
  28. 28. antislashn.org Android avancé - NDK 28 / 97 Déboguer ● Ajouter le support du débogage dans build.gralde ... buldTypes { release { minifyEnabled false proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' } debug{ debuggable=true jniDebuggable=true } } ...
  29. 29. antislashn.org Android avancé - NDK 29 / 97 Déboguer ● Créer un configuration Android Native ● Run → Edit configuration ou ● si nécessaire ajouter une configuration
  30. 30. antislashn.org Android avancé - NDK 30 / 97 Déboguer ● Mettre les points d'arrêt nécessaires dans les sources ● Lancer l'application native en mode debug ● le démarrage de l'application peut être assez long ● l'onglet Debug s'affiche
  31. 31. antislashn.org Android avancé - NDK 31 / 97 JNI - Implémentation de la méthode native ● La fonction native prend au moins deux paramètres ● même si la méthode Java n'a aucun paramètre public class Calcul { public native void foo(); public native static void bar(); } JNIEXPORT void JNICALL Java_org_antislashn_jni_hello_Calcul_foo (JNIEnv *, jobject); JNIEXPORT void JNICALL Java_org_antislashn_jni_hello_Calcul_bar (JNIEnv *, jclass); javah
  32. 32. antislashn.org Android avancé - NDK 32 / 97 JNI - Implémentation de la méthode native ● JNIEnv est un pointeur vers une structure correspondant à l'interface de la JVM ● C++ : le déférencement est effectué par env-> ● C : le déférencement est effectué par (*env)->
  33. 33. antislashn.org Android avancé - NDK 33 / 97 JNI - Implémentation de la méthode native ● Le second paramètre correspond à l'instance de la classe ou la classe elle-même ● méthode statique : paramètre de type jclass ● méthode d'instance : paramètre de type jobject ● Les paramètres supplémentaires correspondent aux paramètres de la méthode native Java ● deux types de paramètres – les types primitifs – les références
  34. 34. antislashn.org Android avancé - NDK 34 / 97 JNI - Types primitifs ● Les types primitifs ont une correspondance directe avec les types C/C++ Java JNI C/C++ Taille boolean jboolean unsigned char unsigned 8 bits byte jbyte char signed 8 bits char jchar unsigned short unsigned 16 bits short jshort short signed 16 bits int jint int signed 32 bits long jlong long long signed 64 bits float jfloat float 32 bits double jdouble double 64 bits
  35. 35. antislashn.org Android avancé - NDK 35 / 97 JNI - les références Types Java Types Natifs java.lang.Class jclass java.lang.Throwable jtrhowable java.lang.String jstring autres objets jobject java.lang.Object[] jobjectArray boolean[] jbooleanArray byte[] jbyteArray char[] jcharArray short[] jshortArray int[] jintArray long[] jlongArray float[] jfloatArray double jdoubleArray
  36. 36. antislashn.org Android avancé - NDK 36 / 97 JNI - les références ● Les références sont passées à la méthodes natives ● elles ne peuvent pas être directement modifiées ● le garbage-collector utilise les références pour libérer la mémoire ● JNI propose trois types de références – référence local (local reference) – référence globale (global reference) – référence faible (weak reference)
  37. 37. antislashn.org Android avancé - NDK 37 / 97 JNI - référence locale ● Les références locales sont libérées au retour de la fonction native ● la référence, pas l'objet qui est référencé – les références passées au fonctions natives sont locales – la plupart des références retournées par JNI sont locales ● une référence locale ne doit pas être sauvegardée d'un appel de méthode à l'autre – le code ci-dessous n'est donc pas valide static jobject myReference; JNIEXPORT void JNICALL Java_org_antislashn_jni_hello_Calcul_foo (JNIEnv *env, jobject thiz, jobject lRef){ myReference = lRef; }
  38. 38. antislashn.org Android avancé - NDK 38 / 97 JNI - référence locale ● Les références locales peuvent être supprimées ● La spécification impose à la JVM de garder au moins 16 références en même temps ● la JVM peut refuser d'en créer d'autres ● il peut être nécessaire de préciser explicitement le nombre de références (*env)->DeleteLocalRef(myReference); (*env)->EnsureLocalCapacity(40);
  39. 39. antislashn.org Android avancé - NDK 39 / 97 JNI - référence globale ● Une référence globale garde une référence sur un objet ● le garbage-collector tient compte de cette référence ● Une références globale peut être créée sur une référence locale ● Une référence globale peut-être supprimée jclass localClazz; jclass globalClazz; localClazz = (*env)->FindClass(env, "java/lang/String"); globalClazz = (*env)->NewGlobalRef(env,localClazz); (*env)->DeleteGlobalRef(env,globalClazz);
  40. 40. antislashn.org Android avancé - NDK 40 / 97 JNI - référence faible ● Une référence faible peut-être utilisée d'une méthode native à une autre méthode native ● comme pour une référence globale ● Une référence faible ne permet pas de garantir que l'objet référencé ne supprimé par le GC ● à l'inverse d'une référence globale
  41. 41. antislashn.org Android avancé - NDK 41 / 97 JNI - référence faible ● Création d'une référence faible ● Vérification de la validité ● Suppression jclass localClazz; jclass weakClazz; localClazz = (*env)->FindClass(env, "java/lang/String"); weakClazz = (*env)->NewWeakGlobalRef(env,localClazz); if((*env)->IsSameObject(env,weakClazz,NULL) == JNI_TRUE){ // le GC a supprimé l'objet, l'objet ne peut pas être utilisé } else{ // l'objet est toujours valide, il peut-être utilisé } (*env)->DeleteWeakGlobalRef(env,weakClazz);
  42. 42. antislashn.org Android avancé - NDK 42 / 97 JNI ● Les pages suivantes présentent quelques opérations de base ● création d'objets, conversion vers C, destruction, … ● Toutes ces opérations sont accessibles via le pointeur vers JNIEnv ● voir la documentation – http://docs.oracle.com/javase/8/docs/technotes/guides/jni/spec/jniTOC.html
  43. 43. antislashn.org Android avancé - NDK 43 / 97 JNI - opérations sur String ● Profonde différence entre les chaînes de caractères Java et C ● JNI support Unicode et UTF-8 – les méthodes diffèrent par l'ajout de "UTF" dans leur nom ● Création ● retourne NULL si la construction a échouée jstring javaString; javaString = (*env)->NewStringUTF(env,"hello, world");
  44. 44. antislashn.org Android avancé - NDK 44 / 97 JNI - opérations sur String ● Conversion vers C ● isCopy indique si une copie a été effectuée – peut-être NULL ● Destruction d'une chaîne C retournée par une fonction JNI jstring javaString; const jbyte * str; jboolean isCopy; javaString = (*env)->NewStringUTF(env,"hello, world"); str = (*env)->GetStringUTFChars(env,javaString,$isCopy); (*env)->ReleaseStringUTFChars(env,javaString,str);
  45. 45. antislashn.org Android avancé - NDK 45 / 97 JNI - opérations sur les tableaux ● Création ● Les différentes signatures sont du type NewXXXArray – où XXX est Int, Char, Byte,... – renvoie NULL si la création n'a pas été possible ● Conversion vers C jintArray ints; ints = (*env)->NewIntArray(env,10); jintArray javaArray; javaArray = (*env)->NewIntArray(env,10); jint nativeArray[10]; (*env)->GetIntArrayRegion(env, javaArray,0,10, nativeArray);
  46. 46. antislashn.org Android avancé - NDK 46 / 97 JNI - opérations sur les tableaux ● Utilisation d'un pointeur ● fonctions GetXXXArrayElements ● le pointeur retourné doit être libérer – fonctions ReleaseXXXArrayElements jint* pJint = (*env)->GetIntArrayElements(env,javaArray); (*env)->ReleaseIntArrayElements(env,javaArray,pJint,0); release mode Release Mode Action 0 Recopie contenu et libère le tableau natif JNI_COMMIT Recopie le contenu mais ne libère pas le tableau natif Le tableau Java peut-être mis à jour JNI_ABORT Libère le tableau natif sans recopier son contenu
  47. 47. antislashn.org Android avancé - NDK 47 / 97 JNI - NIO ● Fonctions JNI pour utiliser les buffers NIO ● un "direct buffer" est un tableau d'octets ● Création ● Récupération unsigned char* buffer = (unsigned char*) malloc(1024); jobject directBuffer = (*env)->NewDirectByteBuffer(env,buffer,1024); unsigned char * buf = (unsigned char*) (*env)->GetDirectBufferAddress(env,directBuffer);
  48. 48. antislashn.org Android avancé - NDK 48 / 97 JNI - membres d'une classe Java ● Deux types de membre ● méthode ou propriété d'instance ● méthode ou propriété de classe - propriété statique ● L'accès aux membres d'une instance (ou d'une classe) passe par le type jclass jclass clazz = (*env)->GetObjectClass(env,instance);
  49. 49. antislashn.org Android avancé - NDK 49 / 97 JNI - membres d'une classe Java ● Les extraits de code des pages suivantes utilisent la classe Memo public class Memo { private static int NB_MEMOS = 0; private int urgence = 0; private String titre; private String descriptif; public Memo() { NB_MEMOS++; } public Memo(String titre, String descriptif) { this.titre = titre; this.descriptif = descriptif; NB_MEMOS++; } // Liste des getteurs / setteurs ... }
  50. 50. antislashn.org Android avancé - NDK 50 / 97 JNI - signature des types ● La manipulation des propriétés et des méthodes passe par l'utilisation des signatures de type de la JVM Type Java Signature boolean Z byte B char C short S int I long J float F double D classe ex: java.lang.String Ljava/lang/String; type[] [type méthode (arg-type) ret-type retour void V
  51. 51. antislashn.org Android avancé - NDK 51 / 97 JNI - signature des types ● Exemple ● une méthode int foo(int n, String s, int[] ints); – est associée au descripteur (ILjava/lang/String;[I)J ● une méthode int bar(); – est associée au descripteur ()I ● une méthode void foo(String s); – est associée au descripteur (Ljava/lang/String;)V
  52. 52. antislashn.org Android avancé - NDK 52 / 97 JNI - signature des types ● L'outil javap aide à trouver les signatures javap -classpath .debug -p -s org.antislashn.jni.hello.Memo ... public int getUrgence(); descriptor: ()I public void setUrgence(int); descriptor: (I)V public java.lang.String getTitre(); descriptor: ()Ljava/lang/String; public void setTitre(java.lang.String); descriptor: (Ljava/lang/String;)V ...
  53. 53. antislashn.org Android avancé - NDK 53 / 97 JNI - propriétés d'une classe Java ● Accès aux propriétés ● d'abord récupérer le jfieldID de la propriété – méthodes de JNIEnv : GetFieldId ou GetStaticFieldId ● lire la valeur du champ – méthodes GetXxxField pour les propriétés d'instance – méthodes GetStaticXxxField pour les propriétés de classe – où Xxx est Int, Boolean, Short, Object, … ● mettre à jour la valeur du champ – méthodes SetXxxField pour les propriétés d'instance – méthodes SetStaticXxxField pour les propriétés de classe – où Xxx est Int, Boolean, Short, Object, …
  54. 54. antislashn.org Android avancé - NDK 54 / 97 JNI - propriétés d'une classe Java ● Accès aux propriétés : extrait de code ... jclass clazz = (*env)->GetObjectClass(env,instance); jfieldID instanceFieldID = (*env)->GetFieldID(env,clazz,"titre","Ljava/lang/String;"); jfieldID staticFieldId = (*env)->GetStaticFieldID(env,clazz,"NB_MEMOS","I"); jstring titre = (*env)->GetObjectField(env,instance,instanceFieldID); jint nb = (*env)->GetStaticIntField(env,clazz,staticFieldId); (*env)->SetStaticIntField(env,clazz,staticFieldId,35); ... statique donc de type jclass instance de Memo reçue comme paramètre de la méthode nouvelle valeur pour la propriété statique de Memo type de la propriété
  55. 55. antislashn.org Android avancé - NDK 55 / 97 JNI - méthodes d'une classe Java ● Comme pour les propriétés ● deux types de méthodes – statique – ou d'instance ● Mêmes principes que pour accéder aux propriétés ● récupération du l'identifiant de type jmethodID – méthodes de JNIEnv : GetMethodID, GetStaticMethodID ● puis appel de la méthode – méthodes CallStaticXxxMethod, CallXxxMethod,.. ● cf. la documentation pour l'ensemble des méthodes d'invocation
  56. 56. antislashn.org Android avancé - NDK 56 / 97 JNI - méthodes d'une classe Java ● Invocation d'une méthode Java, extrait de code ... jmethodID setTitreID = (*env)->GetMethodID(env,clazz,"setTitre","(Ljava/lang/String;)V"); jstring newTitle = (*env)->NewStringUTF(env,"Nouveau titre"); (*env)->CallVoidMethod(env,instance,setTitreID,newTitle); jmethodID getTitreID = (*env)->GetMethodID(env,clazz,"getTitre","()Ljava/lang/String;"); jstring t = (*env)->CallObjectMethod(env,instance,getTitreID); ... paramètre de type jstring
  57. 57. antislashn.org Android avancé - NDK 57 / 97 Multithreading ● Un thread Java peut lancer du code natif ● aisé à coder, le code Java interagit avec les instances des classes java.lang.Thread ● pas d'impact sur le code natif ● le code natif peut communiquer avec le code Java via JNIEnv
  58. 58. antislashn.org Android avancé - NDK 58 / 97 Multithreading ● L'utilisation des threads Java doit prendre en considération les points suivants ● le code natif est-il thread-safe ? ● le code natif ne peut pas bénéficier de la programmation concurrente Java ● le code natif exécuté dans des threads Java différents – ne peuvent pas communiquer – ne peuvent pas partager des ressources
  59. 59. antislashn.org Android avancé - NDK 59 / 97 Multithreading ● Exemple de code ● le code Java crée des threads ● le code natif est exécuté par le thread Java ... private void javaThreads(int threads, final int iterarions){ // Création d'un thread Java pour chaque worker natif for(int i=0 ; i<threads ; i++){ final int id = i; Thread thread = new Thread(){ @Override public void run() { nativeWorker(id,iterarions); } }; thread.start(); } } ...
  60. 60. antislashn.org Android avancé - NDK 60 / 97 POSIX Thread ● POSIX : norme technique de l'IEEE ● IEEE : Institute of Electrical an Electronics Engineers ● POSIX : Portable Operating System Interface X ● POSIX Thread est aussi nommé Pthreads ● Utilisation de Pthread ● inclure la librairie pthread.h – l'implémentation fait partie de Bionic – pas de lien supplémentaire pour le linker ● Les pages suivantes présentent les principes de base de PThread ● voir le man pour une documentation complète
  61. 61. antislashn.org Android avancé - NDK 61 / 97 POSIX Thread ● Création d'un thread : fonction pthread_create ● arguments – pthread_t* thread : pointeur mis à jour par la fonction pour retourner le nouveau thread – pthread_attr_t const* attr : attributs nécessaires à la création du thread (base de la pile, taille de la pile, etc...) – void* (*start_routine)(void*) : pointeur vers fonction de la routine devant être exécutée par le thread – void* arg : arguments passés à la fonction, peut être NULL ● retour – int : 0 si le thread est créé, ou code d'erreur
  62. 62. antislashn.org Android avancé - NDK 62 / 97 POSIX Thread ● Les pthreads ne font pas partie de la plateforme Java ● ils doivent être attachés à la JVM pour interagir avec la partie Java ● une fois attaché le pthread doit invoquer une méthode callback pour interagir avec l'IHM – le pthread doit donc avoir une référence vers l'activité ● utilisation de références globales comme cache ● utilisation de la fonction JNI_OnLoad – fonction appelée par la JVM lorsque la librairie est chargée – l'appel de la fonction fournit comme premier paramètre un pointeur vers la JVM – le pthread est détaché lorsque la tâche est finie
  63. 63. antislashn.org Android avancé - NDK 63 / 97 POSIX Thread ● Exemple de code ● la référence vers la JVM est mise en cache – les références globales devront être supprimées ... static jmethodID gOnNativeMessage = NULL; static JavaVM* gVm = NULL; static jobject gObj = NULL; jint JNI_OnLoad(JavaVM* vm, void* resserved){ gVm = vm; return JNI_VERSION_1_2; } ...
  64. 64. antislashn.org Android avancé - NDK 64 / 97 POSIX Thread ● Le pthread doit être créé, puis attaché à la JVM ● création par pthread_create ● la routine exécutée par pthread devra – attacher le pthread à la JVM – exécuter la tâche – détacher le pthread à la JVM
  65. 65. antislashn.org Android avancé - NDK 65 / 97 POSIX Thread ● Extraits de code ... pthread_t thread; // Create a new thread int result = pthread_create(&thread,NULL,nativeWorkerThread,static_cast<void*>(nativeWorkerArgs)); if (0 != result) { ... création du pthread fonction exécutée dans le pthread pointeur sur les paramètres passés à la fonction qui sera exécutée dans le pthread
  66. 66. antislashn.org Android avancé - NDK 66 / 97 POSIX Thread ● Code de la fonction exécutée dans le pthread static void* nativeWorkerThread(void* args) { JNIEnv *env = NULL; // On attache le thread courant à la JVM // et on récupère un pointeur vers JNIEv if (gVm->AttachCurrentThread(&env, NULL) == 0) { NativeWorkerArgs* nArgs = static_cast<NativeWorkerArgs*>(args); // Excéution du worker natif dans le context du thread Java_org_antislashn_threads_MainActivity_nativeWorker(env, gObj, nArgs->id, nArgs->iterations); delete nArgs; // on détache le thread courant de la JVM gVm->DetachCurrentThread(); } return (void*)1; } tâche réellement exécutée dans le pthread la tâche est attachée avant son exécution puis détachée à la fin de son exécution
  67. 67. antislashn.org Android avancé - NDK 67 / 97 POSIX Thread ● Contrairement aux threads Java, les threads POSIX peuvent renvoyer un résultat ● une fonction de type join est utilisée pour attendre la fin du Pthread et récupérer le résultat – int pthread_join(pthread_t thread, void** ret_val) ● cette fonction est bloquante jusqu'à la fin du pthread ● paramètres – pthread_t thread : thread retourné par pthread_create – void** ret_val : pointeur vers le résultat (de type void*) retourné par le tâche du pthread
  68. 68. antislashn.org Android avancé - NDK 68 / 97 POSIX Thread ● Extrait de code ... pthread_t* handles = new pthread_t[nbThreads]; ... // Attente de la fin des tâches exécutées par les pthreads for(jint i=0 ; i< nbThreads ; i++){ void* result = NULL; int r=0; if((r=pthread_join(handles[i],&result)) != 0){ ... } else{ char message[50]; sprintf(message,"Worker %d returned %d", i, result); jstring messageString = env->NewStringUTF(message); env->CallVoidMethod(thizz, gOnNativeMessage, messageString); if (NULL != env->ExceptionOccurred()) { return; } } } ...
  69. 69. antislashn.org Android avancé - NDK 69 / 97 POSIX Thread ● Synchronisation des pthreads par mutexes ● type pthread_mutex_t ● Fonctions de base ● initialisation d'un mutex – fonction pthread_mutex_init – macro PTHREAD_MUTEX_INITIALIZER ● blocage d'un mutex – fonction pthread_mutext_lock ● déblocage d'un mutex – fonction pthread_mutex_unlock
  70. 70. antislashn.org Android avancé - NDK 70 / 97 POSIX Thread ● Initialisation d'un mutex ● il est nécessaire de créer un variable de type pthread_mutext_t – ici en variable statique ● puis appel de la fonction d'initialisation – le second paramètre étant NULL, création d'un mutex par défaut ... static pthread_mutex_t mutex; ... void Java_org_antislashn_threads_MainActivity_nativeInit(JNIEnv *env, jobject thizz) { if(pthread_mutex_init(&mutex,NULL)!=0){ jclass exceptionClazz = env->FindClass("java/lang/RuntimeException"); env->ThrowNew(exceptionClazz, "Unable to initialize mutex"); return; } ...
  71. 71. antislashn.org Android avancé - NDK 71 / 97 POSIX Thread ● Le worker du pthread peut acquérir le mutex en début de tâche et le libérer lorsque la tâche est terminée ● la fonction pthread_mutext_lock est bloquante tant que le mutex n'est pas disponible ... void Java_org_antislashn_threads_MainActivity_nativeWorker(JNIEnv *env, jobject thizz, jint id, jint iterations) { if(pthread_mutex_lock(&mutex) !=0){ jclass exceptionClazz = env->FindClass("java/lang/RuntimeException"); env->ThrowNew(exceptionClazz, "Unable to lock mutex"); return; } ... if (pthread_mutex_unlock(&mutex) != 0) { jclass exceptionClazz = env->FindClass("java/lang/RuntimeException"); env->ThrowNew(exceptionClazz, "Unable to unlock mutex"); return; } } ...
  72. 72. antislashn.org Android avancé - NDK 72 / 97 POSIX Thread ● Lorsque le mutex n'est plus utilisé il doit être détruit ● ici dans la fonction native, invoquée par onDestroy() de l'activité ... void Java_org_antislashn_threads_MainActivity_nativeFree(JNIEnv *env, jobject thizz) { ... if(pthread_mutex_destroy(&mutex) != 0){ jclass exceptionClazz = env->FindClass("java/lang/RuntimeException"); env->ThrowNew(exceptionClazz, "Unable to destroy mutex"); } } ...
  73. 73. antislashn.org Android avancé - NDK 73 / 97 Fichier Android.mk ● Fichier de type makefile utilisé par la chaîne de build ● Permet de regrouper les sources en modules de type librairie statique ou dynamique ● le fichier Android.mk peut définir plusieurs modules ● seules les librairies dynamiques peuvent être incluent dans l'apk final ● Cf. la documentation de référence ● http://android.mk/
  74. 74. antislashn.org Android avancé - NDK 74 / 97 Fichier Android.mk ● Fichier minimal type ● LOCAL_PATH directive de début de fichier – indique l'endroit où se trouve les sources – my-dir renvoie le répertoire courant (celui où est Android.mk) ● CLEAR-VARS nettoie les varaiables LOCAL_XXX – LOCAL_MODULE, LOCAL_SRC_FILE, … – sauf LOCAL_PATH LOCAL_PATH := $(call my-dir) include $(CLEAR_VARS) LOCAL_MODULE := hello-jni LOCAL_SRC_FILES := hello-jni.c include $(BUILD_SHARED_LIBRARY)
  75. 75. antislashn.org Android avancé - NDK 75 / 97 Fichier Android.mk ● LOCAL_MODULE définit le nom d'un module ● ce nom doit être unique, et ne doit pas contenir d'espace – si le module est nommé foo, la librairie générée sera libfoo.so ● LOCAL_SRC_FILES liste des fichiers C/C++ qui seront assemblés dans le module ● juste les fichier .c et .cpp – séparés par un espace ● si les extensions sont différentes utiliser la variable LOCAL_CPP_EXTENSION
  76. 76. antislashn.org Android avancé - NDK 76 / 97 Fichier Android.mk ● include $(BUILD_SHARED_LIBRARY) ● permet la génération de la librairie dynamique ● BUID_STATIC_LIBRARY génère une librairie statique ● LOCAL_STATIC_LIBRARIES ajoute des librairies statiques au modules
  77. 77. antislashn.org Android avancé - NDK 77 / 97 Fichier Android.mk ● Génération de plusieurs librairies dynamiques LOCAL_PATH := $(call my-dir) # # Module 1 # include $(CLEAR_VARS) LOCAL_MODULE := module1 LOCAL_SRC_FILES := module1.c include $(BUILD_SHARED_LIBRARY) # # Module 2 # include $(CLEAR_VARS) LOCAL_MODULE := module2 LOCAL_SRC_FILES := module2.c include $(BUILD_SHARED_LIBRARY)
  78. 78. antislashn.org Android avancé - NDK 78 / 97 Fichier Android.mk ● Génération de librairies statiques LOCAL_PATH := $(call my-dir) # # 3rd party AVI library # include $(CLEAR_VARS) LOCAL_MODULE := avilib LOCAL_SRC_FILES := avilib.c platform_posix.c include $(BUILD_STATIC_LIBRARY) # # Native module # include $(CLEAR_VARS) LOCAL_MODULE := module LOCAL_SRC_FILES := module.c LOCAL_STATIC_LIBRARIES := avilib include $(BUILD_SHARED_LIBRARY)
  79. 79. antislashn.org Android avancé - NDK 79 / 97 Fichier Android.mk ● Partager une librairie C/C++ entre plusieurs projets NDK ● les librairies à partager dont mises dans un répertoire spécifique – hors de tout projet NDK ● le nom du répertoire de la librairie correspond au nom du module – avec leurs propres fichier .mk ● ils seront invoqués lors de la construction du projet LOCAL_PATH:=$(call my-dir) include $(CLEAR_VARS) LOCAL_MODULE := calculs LOCAL_SRC_FILES := calculs.c include $(BUILD_SHARED_LIBRARY) contient
  80. 80. antislashn.org Android avancé - NDK 80 / 97 Fichier Android.mk ● Partager une librairie C/C++ entre plusieurs projets NDK ● le projet NDK qui utilise la librairie partagée déclare celle- ci dans son fichier Android.mk ● il faut positionner la variable d'environnement NDK_MODULE_PATH avant d'invoquer ndk-build LOCAL_PATH:=$(call my-dir) include $(CLEAR_VARS) LOCAL_MODULE := module LOCAL_SRC_FILES := module.c LOCAL_SHARED_LIBRARIES := calculs include $(BUILD_SHARED_LIBRARY) $(call import-module,calculs)
  81. 81. antislashn.org Android avancé - NDK 81 / 97 Android Studio - compilation automatique ● Par défaut Gradle construit les librairies .so si un répertoires jni est présent ● la compilation est effectuée à la construction de l'apk ● les fichiers .mk ne sont pas nécessaires – ils ne seront pas pris en compte ● les librairies sont construites dans le répertoire libs du projet – le nom par défaut de la librairie est app ● les fichiers générés pour les différentes cibles seront nommés : libapp.so –
  82. 82. antislashn.org Android avancé - NDK 82 / 97 Android Studio - compilation automatique ● Il est aussi possible de paramétrer gradle pour compiler et générer les fichiers .so au moment de la création de l'apk ● dans le fichier build.gradle du module – dans la section defaultConfig ● il faut que le répertoire du NDK soit positionné dans le fichier local.properties ... ndk { moduleName "hello-jni" } ... ndk.dir=C:DEVELOPPEMENTSAndroidsdkndk-bundle sdk.dir=C:DEVELOPPEMENTSAndroidsdk
  83. 83. antislashn.org Android avancé - NDK 83 / 97 Android Studio - compilation automatique ● Selon la version d'Android Studio il peut être nécessaire d'ajouter dans le fichier gradle.properties la ligne ● une erreur apparaît lors de la construction de l'apk ● L'ensemble des macros et variables du fichier make sont configurables par gradle android.useDeprecatedNdk=true ndk { moduleName "mymodule" ldLibs "log" stl "gnustl_static" cFlags "-std=c++11 -fexceptions" }
  84. 84. antislashn.org Android avancé - NDK 84 / 97 ● Il faut désélectionner l'appel automatique de ndk build‑ par gradle ● dans le fichier build.gradle – annulation de la déclaration des répertoires JNI Android Studio - par ndk-build android { compileSdkVersion 23 buildToolsVersion "23.0.2" sourceSets{ main{ jniLibs.srcDirs "src/main/libs" jni.srcDirs = [] } } ...
  85. 85. antislashn.org Android avancé - NDK 85 / 97 Android Studio - par ndk-build ● Compilation dans le terminal ● ndk-build utilise make, il est nécessaire d'ajouter dans le répertoire jni – un fichier Android.mk pour l'unité de compilation ● cf. http://developer.android.com/ndk/guides/android_mk.html – un fichier Application.mk pour les cibles ● cf. http://developer.android.com/ndk/guides/application_mk.html LOCAL_PATH:=$(call my-dir) include $(CLEAR_VARS) LOCAL_MODULE := hello-jni LOCAL_SRC_FILES := hello-jni.c include $(BUILD_SHARED_LIBRARY) Android.mk APP_ABI := all Application.mk
  86. 86. antislashn.org Android avancé - NDK 86 / 97 Android Studio - par ndk-build ● Compilation dans le terminal ● dans le terminal – se positionner dans le répertoire jni – invoquer la commande ndk-build ● Les librairies des différentes cibles sont générées dans le répertoire main/libs
  87. 87. antislashn.org Android avancé - NDK 87 / 97 Android Studio - par script gradle ● Il est aussi possible d'automatiser l'appel de ndk build‑ par un script gradle, dans le fichier build.gradle du module ● il faut éviter l'invocation automatique de ndk build‑ – dans android.sourceSets.main ● et ajouter en fin de fichier, en fait en dehors du bloc android{...} jni.srcDirs = [] task ndkBuild(type:Exec) { if (Os.isFamily(Os.FAMILY_WINDOWS)) { commandLine 'ndk-build.cmd', '-C', file('src/main').absolutePath } else { commandLine 'ndk-build', '-C', file('src/main').absolutePath } } tasks.withType(JavaCompile) { compileTask -> compileTask.dependsOn ndkBuild }
  88. 88. antislashn.org Android avancé - NDK 88 / 97 SWIG ● SWIG est un outil permettant de connecter du code C/C++ avec d'autres langages ● ne fait pas partie d'Android ● n'est pas dédié à Java ● SWIG nécessite l'écriture d'une interface afin de produire le code Java ● Site officiel : http://www.swig.org/ ● installation sous windows, linux, Mac
  89. 89. antislashn.org Android avancé - NDK 89 / 97 SWIG - exemple de base ● Comme introduction à Swig nous allons utiliser la fonction getuid ● cf. http://linux.die.net/man/2/getuid ● Etapes ● écrire l"interface SWIG ● généré le proxy Java ● ajouter les sources générées par SWIG dans Android.mk ● utiliser le proxy généré par SWIG
  90. 90. antislashn.org Android avancé - NDK 90 / 97 SWIG - interface ● Ce fichier déclare ● les prototypes de fonctions ● les déclarations de classes ● les déclarations de variables ● Syntaxe similaire à un header C/C++ ● Le fichier interface s’appellera unix.i ● mis dans le répertoire jni
  91. 91. antislashn.org Android avancé - NDK 91 / 97 SWIG - interface ● Interface Unix.i /* Module name is unix. */ %module unix %{ /* Include the POSIX operating system APIs. */ #include < unistd.h> %} /* Tell SWIG about uid_t. */ typedef unsigned int uid_t; /* Ask SWIG to wrap getuid function. */ extern uid_t getuid(void);
  92. 92. antislashn.org Android avancé - NDK 92 / 97 SWIG - génération du proxy Java ● Auparavant il faut créer le package java de la classe proxy ● ici : org.antislashn.swig.proxy ● Puis lancer la commande swig ● Génération : ● un fichier Unix_wrap.c ● deux classes Java – Unix.java – UnixJNI.java swig -java -package org.antislashn.swig.proxy -outdir java/org/antislashn/swig/proxy jni/unix.i
  93. 93. antislashn.org Android avancé - NDK 93 / 97 SWIG - utilisation ● Deux classes Java ont été générées ● L'utilisation est triviale public class Unix { public static long getuid() { return UnixJNI.getuid(); } } public class UnixJNI { public final static native long getuid(); } public class MainActivity extends AppCompatActivity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); TextView tv = (TextView) findViewById(R.id.uid); tv.setText(Long.toString(Unix.getuid())); } }
  94. 94. antislashn.org Android avancé - NDK 94 / 97 Web ● http://developer.android.com/ndk/index.html ● http://developer.android.com/tools/building/configuring-gradle.html ● http://tools.android.com/tech-docs/new-build-system/user-guide ● http://tools.android.com/tech-docs/new-build-system/gradle-experimental ● http://android.mk/ ● http://mobilepearls.com/labs/native-android-api/ ● http://developer.android.com/ndk/guides/cpp-support.html ● http://docs.oracle.com/javase/8/docs/technotes/guides/jni/spec/jniTOC.html ● http://linux.die.net/man/7/pthreads
  95. 95. antislashn.org Android avancé - NDK 95 / 97 Bibliographie ● Apress ● Pro Android C++ with th NDK ● Beginning Android C++ Game Developement ● Packt Publishing ● Android Native Development Kit Cookbook ● Android NDK ● Gradle for Android
  96. 96. antislashn.org Titre support 96 / 97 copyleft Support de formation créé par Franck SIMON http://www.franck-simon.com
  97. 97. antislashn.org Titre support 97 / 97 copyleft Cette œuvre est mise à disposition sous licence Attribution Pas d'Utilisation Commerciale Partage dans les Mêmes Conditions 3.0 France. Pour voir une copie de cette licence, visitez http://creativecommons.org/licenses/by-nc-sa/3.0/fr/ ou écrivez à Creative Commons, 444 Castro Street, Suite 900, Mountain View, California, 94041, USA.

×