jni基本操作 4. 加载与卸载函数,动态注册与反注册本地方法

版权声明:本文为匆忙拥挤repeat(stone)原创文章,转载请注明出处 —— http://blog.csdn.net/jjwwmlp456 https://blog.csdn.net/jjwwmlp456/article/details/89321855


系列文章

jni基本操作 1. java 层创建 native 方法,并生成对应 jni 函数
jni基本操作 2. 操作java中的属性
jni基本操作 3. 操作java中的方法
jni基本操作 4. 加载与卸载函数,动态注册与反注册本地方法


jni.h 中的加载与卸载函数

JNIEXPORT jint JNI_OnLoad(JavaVM* vm, void* reserved);//返回值,是 jni version
JNIEXPORT void JNI_OnUnload(JavaVM* vm, void* reserved);

重写这两个函数,可以对 jni 本地方法的加载与卸载进行控制。
它们可以看做是 jni 本地方法的 生命周期函数。


动态注册与反注册本地方法函数

jint   (*RegisterNatives)(JNIEnv*, jclass, const JNINativeMethod*, jint);
jint   (*UnregisterNatives)(JNIEnv*, jclass);
typedef struct {
    const char* name; //java 层 native 方法 名
    const char* signature; //方法签名
    void*       fnPtr;  //本地函数指针
} JNINativeMethod;

动态注册与反注册,需要在 JNI_OnLoad和JNI_OnUnload中去实现。
反注册,可以不予实现。
返回值,不等于 JNI_OK 就表示失败了。
注册与反注册都需要一个 jclass 型参数。


jni动态注册java 的 native方法

java native方法 与 javah 生成的JNI 函数

java native 方法,通过 javah 可以生成 一个 jni 函数:

java:
public native static String helloWorld();

.h: 
JNIEXPORT jstring JNICALL Java_com_stone_ndk_jni_JniActivity_helloWorld
  (JNIEnv *,  jclass);

该 native 方法就与 jni 函数一一对应了


不用 javah,动态绑定java层的 native 方法和本地代码中的函数

在 JNI_OnLoad()中使用RegisterNatives(),注册本地方法数组;
数组内每个元素就是 JNINativeMethod 类型。

示例:

java中,com/stone/ndk/jni/JniActivity:
public native static String helloWorld();

cpp:
JavaVM* g_jvm;

jstring getString(); //仅声明在前面,方便后面调用;需要在后面某处具体实现

//声明需要注册的方法 数组
static JNINativeMethod methods[] = {
        //对应 java 中的 native 方法名, 方法签名描述,  本地方法
        {"helloWorld3", "()Ljava/lang/String;", reinterpret_cast<void *>(getString)}
};

JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM* vm, void* reserved) {
    JNIEnv* env;
    //获取 env
    if (JNI_OK != vm->GetEnv(reinterpret_cast<void**> (&env),JNI_VERSION_1_6)) {
//        LOGW("JNI_OnLoad could not get JNI env");
        return JNI_ERR;
    }

    g_jvm = vm; //用于后面获取JNIEnv
    jclass clazz = env->FindClass("com/stone/ndk/jni/JniActivity");

    //注册Native方法
    //将 methods 内所有方法 与 clazz 对应类中的 native 方法进行绑定
    if (env->RegisterNatives(clazz, methods, sizeof(methods) / sizeof((methods)[0])) != JNI_OK) {
//        LOGW("RegisterNatives error");
        return JNI_ERR;
    }

    return JNI_VERSION_1_6;
}

jstring getString() {
//    nullptr 是c++中空指针类型的关键字。  c11
    JNIEnv *env = nullptr;
//    JNIEnv *env = NULL; 旧写法
    g_jvm->AttachCurrentThread(&env, nullptr); //g_jvm为JavaVM指针

    return env->NewStringUTF("stone->中华人民共和国!");
}

本例,将 本地方法 getString与 java 层的 native 方法 helloWorld 进行了动态绑定。


动态注册时的参数

动态注册的本地方法,可以有 JNIEnv* 型的参数,jobject/jclass 型的参数,也可以没有这两个参数。
一般情况都可能要用到 env 指针,那只有一个 JNIEnv* 参数,也是可以的。
如上例中的 getString();可以写成:

jstring getString();
jstring getString(JNIEnv* env);
jstring getString(JNIEnv* env, jclass clazz);

正规的写法,还是应该正确匹配 java 层native方法是否是静态的。

示例

java中,com/stone/ndk/jni/JniActivity:
public native String helloWorld3(int index);
public native static String helloWorld4(int index);

cpp:
jstring getString(JNIEnv* env, jobject jobj, jint index);
jstring getString2(JNIEnv* env, jclass clazz, jint index);

//声明需要注册的方法 数组
static JNINativeMethod methods[] = {
        //对应 java 中的 native 方法名, 方法签名描述,  本地方法
        {"helloWorld3", "(I)Ljava/lang/String;", reinterpret_cast<void *>(getString)},
        {"helloWorld4", "(I)Ljava/lang/String;", reinterpret_cast<void *>(getString2)}
};

JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM* vm, void* reserved) {
	JNIEnv* env;
	 //获取 env
    if (JNI_OK != vm->GetEnv(reinterpret_cast<void**> (&env),JNI_VERSION_1_6)) {
        return JNI_ERR;
    }
    jclass clazz = env->FindClass("com/stone/ndk/jni/JniActivity");
    //注册Native方法
    if (env->RegisterNatives(clazz, methods, sizeof(methods) / sizeof((methods)[0])) != JNI_OK) {
        return JNI_ERR;
    }
    return JNI_VERSION_1_6;
}

jstring getString(JNIEnv* env, jobject jobj, jint index) {
	char *res = nullptr;
	if (env->IsInstanceOf(jobj, env->FindClass("com/stone/ndk/jni/JniActivity"))) {
        res = "stone->  是实例";
    } else {
        res "stone->  不是实例";
    }
    return env->NewStringUTF(res);
}

jstring getString2(JNIEnv* env, jclass clazz, jint index) {
	jobject jobj = env->AllocObject(clazz);
	char *res = nullptr;
	if (env->IsInstanceOf(jobj, env->FindClass("com/stone/ndk/jni/JniActivity"))) {
        res = "stone->  是实例";
    } else {
        res "stone->  不是实例";
    }
    return env->NewStringUTF(res);
}

其它

反注册的示例就不写了,无非就是重写 JNI_OnUnload(),内部一样的要先从JavaVM*获取 JNIEnv*,再调用 env->UnregisterNatives(clazz); clazz 也是通过env->findClass() 进行获取。

动态注册的优点:

  1. 不需要每次用 javah生成头文件了,或者自书写一堆 JNIEXPORT jstring JNICALL ... 这样的函数声明。
  2. 不强制限定 java 层 native 方法定义在哪个具体包名下的哪个具体类中。上例中只是为了演示需要,限定了类的完全路径,以测试jobject 是否是 指定的 jclass 的实例。实际中,只需要知道 java 类中的属性、方法的定义就可以了,然后通过 jni 操作java 类的实例对象(jobject)或字节码对象(jclass),获取、设置、调用 相应的 java 类中的 属性、方法。
  3. 移植更方便 (第2点的简短的说法)

猜你喜欢

转载自blog.csdn.net/jjwwmlp456/article/details/89321855