1. 程式人生 > >使用JNI在JAVA和C++之間進行互動操作

使用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();



2.在JNI函式中呼叫Java程式碼
假設在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);
}