JNI manteniendo una referencia global a un objeto, accediendo a ella con otros métodos JNI. Mantener un objeto C ++ activo en varias llamadas JNI

Estoy apenas comenzando con JNI y tengo un problema siguiente.

Tengo una biblioteca de C ++ que tiene una clase simple. Tengo tres métodos JNI llamados desde el proyecto Java Android que, instatiate dicha clase, llaman a un método en la clase instanciada, y la destruyen, respectivamente. Mantengo una referencia global a este objeto, por lo que estaría disponible para mí en los otros dos métodos JNI.

Sospecho que no puedo hacer esto. Cuando ejecuto la aplicación, obtengo un error de tiempo de ejecución (utilizado referencia obsoleto), y sospecho que esto es porque el refernce global no es válido en las llamadas posteriores a otros métodos JNI.

¿Es la única manera de lograr lo que quiero (tener el objeto en vivo a través de múltiples llamadas JNI), para pasar de nuevo el puntero a la instancia de la clase de nuevo a Java, mantenerlo alrededor de allí, y luego pasar de nuevo a las funciones JNI? Si es así, está bien, quiero asegurarme de que no puedo hacerlo con una referencia global, y no solo estoy perdiendo algo.

He leído la documentación y los capítulos sobre las referencias globales / locales en JNI, pero parece que sólo se aplica a las clases Java, y no a mis propias clases nativas de C ++, o estoy equivocado.

Aquí está el código si mi descripción no está clara (resumiendo, me pregunto si este mecanismo de objetos persistentes funcionará en absoluto):

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(); } 

}

Encabezado 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 cuerpo:

 #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; } 

Encabezado de C ++:

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

C ++ cuerpo:

 #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; } 

Gracias

El problema con su implementación es el miembro de datos jstring . NewStringUTF() crea un objeto String Java para volver de un método JNI. Por lo tanto, es una referencia local de Java. Sin embargo, está almacenando esto dentro de un objeto C ++ y tratar de utilizarlo a través de llamadas JNI.

Debe mantener una mejor distinción entre objetos C ++, Java y la interfaz JNI entre. En otras palabras, el C ++ debería utilizar una forma C ++ de almacenar cadenas (como std::string ). La implementación JNI de InvokeNativeFunction() debería convertirlo en jstring como valor devuelto.

PS: Hay casos que requieren la implementación de C ++ para mantener referencias a objetos Java (o al revés). Pero hace que el código más complejo y propenso a errores de memoria si no se hace bien. Así que sólo debe usarlo donde realmente añade valor.

No puedes hacer eso. Las referencias de objetos, incluidas las referencias de clases, no son válidas en las llamadas JNI. Necesita leer la sección de la Especificación JNI sobre referencias locales y globales.

No pude encontrar una buena respuesta en SO sobre este tema, así que aquí está mi solución para mantener objetos vivos en C + + con el fin de hacer referencia a ellos desde múltiples llamadas JNI:

Java

En el lado de Java, estoy creando una clase con un puntero long para guardar una referencia al objeto de C ++. Envolviendo los métodos C ++ en una clase Java, nos permite usar los métodos C ++ en múltiples actividades. Tenga en cuenta que estoy creando el objeto C ++ en el constructor, y estoy borrando el objeto en cleanup. Esto es muy importante para evitar fugas de memoria:

 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 ++

En el lado de C ++, estoy definiendo funciones para crear, modificar y eliminar el objeto. Es importante mencionar que tenemos que usar new y delete para almacenar el objeto en la memoria HEAP para mantenerlo vivo a lo largo del ciclo de vida de las instancias de clase Java. También estoy almacenando el puntero a CppObject directamente en el JavaClass , utilizando getFieldId , SetLongField y GetLongField :

 // 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; } } 

NOTAS:

  • A diferencia de Java, C ++ no tiene recolección de basura, y el objeto vivirá en la memoria HEAP, hasta que use delete .
  • Estoy usando GetFieldID , SetLongField y GetLongField para almacenar la referencia de objeto desde C ++, pero también podría almacenar el puntero de objeto jlong de Java como se discute en otras respuestas.
  • En mi código final, implementé la clase JavaObject como Parcelable para pasar mi clase a múltiples actividades usando Intent con extras.
  • Uso de pjsip con android
  • Devuelve jbyteArray desde c nativo en Android
  • Decodificación y manipulación de imágenes mediante JNI en android
  • ¿Cómo utilizar NDK? Comenzando con la aplicación "hola mundo"
  • Error de compilación con ndk jni para curl
  • ¿Cómo arreglar el archivo libgnustl_shared.so duplicado que en sdks de terceros?
  • Android JNI nativo C función llamada mata actividad
  • Android JNI - Llamar AttachCurrentThread sin DetachCurrentThread
  • Defragment H264 NAL corriente (originalmente 1722 avb paquetes)
  • No se puede hacer JNI llamar desde c ++ a java en android lollipop usando jni
  • ¿Cómo podemos agregar la imagen animada / simple como una capa superior al video y exportarla como un solo video en Android?
  • FlipAndroid es un fan de Google para Android, Todo sobre Android Phones, Android Wear, Android Dev y Aplicaciones para Android Aplicaciones.