我想要一种在JNI代码中抛出异常的一致而简单的方法;处理链式异常的东西(隐含地来自env-> ExceptionOccurred方法,或显式地通过参数,无论哪种方式都是好的)并且每次我想要这样做时都会让我查找构造函数。所有上述内容最好都是C语言,尽管我可以根据需要从C ++中翻译它。
SO上的任何人都有这样的东西可以分享吗?
答案 0 :(得分:45)
我们只是为我们想要抛出的每种类型的异常编写实用程序方法。以下是一些例子:
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 );
}
这样,很容易找到它们,你的代码完成编辑器将帮助你输入它们,你可以传递简单的参数。
我确信你可以扩展它来处理链式异常或其他更复杂的方法。这足以满足我们的需求。
答案 1 :(得分:21)
我只使用2行:
sprintf(exBuffer, "NE%4.4X: Caller can %s %s print", marker, "log", "or");
(*env)->ThrowNew(env, (*env)->FindClass(env, "java/lang/Exception"), exBuffer);
产地:
Exception in thread "main" java.lang.Exception: NE0042: Caller can log or print.
答案 2 :(得分:6)
我的代码以Java开头,调用C ++,然后再调用Java来查找,获取和设置字段值。
如果有人在寻找C ++方法找到这个页面,我将继续讨论:
我现在正在做的是用C ++ try / catch块包装我的JNI方法体,
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 */
}
}
其中PendingException被简单地声明:
class PendingException {};
我在从C ++调用任何JNI后调用以下方法,因此如果Java异常状态指示错误,我将立即保释并让正常的Java异常处理将(Native方法)行添加到堆栈跟踪,同时让C ++有机会在展开时清理:
PendingException PENDING_JNI_EXCEPTION;
void throwIfPendingException(JNIEnv* env)
{
if (env->ExceptionCheck()) {
throw PENDING_JNI_EXCEPTION;
}
}
对于失败的env-> GetFieldId()调用,我的Java堆栈跟踪如下所示:
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)
如果我调用抛出的Java方法,那就非常相似了:
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)
我不能谈论将Java异常包装在C ++中的另一个Java异常中,我认为这是你问题的一部分 - 我没有发现需要这样做 - 但如果我这样做,我要么使用本机方法的Java级包装器来做这件事,或者只是扩展我的异常抛出方法以获取jthrowable并用丑陋的东西替换env-> ThrowNew()调用:不幸的是Sun没有提供版本的扔了一个jthrowable的ThrowNew。
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);
}
我不会考虑缓存(异常)类构造函数引用,因为异常不应该是通常的控制流机制,因此如果它们很慢则无关紧要。我想,无论如何,查找都不是非常慢,因为Java可能会为这种事情进行自己的缓存。
答案 3 :(得分:0)
对于需要像以前一样需要更多解释的人,我将给出一个更完整,更笼统的答案。
首先很高兴使用Throw Exception
设置方法,以便IDE会请求try / catch。
public native int func(Param1, Param2, Param3) throws IOException;
由于this,我决定在IOException
上选择Exception
。
JNIEXPORT int JNICALL Java_YourClass_func
(int Param1, int Param2, int Param3) {
if (Param3 == 0) { //something wrong
jclass Exception = env->FindClass("java/lang/Exception");
env->ThrowNew(Exception, "Can't divide by zero."); // Error Message
}
return (Param1+Param2)/Param3;
}