在java的编程中,我们经常会遇到各种的异常,也会处理各种的异常。处理异常在java中非常简单,我们通常会使用try-catch-finally来处理,也可以使用throw简单抛出一个异常。

下面说一下jni编程的时候如何处理异常:

注意:只在Release 编译模式下有效  Build Variant:release  ,如果是Debug  JNI 异常Android好像并不捕获,仍可能崩溃。

jni处理异常流程:

  1. 我们首先要在有可能产生异常的地方检测异常
  2. 处理异常 
    是的,我觉得异常的处理就是可以简单的总结为两步,在异常处理中我们通常会打印栈信息等。在jni编程中,异常处理的思路应该也与之类似,过程可用下图表示: 

    在jni中我么需要手动清除异常。因此,jni中异常的处理应该严格遵循:检查->处理->清除的流程,在图中我把清除也认为是处理的其中一步了。

 

JNI中异常处理函数:

jni.h中有如下相关的函数的定义:

jint        (*Throw)(JNIEnv*, jthrowable);
jint (*ThrowNew)(JNIEnv *, jclass, const char *);
jthrowable (*ExceptionOccurred)(JNIEnv*);
void (*ExceptionDescribe)(JNIEnv*);
void (*ExceptionClear)(JNIEnv*);
void (*FatalError)(JNIEnv*, const char*);

此外,还有一个函数和他们没放在一起:

jboolean    (*ExceptionCheck)(JNIEnv*);

单从名字上我们可以知道用于检测异常的发生的函数有:

  • (ExceptionCheck)(JNIEnv);
  • (ExceptionOccurred)(JNIEnv); 
    用于清理异常的有:
  • (ExceptionClear)(JNIEnv); 
    用于跑出异常的有:
  • (Throw)(JNIEnv, jthrowable);
  • (ThrowNew)(JNIEnv , jclass, const char *);
  • (FatalError)(JNIEnv, const char*); 
    下面对以上函数做一个简单的介绍: 
    1> ExceptionCheck:检查是否发生了异常,若有异常返回JNI_TRUE,否则返回JNI_FALSE 
    2> ExceptionOccurred:检查是否发生了异常,若用异常返回该异常的引用,否则返回NULL 
    3> ExceptionDescribe:打印异常的堆栈信息 
    4> ExceptionClear:清除异常堆栈信息 
    5> ThrowNew:在当前线程触发一个异常,并自定义输出异常信息 
    6> Throw:丢弃一个现有的异常对象,在当前线程触发一个新的异常 
    7> FatalError:致命异常,用于输出一个异常信息,并终止当前VM实例(即退出程序)

测试异常:

JNI 代码

void Java_cn_com_myapplication_MainActivity_stringFromJNI(JNIEnv *env, jobject obj)
{

jclass jcls = env->FindClass("java/lang/Exception");
env->ThrowNew(jcls,"jni error");

}

Android 代码:

public native String stringFromJNI() throws Exception;

 

调用方法

protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);

// Example of a call to a native method
TextView tv = (TextView) findViewById(R.id.sample_text);
try{
String getStr = stringFromJNI();
tv.setText(getStr);
}catch (Exception e)
{
Log.e("jni error",e.getMessage());
}
}

 

 在 Release 下可以正常捕捉到异常;一般我们需要将JNI 异常进行统一处理,并及时返回,以免发生其它异常;

可以在可能有异常的语句后加此函数

int jniCheckException(JNIEnv *env,string msg) {
jthrowable ex = (env)->ExceptionOccurred();
if (ex) {
(env)->ExceptionDescribe();
(env)->ExceptionClear();
(env)->DeleteLocalRef( ex);
jclass cls = env->FindClass("java/lang/Exception");
env->ThrowNew(cls, msg.c_str());

env->DeleteLocalRef(cls);
return 1;
}

return 0;
}

// use
if(jniCheckException(env,"xxxxxxx exception")
{
return 1;// 不再向下执行;
}

jmethodID jniGetStaticMethodID(JNIEnv *env, jclass cls, const char *name, const char *signature) {
jmethodID method = (env)->GetStaticMethodID( cls, name, signature);
if (method == NULL) {
LOGE("Method %s %s not found", name, signature);
char temp[128]={0};
sprintf(temp,"Method %s %s not found", name, signature);
jniCheckException(env,temp);
}
return method;
}

注意:

  1.不能够捕捉多个 JNI 异常,如上面发生了二个或多个JNI异常,仍可能会崩溃,所以需要在每个可能崩溃的语句后使用;

  这里感觉有些鸡肋吧,如果只有一个 jni 异常,java 也可捕捉到。

  2.只能捕捉 JNI 异常,并不能捕捉 NDK C/C++ 语言的异常

JNI 异常主要处理:

1、当调用一个JNI函数后,必须先检查、处理、清除异常后再做其它 JNI 函数调用,否则会产生不可预知的结果。
2、一旦发生异常,立即返回,让调用者处理这个异常。或 调用 ExceptionClear 清除异常,然后执行自己的异常处理代码。

NDK  异常处理

void Java_cn_com_myapplication_MainActivity_stringFromJNI(JNIEnv *env, jobject obj)
{


char * tNull= nullptr;
string rstr = tNull;

// jni exit check and clear Exception
jniCheckException(env,"NDK EXCEPTION");
}

上面方便并不能捕捉到异常,因为此异常非JNI 异常;

可使用 C 方式捕捉

void Java_cn_com_myapplication_MainActivity_stringFromJNI(JNIEnv *env, jobject obj)
{

try{

char * tNull= nullptr;
string rstr = tNull;
}
catch (const std::exception& e){
jniCheckException(env,e.what());
return;// return
}
catch (...)
{
jniCheckException(env,e.what());
return;// return
}

}

如果对性能无要求,可能崩溃的不明确可以在整理个函数捕捉;

定义宏:

#define BEGIN_JNI_TRY_CATCH(env)   try{

#define END_JNI_TRY_CATCH(env) } catch (const std::exception& e){\
jniCheckException(env,e.what()); \
}\
catch (...)\
{\
jniCheckException(env,"ndk error"); \
}

样例:

extern "C" JNIEXPORT jstring JNICALL
Java_cn_com_myapplication_MainActivity_stringFromJNI(JNIEnv *env, jobject obj)
{

BEGIN_JNI_TRY_CATCH(env)

/ code
char * tNull= nullptr;
string rstr = tNull;
/
END_JNI_TRY_CATCH(env)

return env->NewStringUTF(hello.c_str());
}