NDK-异常处理与动态注册

/ 0评 / 0

NDK异常处理

jni异常一般发生在C调用java方法的时候,分为两种情况

异常处理方法

检查异常

我们可以使用ExceptionCheck()方法判断是否出现了异常,如果出现了异常,会返回true。

使用ExceptionClear()则会清除异常,让程序继续执行下去,当然,也可以使用ExceptionDescribe(),这个方法会在日志中打印异常堆栈

jfieldID jfieldId = env->GetFieldID(thiz, "testEx", "I");
if(env->ExceptionCheck()){
    LOGD("异常发生了");
    env->ExceptionDescribe();
    env->ExceptionClear();
}

也可以使用ExceptionOccurred()方法判断,如果出现了异常,则会返回jthrowable对象,否则返回NULL

jthrowable jthrowable1 = env->ExceptionOccurred();
if (jthrowable1) {
    env->ExceptionClear();
}

主动抛出异常

我们也可以使用ThrowNew()方法主动抛出异常,参数一是异常的jclass对象

env->ThrowNew(env->FindClass("java/lang/Exception"), "native ex");

注意:ThrowNew()抛出的异常在java层可以try catch到

NDK函数注册

NDK静态注册

通过函数名去匹配对应的native方法,格式如下

extern "C"
JNIEXPORT 返回值类型 JNICALL Java_包名_类名_函数名(JNIEnv * env, jobject obj)

//如下
extern "C"
JNIEXPORT void JNICALL Java_com_example_myapplication_MainActivity_voiceChangeNative
        (JNIEnv *env, jobject obj, jint type, jstring path) {
}

缺点:

1、函数名比较长

2、写死了包名+类名,不够灵活

3、运行期才会匹配jni函数,性能低于动态注册

NDK动态注册

动态注册时机

在JNI中,有两个函数,分别是JNI_OnLoad()和JNI_OnUnload(),分别在加载库和卸载库的时候调用,定义在头文件jni.h

JNIEXPORT jint JNI_OnLoad(JavaVM* vm, void* reserved);
JNIEXPORT void JNI_OnUnload(JavaVM* vm, void* reserved);

动态注册代码一般分为两步步

示例如下

java代码

public class T {

    public native static void staticRegisterCall();

    public native static int staticRegisterCall(String msg);
}

C代码

const char *class_name = "com/example/myapplication/T";

JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM *jvm, void *reserved) {
    LOGD("JNI_OnLoad");
    JNIEnv *env = NULL;

    if (jvm->GetEnv((void **) &env, JNI_VERSION_1_6) != JNI_OK) {
        return -1;
    }

    jclass t_class = env->FindClass(class_name);

    //函数指针使用函数名,括号不能丢掉
    JNINativeMethod methods[] = {
            {"staticRegisterCall", "()V",(void *) (dynamicMethod01)},
            {"staticRegisterCall", "(Ljava/lang/String;)I", (int *) (dynamicMethod02)}
    };

    env->RegisterNatives(t_class,
                         methods,
                         sizeof(methods) / sizeof(JNINativeMethod));

    return JNI_VERSION_1_6;
}

JNIEXPORT void JNI_OnUnload(JavaVM *vm, void *reserved) {
    LOGD("JNI_OnUnload");
}

env->RegisterNatives方法说明

// clazz    代表需要被注册的类
// methods  代表java方法与native方法的对应关系
// nMethods 代表前面methods参数的个数
jint RegisterNatives(jclass clazz, const JNINativeMethod* methods,jint nMethods)

JNINativeMethod说明

typedef struct {
    const char* name;       //java函数的名字
    const char* signature;  //java函数的签名
    void*       fnPtr;      //native函数指针
} JNINativeMethod;

native方法定义说明

如果native方法不需要JNIEnv、jclass/jobject,那么也可以直接不写。如下所示

// 也OK  如果你用不到  JNIEnv jobject ,可以不用写
void dynamicMethod01() {
    LOGD("我是动态注册的函数 dynamicMethod01...");
}

int dynamicMethod02(JNIEnv *env, jclass thiz, jstring valueStr) { // 也OK
    const char *text = env->GetStringUTFChars(valueStr, nullptr);
    LOGD("我是动态注册的函数 dynamicMethod02... %s", text);
    env->ReleaseStringUTFChars(valueStr, text);
    return 200;
}

发表回复

您的电子邮箱地址不会被公开。