Android JNI Development Series (XIII) JNI exception handling

Keywords: Mobile Java Android

JNI exception handling

Difference between JNI exception and JAVA exception handling

  • JAVA has exception handling mechanism, but JNI does not.
  • If exceptions are not captured in JAVA, subsequent code will not execute, and JNI will execute
  • JAVA compile-time exceptions are those that declare an exception in the method display. The compiler requires the capture that must be displayed when calling.

Examples of JNI Exceptions

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

//------- partition line

#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) {// Check if the JNI call throws an exception
//        (*env)->ExceptionDescribe(env);
//// (* env) - > ExceptionClear (env); // Clears the exception that was thrown, and does not print the stack information of the exception at the Java layer
//          If not cleared, the exception stack information thrown by the subsequent call to ThrowNew overrides the previous exception information.
//// (* env) - > ThrowNew (env, (* env) - > FindClass (env, "java/lang/Exception"), "Exceptions thrown by JNI!" ";// After clearing, you can throw your own exception, if not cleared, the exception stack information thrown by the subsequent call to ThrowNew will override the previous exception information.
//        //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);

}


Analysis:

  • First, in Java code, we define a TestException class, which defines a native method and a common method, in which an exception ArithmeticException is normally thrown.
  • The exceptionCallback method is called in the native code and checked for exceptions (memory checking and code commenting can be done in two ways).

summary

  • When a JNI function is called, it must first check, process and clear exceptions before making other JNI function calls, otherwise unpredictable results will be produced.

  • Once an exception occurs, it returns immediately and lets the caller handle the exception. Or call Exception Clear to clear the exception and execute your own exception handling code.

  • Summary of JNI functions related to exception handling:

    • ExceptionCheck: Check if an exception has occurred and return JNI_TRUE if there is an exception, or JNI_FALSE if there is an exception
    • Exception Occurred: Check if an exception has occurred and return a reference to the exception with an exception, or NULL
    • ExceptionDescribe: Print stack information for exceptions
    • Exception Clear: Clear exception stack information
    • ThrowNew: Triggers an exception in the current thread and customizes the output of exception information jint (JNICALL *ThrowNew) (JNIEnv *env, jclass clazz, const char *msg);
    • Throw: Discard an existing exception object and trigger a new exception in the current thread jint (JNICALL *Throw) (JNIEnv *env, jthrowable obj);
    • FatalError: A fatal exception that is used to output an exception message and terminate the current VM instance (that is, exit the program) void (JNICALL *FatalError) (JNIEnv *env, const char *msg);

Posted by Micah D on Tue, 29 Jan 2019 00:21:15 -0800