原文链接地址:
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);
}