JNI入门系列一,第一个JNI程序

JNI官方手册

首先编写一个简单的类,其中包含一个native方法

public class JNI{
	static{
		// 加载动态链接库,该动态链接库我们将在下面进行创建
		System.loadLibrary("hello_jni");
	}	
	public native void hello();
	
	public static void main(String[] args){
		JNI jni = new JNI();
		jni.hello();
	}
}

我们现在来生成对应的动态链接库,注意我们这里使用的是linux,Linux下的动态库名以.so 或 .so.y结尾,其中y代表版本号,而Windows下为.dll。

编译Java文件并生成java头文件

# 编译出class文件
javac JNI.java 
# 生成native方法对应的头文件,头文件名默认以类名开头,比如这里生成的头文件文件名就是 JNI.h
javah JNI -o 

生成的头文件

/* DO NOT EDIT THIS FILE - it is machine generated */
#include <jni.h>
/* Header for class JNI */

#ifndef _Included_JNI
#define _Included_JNI
#ifdef __cplusplus
extern "C" {
#endif
/*
 * Class:     JNI
 * Method:    hello
 * Signature: ()V
 */
 /** 
  * 这个函数声明就是对应到我们在java文件中写的native方法
  * 我们需要来实现该函数声明,如果我们上面的java文件中存在多个native方法,这里就会出现多个函数声明
  * 函数名是固定格式:Java_类全限定名_方法名
  * 
  */
JNIEXPORT void JNICALL Java_JNI_hello
  (JNIEnv *, jobject);

#ifdef __cplusplus
}
#endif
#endif

JNIEnv指针
JNIEnv,指代了Java本地接口环境(Java Native Interface Environment),是一个JNI接口指针,指向了本地方法的一个函数表,该函数表中的每一个成员指向了一个JNI函数,本地方法通过JNI函数来访问JVM中的数据结构,详情如下图:
在这里插入图片描述JNIEnv指针在<jni.h>文件中的具体实现是一个包含诸多JNI函数的结构体,局部摘要如下:

/*
 * We use inlined functions for C++ so that programmers can write:
 *
 *    env->FindClass("java/lang/String")
 *
 * in C++ rather than:
 *
 *    (*env)->FindClass(env, "java/lang/String")
 *
 * in C.
 */

struct JNIEnv_ {
    const struct JNINativeInterface_ *functions;
    jint GetVersion() {
        return functions->GetVersion(this);
    }
    jclass DefineClass(const char *name, jobject loader, const jbyte *buf,
                       jsize len) {
        return functions->DefineClass(this, name, loader, buf, len);
    }
    ...
     jclass GetObjectClass(jobject obj) {
        return functions->GetObjectClass(this,obj);
    }
    jboolean IsInstanceOf(jobject obj, jclass clazz) {
        return functions->IsInstanceOf(this,obj,clazz);
    }

    jmethodID GetMethodID(jclass clazz, const char *name,
                          const char *sig) {
        return functions->GetMethodID(this,clazz,name,sig);
    }
    ...
}

jobject与jclass类型
jobject与jclass通常作为JNI函数的第二个参数,当所声明Native方法是静态方法时,对应参数jclass,因为静态方法不依赖对象实例,而依赖于类,所以参数中传递的是一个jclass类型。相反,如果声明的Native方法时非静态方法时,那么对应参数是jobject 。

新建一个jni.c文件来实现上面的头文件

#include<stdio.h>
// 引入上面的生成的头文件
#include<JNI.h>
/**
 * 实现头文件声明的函数
 */
JNIEXPORT void JNICALL Java_JNI_hello
  (JNIEnv * env, jobject obj){
	printf("hello jni\n");
}

生成动态链接库文件应该为:libhello_jni.so,这里的名字要和我们上面加载的动态链接库的名字对应上。lib_.so,中间下划线的位置就是我们一个动态链接库的实际名称。lib_.so是一个规范化的名称格式,注意这里使用的是linux

这些命令参数都是GCC的参数,需要去了解一下

# -I是指定头文件的路径 -o是指定输出文件名
# -fPIC是告诉编译器产生与位置无关代码(Position-Independent Code)
# -shared就是生成动态链接库
gcc -fPIC -shared jni.c -I $JAVA_HOME/include -I ./ -I $JAVA_HOME/include/linux -o libhello_jni.so

最终我们就得到了一个名为libhello_jni.so的动态链接库

这时候我们就可以执行一下试试看了
在这里插入图片描述
出错了,为什么会出现这样的错误,错误提示非常的清楚,就是动态链接库没有找到,想想也对,凭什么我们直接System.loadLibrary("")就能找到呢?其加载动态链接库是有其自己的规则的,我们上面生成动态链接库的路径就是当前JNI.class所在路径,也就是我们运行java JNI命令时所在路径。
上面出现了java.library.path,其加载动态链接库就是到java.library.path属性对应的路径去查找对应的动态链接库,那怎么去设置路径呢?

1.启动JVM设置参数

java -Djava.library.path=路径 JNI

2.在/etc/profile 后面加上一行 export LB_LIBRARY_PATH=路径

这样就能成功的加载到动态链接库了

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值