八、JNI-JNI补充功能

  • JNI和线程
  • 注册本地方法

#1. JNI和线程

1.1 规约

当编写JNI函数时,有如下规约是必须要遵守的:

  1. JNIEnv是线程私有的,不能跨线程共享。
  2. Local references只在创建它的线程内有效,也不能跨线程共享。
1.2 Monitor Enter and Exit

JNI层也提供了类似Java层的线程同步操作,Java层多线程环境下为了解决多线程安全问题往往要通过锁机制对共享资源进行访问保护。

synchronized (obj) {
    ...  //sychronized block.
}

Java VM保证任何线程在执行同步block时会先获取obj Monitor,这样会确保任何时候只有最多一个线程能获取obj Monitor并且执行同步block。

Native层也有类似的方案去实现Java层的同步机制。

if (env->MonitorEnter(obj) != JNI_OK) {
    //Error handling.
}
//.... synchronized block.
if (env->MonitorExit(obj) != JNI_OK) {
    //Error handling.
}
MonitorEnter

进入obj Monitor,每一个Java对象都有它自己的Monitor。如果当前线程已经获取到Monitor,再次获取的时候会进行Monitor计数;如果线程尝试获取Monitor的时候,此时没有任何线程占有,那当前线程会变成Monitor owner。如果当前线程尝试获取Monitor时,Monitor已经被其他线程占有,那当前线程会阻塞住直到占有的线程释放Monitor再获取。

为了避免死锁,通过MonitorEnter获取Monitor的线程必须要通过MonitorExit释放所占有的Monitor。

jint MonitorEnter(JNIEnv *env, jobject obj);
MonitorExit

退出obj Monitor。尝试释放的线程必须是Monitor的所有者,通过MonitorExit线程会减少Monitor计数,直到为零时,当前的线程才会真正释放Monitor。

jint MonitorExit(JNIEnv *env, jobject obj);

#2. 注册本地方法

2.1 示例
CatchThrow.java
public class CatchThrow {
    static {
        System.loadLibrary("jnitest");
    }

    public native void nativeAccess() throws IllegalArgumentException;

    public native void dynamicRegister();

    private void callback() throws NullPointerException {
        throw new NullPointerException("CatchThrow.callback");
    }
}
CatchThrow.cpp
static JavaVM *g_cached_vm;
static jclass g_clazz;

void dynamicRegister(JNIEnv *env, jobject obj) {
    jmethodID mid;
    jthrowable throwable;

    mid = env->GetMethodID(g_clazz, "callback", "()V");
    if (mid == nullptr) {
        return;
    }
    env->CallVoidMethod(obj, mid);
    throwable = env->ExceptionOccurred();
    if (throwable) {
        jclass expClass;
        env->ExceptionDescribe();
        env->ExceptionClear();

        expClass = env->FindClass("java/lang/IllegalArgumentException");
        if (expClass == nullptr) {
            return;
        }
        env->ThrowNew(expClass, "Thrown from native code.");
    }
}

static JNINativeMethod gMethods[] = {
        {
                "dynamicRegister",
                "()V",
                (void *) dynamicRegister
        }
};

JNIEXPORT jint JNI_OnLoad(JavaVM *vm, void *reserved) {
    JNIEnv *env;
    jclass clazz;

    g_cached_vm = vm;
    if (g_cached_vm == nullptr) {
        return JNI_ERR;
    }
    vm->GetEnv((void **) &env, JNI_VERSION_1_6);
    if (env == nullptr) {
        return JNI_ERR;
    }
    clazz = env->FindClass("com/nextlabs/hhu/myapplication/core/CatchThrow");
    if (clazz == nullptr) {
        return JNI_ERR;
    }
    g_clazz = (jclass) env->NewWeakGlobalRef(clazz);
    if (g_clazz == nullptr) {
        return JNI_ERR;
    }
    if (env->RegisterNatives(clazz, gMethods,
                             sizeof(gMethods) / sizeof(JNINativeMethod)) != JNI_OK) {
        return JNI_ERR;
    }
    return JNI_VERSION_1_6;
}

JNIEXPORT void JNI_OnUnload(JavaVM *vm, void *reserved) {
    JNIEnv *env;
    vm->GetEnv((void **) &env, JNI_VERSION_1_6);
    if (env == nullptr) {
        return;
    }
    env->DeleteWeakGlobalRef(g_clazz);
    g_clazz = nullptr;
}
2.2 Register API
RegisterNatives

注册clazz类中的native函数,参数methods表示待注册的method数组指针,参数nMethods表示待注册的native函数数量。

jint RegisterNatives(JNIEnv *env, jclass clazz,const JNINativeMethod *methods, jint nMethods);
JNINativeMethod
typedef struct { 
    char *name; 
    char *signature; 
    void *fnPtr; 
} JNINativeMethod; 
UnregisterNatives

取消注册clazz类中的native函数。

jint UnregisterNatives(JNIEnv *env, jclass clazz);

你可能感兴趣的:(八、JNI-JNI补充功能)