JNI保持一个对象的全局引用,用其他的JNI方法访问它。 在多个JNI调用中保持C ++对象的生命

我刚刚开始与JNI,我有以下问题。

我有一个C ++库,有一个简单的类。 我从Java Android项目调用了三个JNI方法,安装所述类,分别在实例化的类上调用方法并销毁它。 我保留了这个对象的全局引用,所以在另外两个JNI方法中我可以使用它。

我怀疑我不能这样做。 当我运行应用程序,我得到一个运行时错误(使用引用陈旧),我怀疑这是因为全局引用在随后调用其他JNI方法是无效的。

是唯一的方法来实现我想要的东西(让对象存在于多个JNI调用中),实际上将指针返回给Java,保留在那里,然后将它传递回JNI函数? 如果是这样,那很好,我想确保我不能用全局引用来做,而且我不会错过一些东西。

我已经阅读了关于JNI中全局/局部引用的文档和章节,但似乎只适用于Java类,而不适用于我自己的本地C ++类,或者我错了。

这里是代码,如果我的描述不清楚(总结,我想知道这种持久对象的机制是否可以工作):

Java的:

package com.test.ndktest; import android.app.Activity; import android.os.Bundle; import android.app.AlertDialog; public class NDKTestActivity extends Activity { static { System.loadLibrary("ndkDTP"); } private native void initializeTestClass(); private native void destroyTestClass(); private native String invokeNativeFunction(); @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.main); initializeTestClass(); String hello = invokeNativeFunction(); destroyTestClass(); new AlertDialog.Builder(this).setMessage(hello).show(); } 

}

JNI头文件:

 extern "C" { jstring Java_com_test_ndktest_NDKTestActivity_initializeTestClass(JNIEnv* env, jobject javaThis); jstring Java_com_test_ndktest_NDKTestActivity_destroyTestClass(JNIEnv* env, jobject javaThis); jstring Java_com_test_ndktest_NDKTestActivity_invokeNativeFunction(JNIEnv* env, jobject javaThis); }; 

JNI身体:

 #include <string.h> #include <jni.h> #include <ndkDTP.h> //JNI header #include <TestClass.h> //C++ header TestClass *m_globalTestClass; void Java_com_test_ndktest_NDKTestActivity_initializeTestClass(JNIEnv* env, jobject javaThis) { m_globalTestClass = new TestClass(env); } void Java_com_test_ndktest_NDKTestActivity_destroyTestClass(JNIEnv* env, jobject javaThis) { delete m_globalTestClass; m_globalTestClass = NULL; } jstring Java_com_test_ndktest_NDKTestActivity_invokeNativeFunction(JNIEnv* env, jobject javaThis) { jstring testJS = m_globalTestClass->getString(); return testJS; } 

C ++头文件:

 class TestClass { public: jstring m_testString; JNIEnv *m_env; TestClass(JNIEnv *env); jstring getString(); }; 

C ++主体:

 #include <jni.h> #include <string.h> #include <TestClass.h> TestClass::TestClass(JNIEnv *env){ m_env = env; m_testString = m_env->NewStringUTF("TestClass: Test string!"); } jstring TestClass::getString(){ return m_testString; } 

谢谢

Solutions Collecting From Web of "JNI保持一个对象的全局引用,用其他的JNI方法访问它。 在多个JNI调用中保持C ++对象的生命"

你实现的问题是jstring数据成员。 NewStringUTF()创build一个Java String对象以从JNI方法返回。 所以这是一个Java本地引用。 但是,您将其存储在C ++对象中,并尝试在JNI调用中使用它。

您应该更好地区分C ++对象,Java和它们之间的JNI接口。 换句话说,C ++应该使用C ++方式来存储string(比如std::string )。 InvokeNativeFunction()的JNI实现应该将其转换为一个jstring作为返回值。

PS:有些情况下需要C ++实现来保持对Java对象的引用(或者其他方法)。 但是如果做得不对,它会使代码更复杂,并且容易出现内存错误。 所以你应该只在真正增加价值的地方使用它。

你不能这样做。 对象引用(包括类引用)在JNI调用中无效。 您需要阅读关于本地和全局引用的JNI规范部分。

我在这个主题上找不到一个好的答案,所以这里是我的解决scheme,以保持对象在C + +活着,以引用他们从多个JNI调用:

Java的

在Java方面,我用long指针创build一个类,以保持对C ++对象的引用。 在Java类中包装C ++方法,允许我们在多个活动中使用C ++方法。 请注意,我正在构造函数上创buildC ++对象,并且正在删除清理对象。 这对于防止内存泄漏非常重要:

 public class JavaClass { // Pointer (using long to account for 64-bit OS) private long objPtr = 0; // Create C++ object public JavaClass() { createCppObject(); } // Delete C++ object on cleanup public void cleanup() { deleteCppObject(); this.objPtr = 0; } // Native methods public native void createCppObject(); public native void workOnCppObject(); public native void deleteCppObject(); // Load C++ shared library static { System.loadLibrary("CppLib"); } } 

C ++

在C ++方面,我正在定义创build,修改和删除对象的函数。 需要提到的是,我们必须使用newdelete来将对象存储在HEAP内存中,以便在Java类实例的整个生命周期中保持活动状态。 我也使用getFieldIdSetLongFieldGetLongField直接在JavaClass存储指向CppObject的指针:

 // Get pointer field straight from `JavaClass` jfieldID getPtrFieldId(JNIEnv * env, jobject obj) { static jfieldID ptrFieldId = 0; if (!ptrFieldId) { jclass c = env->GetObjectClass(obj); ptrFieldId = env->GetFieldID(c, "objPtr", "J"); env->DeleteLocalRef(c); } return ptrFieldId; } // Methods to create, modify, and delete Cpp object extern "C" { void Java_com_test_jnitest_JavaClass_createCppObject(JNIEnv *env, jobject obj) { env->SetLongField(obj, getPtrFieldId(env, obj), (jlong) new CppObject); } void Java_com_test_jnitest_JavaClass_workOnCppObject(JNIEnv *env, jobject obj) { CppObject* cppObj = (CppObject*) env->GetLongField(obj, getPtrFieldId(env, obj)); // Write your code to work on CppObject here } void Java_com_test_jnitest_JavaClass_deleteCppObject(JNIEnv *env, jobject obj) { CppObject* cppObj = (CppObject*) env->GetLongField(obj, getPtrFieldId(env, obj)); delete cppObj; } } 

笔记:

  • 与Java不同的是,C ++没有垃圾回收function,对象将存在于HEAP内存中,直到使用delete
  • 我正在使用GetFieldIDSetLongFieldGetLongField来存储C ++中的对象引用,但是您也可以像在其他答案中讨论的那样存储来自Java的jlong对象指针。
  • 在我的最终代码中,我实现了JavaObject类作为Parcelable ,以便将我的类传递给使用Intent和extras的多个活动。