JNI/NDK开发 进阶(一) JNI异常处理 及 NDK 异常处理

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

 

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

恋恋西风

up up up

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值