使用JNI在JAVA和C++之間進行互動操作
原文連結地址:
https://library.vuforia.com/articles/Solution/How-To-Communicate-Between-Java-and-C-using-the-JNI
本文旨在描述如何使用JNI(Java Native Interface)實現JAVA和C++之間的互相呼叫。
1.在JAVA中呼叫C++方法
假設在ImageTargets.java中,C++方法會被宣告為以public native開頭的函式,例如:public native int initTracker();
在C++程式碼檔案ImageTargets.cpp 中函式會被宣告為:
#include <jni.h>
#ifdef __cplusplus
extern "C"
{
#endif
JNIEXPORT int JNICALL
Java_com_qualcomm_QCARSamples_ImageTargets_ImageTargets_initTracker(JNIEnv *, jobject)
{
...
}
#ifdef __cplusplus
}
#endif
首先,需要注意的是所有在C++中暴漏的JNI函式都被extern"C"程式碼塊包裹,函式的返回值的前後會有兩個巨集:JNIEXPORT 和JNICALL。函式名也必須遵循一定的格式:Java_package_class_function。在Java中你可以像呼叫其他Java函式一樣呼叫JNI函式,例如:
int result = initTracker();
假設在ImageTargets.java中有函式:
public int getTextureCount() { return mTextures.size(); }
你如果想在C++中呼叫上面的函式,首先需要從ImageTargets物件查詢隸屬的ImageTargets類,然後在ImageTargets類中查詢getTextureCount 函式,具體實現的C++程式碼如下:
JNIEXPORT void JNICALL Java_com_qualcomm_QCARSamples_ImageTargets_ImageTargets_initApplicationNative( JNIEnv* env, jobject obj, jint width, jint height) { ... jclass activityClass = env->GetObjectClass(obj); jmethodID getTextureCountMethodID = env->GetMethodID(activityClass, "getTextureCount", "()I"); if (getTextureCountMethodID == 0) { LOG("Function getTextureCount() not found."); return; } textureCount = env->CallIntMethod(obj, getTextureCountMethodID); ... }
下面解釋一下GetMethodID 函式的最後一個引數“()I”:
將Java函式的引數型別依次放在小括號內;
在小括號後面新增返回值型別;
例如(IF)Z代表入參一個Int一個Float,返回值為Boolean
3.在非JNI函式中呼叫Java程式碼
為了在C++中呼叫Java程式碼,我們需要JNIEnv物件和Jobject,在JNI函式中這沒有問題,因為JNIEnv 和Jobject為前兩個入參,但是如果你想在其他的非JNI函式中呼叫Java函式,就需要將JNIEnv 和Jobject儲存為全域性變數。
但是將JNIEnv 儲存為全域性變數是非常危險的,因為JNIEnv不是執行緒安全的,下面的程式碼演示了一種安全的方法來使用JNIEnv,那就是使用NewGlobalRef來對Jclass和Jobject做全域性的引用:
JavaVM* javaVM = NULL;
jclass activityClass;
jobject activityObj;
JNIEXPORT void JNICALL
Java_com_qualcomm_QCARSamples_ImageTargets_ImageTargets_initApplicationNative(
JNIEnv* env, jobject obj, jint width, jint height)
{
env->GetJavaVM(&javaVM);
jclass cls = env->GetObjectClass(obj);
activityClass = (jclass) env->NewGlobalRef(cls);
activityObj = env->NewGlobalRef(obj);
}
void myNativeMethod()
{
JNIEnv *env;
javaVM->AttachCurrentThread(&env, NULL);
jmethodID method = env->GetMethodID(activityClass, "myJavaMethod", "()V");
env->CallVoidMethod(activityObj, method);
}