Java学习记录之JNI相关

java调用c语言主要通过下面这三个步骤来实现的

  1. 加载c库
  2. 找到对应的c函数,有对应的映射规则
  3. 调用函数

在这里插入图片描述

实际的例子

  1. TestJni.java 文件
  2. testJni.c 文件

TestJni.java 源代码

public class TestJni {
	static {//1. 使用静态代码块加载 c库
		System.loadLibrary("testJni"); /* libtestJni.so */
	}

	public static void main (String args[]) {
		// 2. java 源码中 映射到需要调用的 c函数
		
		//调用相应的函数
		test_demo();
	}
}

在java中调用 c 语言中的 test_demo 函数,有两种方式

  1. native static 声明
    public native static void test_demo();
    不加native 编译会报错
TestJni.java:7: error: missing method body, or declare abstract
        public static void test_demo();
                           ^
1 error
public class TestJni {
	static {//1. 使用静态代码块加载 c库
		System.loadLibrary("testJni"); /* libtestJni.so */
	}
	public native static void test_demo();
	public static void main (String args[]) {
		// 2. java 源码中 映射到需要调用的 c函数
		
		//调用相应的函数
		test_demo();
	}
}
  1. 如果不加 static 不能直接直接调用,需要使用 —》类.test_demo() 来调用
public class TestJni {
	static {//1. 使用静态代码块加载 c库
		System.loadLibrary("testJni"); /* libtestJni.so */
	}
	public native void test_demo();
	public static void main (String args[]) {
		TestJni t = new TestJni();
		// 2. java 源码中 映射到需要调用的 c函数
		
		//调用相应的函数,如果加了static,直接使用test_demo()即可调用
		t.test_demo();
	}
}

testJni.c 源代码

#inlcude <stdio.h>

void test_demo()
{
	printf("func:%s, line:%d\n", __func__, __LINE__);
}

int main() 
{
	
	return 0;
}

那么怎么调用呢?

jni.pdf–>chapter 2 Getting Started

编译java代码的命令

javac -encoding gbk TestJni.java

生成c里面对应的头文件

javah -jni TestJni

会在目录下面生成一个TestJni.h

这里会写c语言中函数名称应该写成什么样,其实是 类名+函数名

sgy@ubuntu:~/sgy/java_learn/jni_learn$ cat TestJni.
cat: TestJni.: No such file or directory
sgy@ubuntu:~/sgy/java_learn/jni_learn$ cat TestJni.h
/* DO NOT EDIT THIS FILE - it is machine generated */
#include <jni.h>
/* Header for class TestJni */

#ifndef _Included_TestJni
#define _Included_TestJni
#ifdef __cplusplus
extern "C" {
#endif
/*
 * Class:     TestJni
 * Method:    test_demo
 * Signature: ()V
 */
JNIEXPORT void JNICALL Java_TestJni_test_1demo
  (JNIEnv *, jclass);

#ifdef __cplusplus
}
#endif
#endif
sgy@ubuntu:~/sgy/java_learn/jni_learn$

修改testJni.c文件

#include <jni.h>
#include <stdio.h>
#include "TestJni.h"
JNIEXPORT void JNICALL
Java_TestJni_test_1demo(JNIEnv *env, jobject obj)
{
	printf("func:%s, line:%d\n", __func__, __LINE__);
return;
}



int main() 
{
	
	return 0;
}

编译成动态库

gcc -fPIC -shared -I /usr/lib/jvm/java-1.8.0-openjdk-amd64/include/ -I /usr/lib/jvm/java-1.8.0-openjdk-amd64/include/linux/ -o libtestJni.so testJni.c

告诉java 动态库的路径,需要设置环境变量

export LD_LIBRARY_PATH=.

运行java的命令

java TestJni

第二种方法, testJni.c 源码

第一种方法,c语言的函数的名字比较固定,第二种可以比较方便的指定对应关系

jni.pdf—>8.4.1 The JNI_OnLoad Handler

在这里插入图片描述

void test()
{
	printf("func:%s, line:%d\n", __func__, __LINE__);
}

static const JNINativeMethod methods[] = {
	{"test_demo", "()V",  test}, 
};


JNIEXPORT jint JNICALL
JNI_OnLoad(JavaVM *jvm, void *reserved)
{
	JNIEnv *env;
	jclass cls;

	if ((*jvm)->GetEnv(jvm, (void **)&env, JNI_VERSION_1_4)) {
		return JNI_ERR; /* JNI version not supported */
	}

	cls = (*env)->FindClass(env, "TestJni");
		if (cls == NULL) {
		return JNI_ERR;
	}

	if ((*env)->RegisterNatives(env, cls, methods, 1) < 0) {
		return JNI_ERR;
	} 
	return JNI_VERSION_1_4;
}
  1. cls = (*env)->FindClass(env, “JNIDemo”); 先要找到对应的类,返回一个cls
  2. (*env)->RegisterNatives(env, cls, methods, 1) 注册对应java函数和c函数的对应关系,对应关系存在methods结构体里面
typedef struct {
    char *name;          /* Java里调用的函数名 */
    char *signature;    /* JNI字段描述符, 用来表示Java里调用的函数的参数和返回值类型 */
    void *fnPtr;          /* C语言实现的本地函数 */
} JNINativeMethod;


static const JNINativeMethod methods[] = {
	{"test_demo", "()V",  test}, 
};

最后的源代码

#include <jni.h>
#include <stdio.h>
#include "TestJni.h"
JNIEXPORT void JNICALL
Java_TestJni_test_1demo(JNIEnv *env, jobject obj)
{
	printf("func:%s, line:%d\n", __func__, __LINE__);
	return;
}

void test()
{
	printf("func:%s, line:%d\n", __func__, __LINE__);
}

static const JNINativeMethod methods[] = {
	{"test_demo", "()V",  test}, 
};


JNIEXPORT jint JNICALL
JNI_OnLoad(JavaVM *jvm, void *reserved)
{
	JNIEnv *env;
	jclass cls;

	if ((*jvm)->GetEnv(jvm, (void **)&env, JNI_VERSION_1_4)) {
		return JNI_ERR; /* JNI version not supported */
	}

	cls = (*env)->FindClass(env, "TestJni");
		if (cls == NULL) {
		return JNI_ERR;
	}

	if ((*env)->RegisterNatives(env, cls, methods, 1) < 0) {
		return JNI_ERR;
	} 
	return JNI_VERSION_1_4;
}


int main() 
{
	
	return 0;
}

signature的格式和介绍
在这里插入图片描述
jni的字段描述符
对于参数是String类, signature应该写成 Ljava/lang/String;
对于其他类,统一写成 Ljava/lang/Object;

java函数怎么传递参数到c函数里面

1. 基本数据类型,直接使用,直接返回

TestJni.java 里面的函数改成下面这个样子

	public native static int test_demo(int java_var);
	
	System.out.println(test_demo(1));

testJni.c

jint test(JNIEnv *env, jclass cls, jint java_var)
{
	printf("func:%s, line:%d, java_var:%d\n", __func__, __LINE__, java_var);
	return 2;
}

执行结果

func:test, line:13, java_var:1
2
2. String类型参数,返回值是String类型

jni.pdf—>P39

TestJni.java 里面的函数改成下面这个样子

	public native static String test_demo(String java_str);

	System.out.println(test_demo("java_str"));

testJni.c

jstring test(JNIEnv *env, jclass cls, jstring java_str)
{
	const jbyte *str;
	str = (*env)->GetStringUTFChars(env, java_str, NULL);
	if (str == NULL) {
		return NULL; /* OutOfMemoryError already thrown */
	}

	
	printf("func:%s, line:%d, java_str:%s\n", __func__, __LINE__, str);

	
	return (*env)->NewStringUTF(env, "c_str");;
}

static const JNINativeMethod methods[] = {
	{"test_demo", "(Ljava/lang/String;)Ljava/lang/String;",  test}, 
};

执行结果

func:test, line:20, java_str:java_str
c_str
3. 数组类型参数,返回值是int类型

jni.pdf—>P49

TestJni.java 里面的函数改成下面这个样子

		public native static int test_demo(int array[]);
		int array[] = {1, 2, 3};
		System.out.println(test_demo(array));

testJni.c

jint test(JNIEnv *env, jclass cls, jintArray java_array)
{
	jint i = 0;
	jint sum = 0;
	jint *cArray;
	cArray = (*env)->GetIntArrayElements(env, java_array, NULL);

	if (cArray == NULL) {
		return 0; /* exception occurred */
	}
	for (i = 0; i < (*env)->GetArrayLength(env, java_array); i++) {
		sum += cArray[i];
	}
	(*env)->ReleaseIntArrayElements(env, java_array, cArray, 0);
	return sum;
}

static const JNINativeMethod methods[] = {
	{"test_demo", "([I)I",  test}, 
};

执行结果

6

jni关于数组提供的一些函数接口
在这里插入图片描述

PS:疑问

  1. 如果jni的字段描述符 java函数和C语言函数不匹配执行的时候会有什么效果?
         执行的时候会报错
Exception in thread "main" java.lang.NoSuchMethodError: Method TestJni.test_demo()V name or signature does not match
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值