第二章 内容补充
在原文中,构建JNI程序的方法只介绍了一个,其实构建JNI程序还有另外一种方法,这种方法我们称之为动态注册,相对的之前的方法我们称之为静态注册。我们先将方法介绍后,再看一下这两种方法有什么区别。
从一个例子出发,例子和第二章的例子一样,也是在Java中调用一个native方法打印出“Hello World!”的字样。Java侧的代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 |
class HelloWorld { private native void helloworld(); public static void main(String[] args) { HelloWorld h = new HelloWorld(); h.helloworld(); } static { System.loadLibrary("HelloWorld"); } } |
代码意思都是一样的,都是在HelloWorld类的main方法中创建一个对象,然后调用native层的方法HelloWorld来打印字符串。下面部分是native层的代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 |
#include <stdio.h> #include <jni.h> void JNICALL native_helloworld(JNIEnv* env, jobject ojb) { printf("Hello World!\n"); return; } static const JNINativeMethod gMethods[] = { {"helloworld", "()V", (void *)native_helloworld} }; static jclass myClass; static const char* const ClassName="HelloWorld"; JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM* vm, void* reserved) { JNIEnv* env = NULL; jint result = -1; if((*vm)->GetEnv(vm, (void**)&env, JNI_VERSION_1_6) != JNI_OK) return -1; myClass = (*env)->FindClass(env, ClassName); if(myClass == NULL) { printf("Cannot get class:%s\n", ClassName); return -1; } if((*env)->RegisterNatives(env, myClass, gMethods, sizeof(gMethods)/sizeof(gMethods[0])) < 0) { printf("Register native methods failed.\n"); return -1; } printf("--------JNI_OnLoad---------\n"); return JNI_VERSION_1_6; } |
- 首先要介绍的是JNI_OnLoad方法,这个方法是自动调用的,在Java侧,我们在静态代码块内通过System.loadLibrary,加载动态库的时候,相应的动态库的JNI_OnLoad方法就会自动执行。
- 在JNI_OnLoad方法中,带有的参数是JavaVM而不是我们常见的JNIENV,所以需要向获取到JNIEnv对象,然后我们通过JNIEnv的FindClass方法找到我们需要动态注册的类。
- 最后我们通过RegisterNatives方法,将gMethods数组中的Java层的函数和native层的函数动态绑定起来。
其中JNINativeMethod是一个结构体,定义如下:
1 2 3 4 5 |
typedef struct { const char* name; const char* signature; void* fnPtr; } JNINativeMethod; |
name指的是Java中定义的方法, signature指的是方法的描述符,也可以叫做签名,“()V”中“()”表示的是方法的参数为void,“V”表示的是返回值为void,具体的定义在第四章会讲到,这里可以先跳过。fnPtr指向的是native的方法名。
最后按照第二章中介绍的方法,编译运行就可以了。
动态注册和静态注册相比,动态注册更加灵活而且简单,不像静态注册那样,还需要用java和javah编译后才得出最后的函数名,如果中间Java文件改动,添加了native方法或者修改了native方法的函数名,又要重新做上面的步骤,麻烦。看过Android源码的朋友都应该发现了,Android中的JNI相关内容几乎都是用动态注册方法的。
此文为博主原创文章,转载请注明出处