1. Mise en place d'un projet natif dans Android Studio
Voici un exemple de configuration CMake pour générer une bibliothèque partagée.
cmake_minimum_required(VERSION 3.10.2)
project("applicationNative")
add_library(nativedll SHARED code-cpp.cpp source.c)
find_library(bibliotheque-log log)
target_link_libraries(nativedll ${bibliotheque-log})
2. Appels depuis Java vers le code natif
Déclarer une méthode native dans une classe Java.
public native String recupererChaineDepuisNatif();
Implémenter la méthode en C++.
extern "C" JNIEXPORT jstring JNICALL
Java_com_exemple_app_Activity_recupererChaineDepuisNatif(
JNIEnv* environ,
jobject instance) {
std::string message = "Salut depuis le code natif";
return environ->NewStringUTF(message.c_str());
}
3. Configuration des logs personnalisés
#ifndef JOURNALNATIF_H
#define JOURNALNATIF_H
#define ETIQUETTE_LOG "monAppNatif"
#include <android/log.h>
#define JOURNAL_DEBUG(TAG, ...) __android_log_print(ANDROID_LOG_DEBUG, TAG, __VA_ARGS__)
#define JOURNAL_INFO(TAG, ...) __android_log_print(ANDROID_LOG_INFO, TAG, __VA_ARGS__)
#define JOURNAL_ERREUR(TAG, ...) __android_log_print(ANDROID_LOG_ERROR, TAG, __VA_ARGS__)
#endif
Utilisation : JOURNAL_INFO(ETIQUETTE_LOG, "Appel méthode %d", valeur);
4. Appels depuis le code natif vers Java
Processus général :
Obtenir la classe Java via FindClass ou GetObjectClass.
Acquérir l'identifiant de la méthode avec GetMethodID ou GetStaticMethodID.
Créer une instance de l'objet si nécessaire.
Appeler la méthode appropriée.
Gestion des environnements d'exécution :
Dans un thread natif, l'utilisation de JNIEnv et des références Java nécessite une attache au processus via JavaVM.
static JavaVM *vmJavaGlobal = NULL;
static jclass classeRef = NULL;
static inline JNIEnv *obtenirEnvironnement(bool *necessiteDetach) {
*necessiteDetach = false;
JNIEnv *env = NULL;
int statut = vmJavaGlobal->GetEnv(reinterpret_cast<void **>(&env), JNI_VERSION_1_4);
if (statut < 0) {
JavaVMAttachArgs arguments = {JNI_VERSION_1_4, NULL, NULL};
int resultat = vmJavaGlobal->AttachCurrentThread(&env, (void *) &arguments);
if (resultat != JNI_OK) {
return NULL;
}
*necessiteDetach = true;
}
return env;
}
static inline void libererEnvironnement() {
vmJavaGlobal->DetachCurrentThread();
}
Exemple d'initialisation pour stocker des références globales.
static void initialiserConfiguration(JNIEnv *environ) {
jclass classe;
environ->GetJavaVM(&vmJavaGlobal);
classe = environ->FindClass("com/exemple/app/MaClasse");
classeRef = reinterpret_cast<jclass>(environ->NewGlobalRef(classe));
}
Appel d'une méthode Java depuis le code natif.
extern "C"
JNIEXPORT jint JNICALL
Java_com_exemple_app_Activity_appelerAge(JNIEnv *environ, jobject objet) {
jclass classe = environ->FindClass("com/exemple/app/Activity");
jmethodID methode = environ->GetMethodID(classe, "obtenirAge", "()I");
jobject instance = environ->AllocObject(classe);
jint resultat = environ->CallIntMethod(instance, methode);
JOURNAL_INFO(ETIQUETTE_LOG, "Valeur retournée %d", resultat);
return resultat;
}
extern "C"
JNIEXPORT jstring JNICALL
Java_com_exemple_app_Test_appelerNom(JNIEnv *environ, jobject objet) {
const char *chaine = NULL;
jclass classe = environ->FindClass("com/exemple/app/Test");
jmethodID methode = environ->GetMethodID(classe, "obtenirNom", "()Ljava/lang/String;");
jobject instance = environ->AllocObject(classe);
jstring resultat = static_cast<jstring>(environ->CallObjectMethod(instance, methode));
if (resultat) chaine = environ->GetStringUTFChars(resultat, NULL);
JOURNAL_INFO(ETIQUETTE_LOG, "Nom récupéré %s", chaine);
environ->ReleaseStringUTFChars(resultat, chaine);
return resultat;
}
En Java, définir les méthodes cibles.
public native int appelerAge();
public int obtenirAge() {
return 25;
}
public class Test {
public native String appelerNom();
public String obtenirNom() {
return "Dupont";
}
}
5. Gestion des threads avec JNI
Utilisez JavaVM pour obtenir JNIEnv dans les threads natifs, créez des références globales pour les objets Java, et libérez les ressources après utilisation.
Conseils : Conservez JavaVM* comme variable globale, convertissez les références locales en globales pour la portabilité multi-thread, et détachez correctement les threads.