JNI(直接注册JNI本地函数)

Author Avatar
罗炜光 9月 30, 2016
  • 在其它设备中阅读本文章

JNI机制提供了名称为RegisterNatives()的JNI函数,该函数运行C/C++开发者将JNI本地函数与Java类的本地方法直接映射在一起。当不调用RegisterNatives()函数时,Java虚拟机会自动检索并将JNI本地函数与相应的Java本地方法链接在一起。但当开发者直接调用RegisterNatives()函数进行映射时,Java虚拟机就不必进行映射处理,这会极大提高运行速度,提高运行效率。

实例

HelloJNI.java

public class HelloJNI 
{

    //本地方法声明
    native void printHello();
    native void printString(String str);

    //加载库
    static { System.loadLibrary("hellojni"); }

    public static void main(String[] args) 
    {
        HelloJNI myJNI = new HelloJNI();

        //调用本地方法(实际调用的是使用C语言编写的JNI本地函数)
        myJNI.printHello();
        myJNI.printString("Hello World from printString fun");
    }
}

hellojnimap.cpp

#include "jni.h"
#include <stdio.h>

//JNI本地函数原型
void printHelloNative(JNIEnv *env,jobject obj);
void printStringNative(JNIEnv *env,jobject obj,jstring string);

JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM *vm,void *reserved)
{
    JNIEnv* env = NULL;
    JNINativeMethod nm[2];
    jclass cls;
    jint result = -1;

    if(vm->GetEnv((void**) &env,JNI_VERSION_1_4) != JNI_OK)
    {
        printf("Error");
        return JNI_ERR;
    }

    cls = env->FindClass("HelloJNI");

    nm[0].name = (char *)"printHello";
    nm[0].signature = (char *)"()V";
    nm[0].fnPtr = (void *)printHelloNative;

    nm[1].name = (char *)"printString";
    nm[1].signature = (char *)"(Ljava/lang/String;)V";
    nm[1].fnPtr = (void *)printStringNative;

    env->RegisterNatives(cls,nm,2);

    return JNI_VERSION_1_4;
}

//实现JNI本地函数
void printHelloNative(JNIEnv *env,jobject obj)
{
    printf("Hello World!\n");

    return;
}

void printStringNative(JNIEnv *env,jobject obj,jstring string)
{
    const char *str = env->GetStringUTFChars(string,0);
    printf("%s!\n",str);

    return;
}

编译:

gcc -shared -o hellojni.dll hellojnimap.cpp
javac HelloJNI.java

运行:

java HelloJNI

结果:

Hello World!
Hello World from printString fun!

JNI_OnLoad()讲解

在加载指定库文件时,JNI_OnLoad()函数会被自动调用执行,程序开发者若想手工映射本地方法与JNI本地函数,需要在JNI_OnLoad()函数内调用RegisterNatives()函数进行映射匹配。

JNI_OnLoad()

  • 形式:
    jint JNI_OnLoad(JavaVM *vm,void *reserved)

  • 说明:
    Java虚拟机加载本地库时(System.loadLibrary()等方法被调用时)会调用JNI_OnLoad()函数。在使用加载库的过程中,JNI_OnLoad()函数会向Java虚拟机确认JNI的版本。若库中不包含JNI_OnLoad()函数,Java虚拟机会认为相关库要求JNI1.1版本支持

  • 参数:

    • vm JavaVM接口指针
    • reserved 预定参数
  • 返回值:
    若执行成功,则返回所生成的数组引用,若失败,则返回NULL

判断支持版本

GetEnv()

  • 形式:
    jnit GetEnv(JavaVM *vm,void **env,jint version)

  • 说明:
    判断Java虚拟机是否支持version指定的JNI版本,而后将JNI接口指针设置到*env中

  • 参数:

    • vm JavaVM接口指针的地址
    • env JNI接口指针地址
    • version JNI版本
  • 返回值:
    若执行成功,返回0,失败,返回负数

注册绑定

JNINativeMethod用来保存待映射的本地方法与JNI本地函数的相关信息

typedef struct {
    char *name;//本地方法名称
    char *signature;//本地方法签名
    void *fnPtr;//与本地方法相对应的JNI本地函数指针
} JNINativeMethod

RegisterNatives()

  • 形式:
    jarray RegisterNatives(JNIEnv *env,jclass clazz,const JNINativeMethod *methods,jnit nMethods)

  • 说明:
    将clazz指定类中的本地方法与JNI本地函数链接在一起,链接信息保存在JNINativeMethod结构体数组中

  • 参数

    • env JNI接口指针
    • clazz Java类
    • methods 包含本地方法与JNI本地函数的链接信息
    • nMethods methods数组元素的个数
  • 返回值
    若执行成功,返回数组引用,否则,返回NULL

参考资料

Amdroid框架揭秘