java调用JNI总结

工作中需要用到java调用DES加密解密算法进行通信加密、MAC计算等,原来直接有C语言版的DES算法库可以用,但是java用的不熟,java的DES算法库没用过,就想到把C语言的DES算法库编译成DLL,然后通过java的JNI调用。

网上java调用jni的例子挺多,本以为挺简单的,但是实践了一下才知道,好多地方容易出错,这里总结一下容易出错的地方。

java调用JNI,按步骤,首先需要建一个类,声明本地方法。然后用javac编译这个类,最后用javah生成c语言的头文件并实现对应的*.c文件,并编译为动态库。把这个动态库放

在指定位置,如/bin目录下。在java中静态加载这个DLL。

以一个简单的例子说明:

package test;
public class TestDes {

	 public native void SayHello(String name);
	 public native void DesOperation(int[] pInOut,int[] pbKey,int cnt);
	 public native int  MathDesMac(int[] pbuf,int len,int[] pbKey,int type);
	 static
     {
        System.load(Class.class.getResource("/").getPath()+"test_TestDes.dll");
		 //System.load("C:/Users/yang/A303workspc/testjni/bin/test/test_TestDes.dll");
     }
	/**
	 * @param args
	 */
	public static void main(String[] args) {
		// TODO Auto-generated method stub
		System.out.println("java jni test:");
		System.out.println(Class.class.getResource("/").getPath());
		//System.out.println(System.getProperty("user.dir"));
		TestDes h=new TestDes();
		h.SayHello("nice to meet you!\r\n");
	}

}

常见的错误如:java.lang.UnsatisfiedLinkError,

javah -jni -classpath . test.TestDes
错误:无法访问 test.TestDes

javah -jni -classpath .  TestDes
错误:无法访问 TestDes
错误的类文件: .\TestDes.class
类文件包含错误的类: test.TestDes

原因是有些地方需要注意:

当调用javah命令生成c语言的头文件时,首先需要进入*java的源文件目录下,调用javac 把有本地方法的java类文件编译为*.class,然后用javah命令时,需要在正确的目录下调用,切换回src目录下调用javah -jni命令。注意,指定的类clss文件要带上包名。如 test.TestDes

如在yang@DESKTOP-LRJFD2R ~/a303workspc/testjni/src 目录下,调用javah -jni -classpath . test.TestDes

其中test是包的名字。


生成了test_TestDes.h的头文件。

文件内容如下:

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

#ifndef _Included_test_TestDes
#define _Included_test_TestDes
#ifdef __cplusplus
extern "C" {
#endif
/*
 * Class:     test_TestDes
 * Method:    SayHello
 * Signature: (Ljava/lang/String;)V
 */
JNIEXPORT void JNICALL Java_test_TestDes_SayHello
  (JNIEnv *, jobject, jstring);

/*
 * Class:     test_TestDes
 * Method:    DesOperation
 * Signature: ([I[II)V
 */
JNIEXPORT void JNICALL Java_test_TestDes_DesOperation
  (JNIEnv *, jobject, jintArray, jintArray, jint);

/*
 * Class:     test_TestDes
 * Method:    MathDesMac
 * Signature: ([II[II)I
 */
JNIEXPORT jint JNICALL Java_test_TestDes_MathDesMac
  (JNIEnv *, jobject, jintArray, jint, jintArray, jint);

#ifdef __cplusplus
}
#endif
#endif

接下来就需要用GCC把这些头文件中的函数给实现了。为了方便,直接把JNI.h和JNI_md.h文件和需要编译的C文件都放到src目录下。

同时把test_TestDes.h头文件中的include<jni.h>,尖括号换为“JNI.h”

GCC生成动态库,如下:gcc -std=c99  -Wl,--add-stdcall-alias  -shared


makefile文件:

########################################
#makefile
########################################
BINARY= test_TestDes
CC= gcc
LD= ld
CFLAGS= -std=c99  -Wl,--add-stdcall-alias 
#LDSCRIPT= -lws2_32
#LDFLAGS= -Llib
#OBJS=  helloworld.o
OBJS= curcalc_calc.o curcalc_crc.o curcalc_des.o curcalc_md5.o curcalc_oth.o  curcalc_sha1.o test_TestDes.o
#CFLAGS=-std=c99

.PHONY: clean
all:images
images: $(BINARY).dll
$(OBJS):%.o:%.c
$(CC) -c  $(CFLAGS) $< -o $@
%.dll: $(OBJS)
$(CC) $(CFLAGS) -shared -o $(BINARY).dll $(OBJS)
cp $(BINARY).dll ../bin
clean:
rm -f *.o

test_TestDes.c文件如下:

#include "test_TestDes.h"
#include <stdio.h>
#include <string.h>
#include "includes.h"
void JNICALL Java_test_TestDes_SayHello(JNIEnv * env, jobject obj, jstring str)
{
    jboolean  b ;
    char s[80];
    memset(s, 0, sizeof(s));
    strcpy(s ,(char*)(*env)->GetStringUTFChars(env,str, &b));
    printf("Hello, %s", s);
    (*env)->ReleaseStringUTFChars(env,str , NULL);
}

void JNICALL Java_test_TestDes_DesOperation(JNIEnv *env, jobject obj, jintArray data, jintArray key, jint t)
{

	jboolean  b ;
	jsize n1,n2;
	
	n1 = (*env)->GetArrayLength(env,data);
	n2 = (*env)->GetArrayLength(env,key);
	printf("len is %d\r\n",n1);
	if((n1 < 8) || (n2 < 8))
	{
		printf("lenth is not enough!");
		return;
	}
	jint jt1[n1];
	jint jt2[n2];
	unsigned char buf1[n1];
	unsigned char buf2[n2];
	//memcpy(s,(jint*)(*env)->GetIntArrayElements(env,data,&b),n);
	(*env)->GetIntArrayRegion(env,data,0,n1,jt1);

	for(int i=0;i < n1;i++)
	{
		buf1[i] = jt1[i];
	}
	
	printf("jt1 is %d %d %d %d\r\n",jt1[0],jt1[1],jt1[2],jt1[3]);
	printf("buf1 is %d %d %d %d\r\n",buf1[0],buf1[1],buf1[2],buf1[3]);
	//printf("len is %d\r\n",n2);
	//memcpy(s,(jint*)(*env)->GetIntArrayElements(env,data,&b),n);
	(*env)->GetIntArrayRegion(env,key,0,n2,jt2);
	
	for(int i=0;i < n2;i++)
	{
		buf2[i] = jt2[i];
	}
	
	CurCalc_DES_DesOperation( buf1, 0, 
										buf2, 
										SINGLE_DES_DECRYPTION | ZERO_CBC_IV, 
										t ) ;

	printf("buf1 is %d %d %d %d\r\n",buf1[0],buf1[1],buf1[2],buf1[3]);
	(*env)->SetIntArrayRegion(env,data,0,8,jt1);
}

jint JNICALL Java_test_TestDes_MathDesMac(JNIEnv *env, jobject obj, jintArray data, jint len,jintArray key, jint type)
{
	return 0;
}

执行结果:


### 回答1: Java调用JNI实例的过程如下: 在Java调用JNI实例的第一步是通过System.loadLibrary()方法加载包含本地函数实现的动态链接库。这将使得Java程序能够找到并访问JNI实例中的方法。 接下来,在Java代码中声明一个native方法,这个方法将由JNI实例来实现。native关键字告诉编译器该方法是一个JNI方法。 然后,使用javah命令自动生成一个C/C++头文件,该头文件中包含了Java声明的native方法的函数原型。头文件的生成步骤是在命令行中进入Java源代码目录,并执行javah命令。 接下来,打开生成的头文件,将native方法的函数原型拷贝到一个C/C++源文件中,并实现该方法。 在C/C++源文件中,需要包含jni.h头文件,并实现native方法的功能。在实现JNI方法时,可以使用JNIEnv接口调用Java方法,通过JNI实例和Java对象进行交互。 最后,使用C/C++的编译器将源文件编译成动态链接库。 在Java代码中,可以通过调用声明的native方法来使用JNI实例,这样就可以调用C/C++中实现的功能了。 需要注意的是,Java和C/C++之间的数据类型转换可能需要注意,因为Java和C/C++使用不同的数据类型系统。为了正确传递和处理参数,可能需要使用JNIEnv接口的一些方法来处理数据类型的转换。 总结来说,Java调用JNI实例的过程包括加载JNI实例的动态链接库、声明native方法、使用javah命令自动生成头文件、实现native方法的功能、编译源文件成动态链接库、通过调用native方法来使用JNI实例。 ### 回答2: Java调用JNI实例是指在Java程序中使用JNI技术(Java Native Interface)来调用本地方法。 JNIJava与本地代码之间的桥梁,它允许Java程序在运行时调用本地编程语言(如C、C++)编写的代码。这样做的好处是可以结合Java的跨平台特性和本地编程语言的高效性能。 要在Java程序中调用JNI实例,首先需要编写本地代码。以C语言为例,我们需要编写一个与Java类对应的本地方法。 编写本地方法时,需要使用特定的修饰符来表明这是一个本地方法,并且使用JNI提供的函数来与Java交互。然后将本地代码编译成动态链接库(如.so文件)。 然后,在Java程序中通过System.loadLibrary方法加载动态链接库。加载成功后,就可以通过Java调用本地方法了。 在调用本地方法时,需要使用native关键字来声明这是一个本地方法,并且使用JNI提供的接口来调用本地方法。 在调用JNI实例时,需要注意以下几点: 1. 确保本地代码与Java代码的数据类型匹配,可以使用JNI提供的函数转换数据类型。 2. 传递参数时,可以将Java的对象、基本数据类型或数组作为参数传递给本地方法。 3. 在本地方法中修改Java的对象时,需要使用JNI提供的函数来获取并修改Java对象的属性或调用Java对象的方法。 总结Java调用JNI实例是一种充分利用Java跨平台特性和本地编程语言高性能的方式。通过编写本地代码并加载动态链接库,可以在Java程序中调用本地方法。在调用JNI实例时,需要注意数据类型匹配和参数传递等问题。 ### 回答3: Java调用JNIJava Native Interface)是一种机制,用于在Java程序中调用本机(Native)方法。Java开发者可以使用JNI将具有特定目标平台相关的操作或功能集成到Java应用程序中。 为了实现Java调用JNI,开发者首先需要编写一个Java类,其中包含用于调用本机方法的声明。然后,在本机方法中编写原生代码,以实现需要的功能。在编写完本机代码后,需要将其编译为动态连接库(.dll,.so等),以供Java程序调用。 在Java类中,使用`native`关键字声明本机方法。然后,通过JavaJNI库提供的函数,将本机方法与本机库中对应的函数进行关联。具体步骤如下: 1. 编写Java类,并在其中声明本机方法的原型,使用`native`关键字标记这些方法。 2. 使用Java的`javac`命令编译Java类,生成字节码文件。 3. 使用Java的`javah`命令生成头文件,该头文件包含了本机方法的声明。 4. 在C/C++中编写实现本机方法的代码,并将其编译为动态连接库。可以根据平台的不同,使用不同的编译选项和工具链进行编译。 5. 将生成的动态连接库与Java程序绑定。这可以通过在Java程序中使用`System.loadLibrary("library_name")`的方式来实现,其中`library_name`为动态连接库的名称。 6. 在Java程序中调用本机方法,通过JNI库提供的函数进行连接。 需要注意的是,Java调用JNI的过程需要遵循特定的规范和约定,以确保本机方法与Java代码之间的正确交互。此外,使用JNI可能会涉及到与本机平台相关的问题,比如内存管理和线程安全等。因此,在使用JNI时,开发者需要仔细考虑潜在的问题,并进行适当的测试和调试。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

特立独行的猫a

您的鼓励是我的创作动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值