JNI/NDK开发 进阶(一) JNI异常处理 及 NDK 异常处理
原创
©著作权归作者所有:来自51CTO博客作者jacklicto的原创作品,请联系作者获取转载授权,否则将追究法律责任
在java的编程中,我们经常会遇到各种的异常,也会处理各种的异常。处理异常在java中非常简单,我们通常会使用try-catch-finally来处理,也可以使用throw简单抛出一个异常。
下面说一下jni编程的时候如何处理异常:
注意:只在Release 编译模式下有效 Build Variant:release ,如果是Debug JNI 异常Android好像并不捕获,仍可能崩溃。
jni处理异常流程:
- 我们首先要在有可能产生异常的地方检测异常
- 处理异常
是的,我觉得异常的处理就是可以简单的总结为两步,在异常处理中我们通常会打印栈信息等。在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());
}