Android JNI開發系列(十三) JNI異常處理 原

JNI 異常處理

JNI異常與JAVA處理異常的區別

  • JAVA 有異常處理機制,而JNI沒有
  • 如果JAVA中異常沒有捕獲,後面的代碼不會執行,JNI會執行
  • JAVA編譯時的異常,是在方法顯示的聲明瞭某一個異常,編譯器要求在調用的時候必須顯示的捕獲

JNI異常舉例分析

package org.professor.jni.java;

public class TestException {

    public native void testException();

    public void exceptionCallback() {
        int a = 20 / 0;
        System.out.println("--->" + a);
    }
}

package org.professor.jni;

import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.widget.TextView;

import org.professor.jni.java.TestException;

public class MainActivity extends AppCompatActivity {

    // Used to load the 'native-lib' library on application startup.
    static {
        // lib+native-lib+.so
        //libnative-lib.so
        System.loadLibrary("hello-jni");
    }

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

        TextView tv = (TextView) findViewById(R.id.sample_text);
//        tv.setText(stringFromJNI());
        TestException testException = new TestException();
        try {
            testException.testException();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

#include <jni.h>
#include <android/log.h>

#ifndef ANDROIDJNIDEMO_TEST_THROW_EXCEPTION_H
#define ANDROIDJNIDEMO_TEST_THROW_EXCEPTION_H


#define LOG_D(...) __android_log_print(ANDROID_LOG_DEBUG, LOG_TAG, __VA_ARGS__)
#define LOG_I(...) __android_log_print(ANDROID_LOG_INFO, LOG_TAG, __VA_ARGS__)
#define LOG_W(...) __android_log_print(ANDROID_LOG_WARN, LOG_TAG, __VA_ARGS__)
#define LOG_E(...) __android_log_print(ANDROID_LOG_ERROR, LOG_TAG, __VA_ARGS__)

#ifdef __cplusplus
extern "C" {
#endif
/*
 *
 * Class:     org_professor_jni_java_TestException
 * Method:    occorException
 * Signature: ()V
 */
JNIEXPORT void JNICALL Java_org_professor_jni_java_TestException_testException
        (JNIEnv *, jobject);

#ifdef __cplusplus
}
#endif
#endif //ANDROIDJNIDEMO_TEST_THROW_EXCEPTION_H

//------分割線

#include <jni.h>
#include <test_throw_exception.h>
#include <stdio.h>

#define LOG_TAG "EXCEPTION"


JNIEXPORT void JNICALL
Java_org_professor_jni_java_TestException_testException(JNIEnv *env, jobject instance) {


    jclass exception_clazz = (*env)->FindClass(env, "org/professor/jni/java/TestException");
    if (NULL == exception_clazz) {
        LOG_D("NOT FIND EXCEPTION CLASS");
        return;
    }

    jmethodID test_method_id = (*env)->GetMethodID(env, exception_clazz, "exceptionCallback",
                                                   "()V");
    if (NULL == test_method_id) {
        LOG_E("NOT FIND TEXT METHOD ID");
        return;
    }

    jmethodID default_constructor_method_id = (*env)->GetMethodID(env, exception_clazz, "<init>",
                                                                  "()V");
    if (NULL == default_constructor_method_id) {
        LOG_I("NOT FIND DEFAULT CONSTRUCTOR ID ");
        return;
    }

    jobject test_exception_instance = (*env)->NewObject(env, exception_clazz,
                                                        default_constructor_method_id);
    if (NULL == test_exception_instance) {
        LOG_W("NOT FIND TEST EXCEPTION INSTANCE");
        return;
    }

    (*env)->CallVoidMethod(env, test_exception_instance, test_method_id);

    LOG_E("In C: CALL exceptionCallback Method!");
//    if ((*env)->ExceptionCheck(env)) {  // 檢查JNI調用是否有引發異常
//        (*env)->ExceptionDescribe(env);
////        (*env)->ExceptionClear(env);        // 清除引發的異常,在Java層不會打印異常的堆棧信息
//          如果不清除,後面調用ThrowNew拋出的異常堆棧信息會覆蓋前面的異常信息
////        (*env)->ThrowNew(env, (*env)->FindClass(env, "java/lang/Exception"), "JNI拋出的異常!"); //清除後可以拋出自己的異常 如果不清除,後面調用ThrowNew拋出的異常堆棧信息會覆蓋前面的異常信息
//        //return;
//    }
    jthrowable throwable = (*env)->ExceptionOccurred(env);
    if (NULL != throwable) {
        (*env)->ExceptionDescribe(env);
//        (*env)->ExceptionClear(env);
    }


    LOG_E("In C: CALL exceptionCallback Method! end");

    (*env)->DeleteLocalRef(env, exception_clazz);
    (*env)->DeleteLocalRef(env, test_exception_instance);

}


分析:

  • 首先在Java代碼中,定義一個TestException 類,裏面定義了一個native方法,和一個普通方法,在普通方法裏面正常會拋出一個異常ArithmeticException
  • 在native代碼中調用了exceptionCallback方法,並對其進行異常檢查(可以用兩種方法對其進行內存檢查,代碼註釋)。

總結

  • 當調用一個JNI函數後,必須先檢查、處理、清除異常後再做其它 JNI 函數調用,否則會產生不可預知的結果。

  • 一旦發生異常,立即返回,讓調用者處理這個異常。或 調用 ExceptionClear 清除異常,然後執行自己的異常處理代碼。

  • 異常處理的相關JNI函數總結:

    • ExceptionCheck:檢查是否發生了異常,若有異常返回JNI_TRUE,否則返回JNI_FALSE
    • ExceptionOccurred:檢查是否發生了異常,若用異常返回該異常的引用,否則返回NULL
    • ExceptionDescribe:打印異常的堆棧信息
    • ExceptionClear:清除異常堆棧信息
    • ThrowNew:在當前線程觸發一個異常,並自定義輸出異常信息 jint (JNICALL *ThrowNew) (JNIEnv *env, jclass clazz, const char *msg);
    • Throw:丟棄一個現有的異常對象,在當前線程觸發一個新的異常 jint (JNICALL *Throw) (JNIEnv *env, jthrowable obj);
    • FatalError:致命異常,用於輸出一個異常信息,並終止當前VM實例(即退出程序) void (JNICALL *FatalError) (JNIEnv *env, const char *msg);
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章