Jimmy Chen

A Programmer

(原创) JNI编程指南与规范 第二章 内容补充

第二章 内容补充

  在原文中,构建JNI程序的方法只介绍了一个,其实构建JNI程序还有另外一种方法,这种方法我们称之为动态注册,相对的之前的方法我们称之为静态注册。我们先将方法介绍后,再看一下这两种方法有什么区别。

  从一个例子出发,例子和第二章的例子一样,也是在Java中调用一个native方法打印出“Hello World!”的字样。Java侧的代码如下:

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层的代码:

#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是一个结构体,定义如下:

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相关内容几乎都是用动态注册方法的。

此文为博主原创文章,转载请注明出处

发表评论

电子邮件地址不会被公开。 必填项已用*标注

This site uses Akismet to reduce spam. Learn how your comment data is processed.

%d 博主赞过: