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