当前位置:  开发笔记 > 编程语言 > 正文

在JNI代码中抛出异常的最佳方法?

如何解决《在JNI代码中抛出异常的最佳方法?》经验,为你挑选了3个好方法。

我想要一种在JNI代码中抛出异常的一致而简单的方法; 处理链式异常的东西(隐式地来自env-> ExceptionOccurred方法,或显式地通过参数,无论哪种方式都是好的)并且每次我想要这样做时都会让我查找构造函数.所有上述内容最好都是C语言,尽管我可以根据需要从C++中翻译它.

SO上有没有人可以分享这样的东西?



1> Steven M. Ch..:

我们只为我们想要抛出的每种类型的异常编写实用程序方法.这里有些例子:

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

这样,很容易找到它们,您的代码完成编辑器将帮助您输入它们,并且您可以传递简单的参数.

我相信你可以扩展它来处理链式异常或其他更复杂的方法.这足以满足我们的需求.


刚发现这个,谢谢.但是,`throwNoClassDefError`中的错误条件不会导致无限递归和不可避免的堆栈溢出吗?我承认,它应该永远不会发生,但这似乎不是处理它的适当方式.也许可以回到`java.lang.error`和`abort()`或其他东西,如果这不起作用.
抱歉,但是没有人能解释为什么在找不到“ java / lang / NoClassDefFoundError”类的情况下throwNoClassDefError函数不会无限递归吗?

2> Java42..:

我只使用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.


@ android.weasel:Dude,它是StackOverflow上的示例代码,用于说明ThrowNew API.它不打算成为任务关键型服务器中的生产代码.让这家伙休息一下......
我明白了捕获java.lang.Exception被认为是不好的做法:我正在抛出com.mycompany.JniException,我想要一个普通的JNI失败案例.

3> android.weas..:

我的代码以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没有提供一个版本的ThrowNew jthrowable.

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可能会为这种事情进行自己的缓存.

推荐阅读
个性2402852463
这个屌丝很懒,什么也没留下!
DevBox开发工具箱 | 专业的在线开发工具网站    京公网安备 11010802040832号  |  京ICP备19059560号-6
Copyright © 1998 - 2020 DevBox.CN. All Rights Reserved devBox.cn 开发工具箱 版权所有