java - una - ¿La mejor manera de lanzar excepciones en el código JNI?
tipos de excepciones en java netbeans (3)
Simplemente uso 2 líneas:
sprintf(exBuffer, "NE%4.4X: Caller can %s %s print", marker, "log", "or");
(*env)->ThrowNew(env, (*env)->FindClass(env, "java/lang/Exception"), exBuffer);
Produce:
Exception in thread "main" java.lang.Exception: NE0042: Caller can log or print.
Me gustaría una forma consistente y simple de lanzar excepciones en el código JNI; algo que maneja excepciones encadenadas (implícitamente desde el método env-> ExceptionOccurred, o explícitamente por parámetros, de cualquier manera es bueno) y me ahorra buscar constructores cada vez que quiero hacer esto. Todo lo anterior está preferiblemente en C, aunque podría traducirlo de C ++ cuando lo necesite.
¿Alguien en SO tiene algo como esto que puedan compartir?
Mi código comienza en Java, invoca C ++, que luego invoca Java de nuevo para cosas como buscar, obtener y establecer valores de campo.
En caso de que alguien que busca un enfoque C ++ encuentre esta página, voy a arar con esto:
Lo que ahora estoy haciendo es envolver mis cuerpos del método JNI con un bloque try / catch de C ++,
JNIEXPORT void JNICALL Java_com_pany_jni_JNIClass_something(JNIEnv* env, jobject self)
{
try
{
... do JNI stuff
// return something; if not void.
}
catch (PendingException e) // (Should be &e perhaps?)
{
/* any necessary clean-up */
}
}
donde PendingException se declara trivialmente:
class PendingException {};
y estoy invocando el siguiente método después de invocar cualquier JNI de C ++, entonces si el estado de excepción de Java indica un error, rescataré inmediatamente y dejaré que el manejo normal de excepciones de Java agregue la línea (método nativo) al rastreo de la pila, mientras dando al C ++ la oportunidad de limpiar mientras se desenrolla:
PendingException PENDING_JNI_EXCEPTION;
void throwIfPendingException(JNIEnv* env)
{
if (env->ExceptionCheck()) {
throw PENDING_JNI_EXCEPTION;
}
}
Mi rastro de la pila de Java se parece a esto para una llamada env- fallida GetFieldId ():
java.lang.NoSuchFieldError: no field with name=''opaque'' signature=''J'' in class Lcom/pany/jni/JniClass;
at com.pany.jni.JniClass.construct(Native Method)
at com.pany.jni.JniClass.doThing(JniClass.java:169)
at com.pany.jni.JniClass.access$1(JniClass.java:151)
at com.pany.jni.JniClass$2.onClick(JniClass.java:129)
at android.view.View.performClick(View.java:4084)
y bastante similar si llamo a un método Java que arroja:
java.lang.RuntimeException: YouSuck
at com.pany.jni.JniClass.fail(JniClass.java:35)
at com.pany.jni.JniClass.getVersion(Native Method)
at com.pany.jni.JniClass.doThing(JniClass.java:172)
No puedo hablar de incluir la excepción de Java dentro de otra excepción de Java dentro de C ++, que creo que es parte de su pregunta, no he encontrado la necesidad de hacerlo, pero si lo hiciera, lo haría con un contenedor Java-level alrededor de los métodos nativos, o simplemente extender mis métodos de lanzamiento de excepciones para tomar un jthrowable y reemplazar el env-> ThrowNew () llamada con algo feo: es desafortunado Sun no proporcionó una versión de ThrowNew que tomó una jwrowable.
void impendNewJniException(JNIEnv* env, const char *classNameNotSignature, const char *message)
{
jclass jClass = env->FindClass(classNameNotSignature);
throwIfPendingException(env);
env->ThrowNew(jClass, message);
}
void throwNewJniException(JNIEnv* env, const char* classNameNotSignature, const char* message)
{
impendNewJniException(env, classNameNotSignature, message);
throwIfPendingException(env);
}
No consideraría las referencias de constructor de clase de caché (excepción) porque se supone que las excepciones no son un mecanismo de flujo de control habitual, por lo que no debería importar si son lentas. Me imagino que la búsqueda no es terriblemente lenta de todos modos, ya que Java presumiblemente hace su propio almacenamiento en caché para ese tipo de cosas.
Solo codificamos los métodos de utilidad para cada uno de los tipos de excepciones que deseamos lanzar. Aquí hay unos ejemplos:
jint throwNoClassDefError( JNIEnv *env, char *message )
{
jclass exClass;
char *className = "java/lang/NoClassDefFoundError";
exClass = (*env)->FindClass( env, className);
if (exClass == NULL) {
return throwNoClassDefError( env, className );
}
return (*env)->ThrowNew( env, exClass, message );
}
jint throwNoSuchMethodError(
JNIEnv *env, char *className, char *methodName, char *signature )
{
jclass exClass;
char *exClassName = "java/lang/NoSuchMethodError" ;
LPTSTR msgBuf;
jint retCode;
size_t nMallocSize;
exClass = (*env)->FindClass( env, exClassName );
if ( exClass == NULL ) {
return throwNoClassDefError( env, exClassName );
}
nMallocSize = strlen(className)
+ strlen(methodName)
+ strlen(signature) + 8;
msgBuf = malloc( nMallocSize );
if ( msgBuf == NULL ) {
return throwOutOfMemoryError
( env, "throwNoSuchMethodError: allocating msgBuf" );
}
memset( msgBuf, 0, nMallocSize );
strcpy( msgBuf, className );
strcat( msgBuf, "." );
strcat( msgBuf, methodName );
strcat( msgBuf, "." );
strcat( msgBuf, signature );
retCode = (*env)->ThrowNew( env, exClass, msgBuf );
free ( msgBuf );
return retCode;
}
jint throwNoSuchFieldError( JNIEnv *env, char *message )
{
jclass exClass;
char *className = "java/lang/NoSuchFieldError" ;
exClass = (*env)->FindClass( env, className );
if ( exClass == NULL ) {
return throwNoClassDefError( env, className );
}
return (*env)->ThrowNew( env, exClass, message );
}
jint throwOutOfMemoryError( JNIEnv *env, char *message )
{
jclass exClass;
char *className = "java/lang/OutOfMemoryError" ;
exClass = (*env)->FindClass( env, className );
if ( exClass == NULL ) {
return throwNoClassDefError( env, className );
}
return (*env)->ThrowNew( env, exClass, message );
}
De esta forma, es fácil encontrarlos, su editor de finalización de código lo ayudará a escribirlos, y puede pasar parámetros simples.
Estoy seguro de que podría ampliar esto para manejar excepciones encadenadas u otros enfoques más complicados. Esto fue suficiente para satisfacer nuestras necesidades.