在java中调用c语言——JNI入门


本文章非原创,多数内容参考以下两篇博客,感谢。由于我在实践中遇到一些问题,所以打算做一些补充,重新写一篇更完整的JNI入门文章。

使用JNI进行Java与C/C++语言混合编程(1)--在Java中调用C/C++本地库

Java语言与C语言混合编程(1)--Java native 关键字


一、编写java文件:创建native method

简单地讲,一个 native Method 就是一个java调用非java代码的接口。表示一个方法的实现在java以外,已经在本地实现了。

如果想更加了解 native Method 可以查看 Java语言与C语言混合编程(1)--Java native 关键字


下面我们举个例子。


package myJNI;

public class myJNI {
	    public native int intMethod(int n);
	    public native boolean booleanMethod(boolean bool);
	    public native String stringMethod(String text);
	    public native int intArrayMethod(int[] intArray);
	     
	    public static void main(String[] args) {
	        System.loadLibrary("myJNIdll");//注意: 不可以在代码中写上后缀 .dll 或 .so
	        myJNI sample = new myJNI();
	        int square = sample.intMethod(5);
	        boolean bool = sample.booleanMethod(true);
	        String text = sample.stringMethod("Java");
	        int sum = sample.intArrayMethod(new int[]{1,2,3,4,5,8,13});
	         
	        System.out.println("intMethod:" + square);
	        System.out.println("booleanMethod:" + bool);
	        System.out.println("stringMethod:" + text);
	        System.out.println("intArrayMethod:" + sum);
	    }
}

上面有4个native方法, 4个native方法就是我们需要用C来实现的方法.

分别是4种类型的参数, int, boolean, String, int[].

其中有一句比较重要, 这句话加载了动态类库

System.loadLibrary("myJNI");

在windows下加载的就是myJNI.dll, 在Linux下加载的就是myJNI.so.

本文使用的windows, 所以后面使用myJNI.dll来表示myJNI动态链接库.

编译myJNI.java, 使用命令行(windows是cmd, linux下一般是bash)

javac myJNI.java


可以看到myJNI.class文件


二、使用javah生成头文件

javah myJNI.myJNI

注意带上包名,我们的文件一般有包名。我的例子里包名是myJNI,在Java文件里可以看到。这和java命令一样。平时IDE用多了容易忘记。

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

#ifndef _Included_myJNI_myJNI
#define _Included_myJNI_myJNI
#ifdef __cplusplus
extern "C" {
#endif
/*
 * Class:     myJNI_myJNI
 * Method:    intMethod
 * Signature: (I)I
 */
JNIEXPORT jint JNICALL Java_myJNI_myJNI_intMethod
  (JNIEnv *, jobject, jint);

/*
 * Class:     myJNI_myJNI
 * Method:    booleanMethod
 * Signature: (Z)Z
 */
JNIEXPORT jboolean JNICALL Java_myJNI_myJNI_booleanMethod
  (JNIEnv *, jobject, jboolean);

/*
 * Class:     myJNI_myJNI
 * Method:    stringMethod
 * Signature: (Ljava/lang/String;)Ljava/lang/String;
 */
JNIEXPORT jstring JNICALL Java_myJNI_myJNI_stringMethod
  (JNIEnv *, jobject, jstring);

/*
 * Class:     myJNI_myJNI
 * Method:    intArrayMethod
 * Signature: ([I)I
 */
JNIEXPORT jint JNICALL Java_myJNI_myJNI_intArrayMethod
  (JNIEnv *, jobject, jintArray);

#ifdef __cplusplus
}
#endif
#endif

我们可以看到其中有四个函数声明, Java_包名_完整类名_方法名, 完整类名包括了包名。

在注释中我们可以看到这样一个东西 Signature, 这个是方法的签名. 关于Signature, 下面通过一个表格来说明.

java类型 Signature 备注
boolean Z  
byte B  
char C  
short S  
int I  
long L  
float F  
double D  
void V  
object L用/分割的完整类名 例如: Ljava/lang/String表示String类型
Array [签名 例如: [I表示int数组, [Ljava/lang/String表示String数组
Method (参数签名)返回类型签名 例如: ([I)I表示参数类型为int数组, 返回int类型的方法

上面头文件的第一个函数声明

JNIEXPORT jint JNICALL Java_myJNI_myJNI_intMethod (JNIEnv *, jobject, jint);

上方注释中的签名是 Signature: (I)I

在每个函数的参数列表中都有JNIEnv *和 jobject两个参数, 这两个参数稍候说明.


三、编辑DLL文件


1.处理头文件之间的联系


使用dev c++创建 DLL project。给其中添加两个文件,分别是

C:\Program Files\Java\jdk1.8.0_101\include\jni.h

C:\Program Files\Java\jdk1.8.0_101\include\win32\jni_md.h

这两个文件在Java安装目录下,通常默认安装在以上路径。



对 jni.h 文件做一些更改,#include <jni_md.h>改为 #include "C:\Program Files\Java\jdk1.8.0_101\include\win32\jni_md.h"

对我们用 javah 生成的头文件的开头部分 include 语句也要用双引号加文件名。自动生成的是如下样子,改为

#include "C:\Program Files\Java\jdk1.8.0_101\include\jni.h"


这两步的操作是因为 #include < > 表示在库函数里查找,但是 jni.h 和 jni_md.h 并不在c语言的库函数里,而是在

Java 的安装目录里。我们把这两个文件放到了我们的 DLL project 里,如果不用双引号将会差找不到。

深入学习的话去了解 include 语句。

include 关系为 dll.h include jni.h , jni.h include jni_md.h


也可以直接用 #include "jni_md.h" 和 #include "jni.h",要把这两个文件和dll.h放在一个目录下。


然后把我们生成的头文件 myJNI_myJNI.h 的内容全部复制粘贴到 dll.h 文件里,粘贴到下图中选中的位置。



为什么不直接用myJNI_myJNI.h 文件呢?因为如果没有上图里的那几句话,dll 文件是无法编译输出的。你也可以把这几句话

放进myJNI_myJNI.h 文件。


2.编写 c 程序

#include "dll.h"
#include <string.h>


JNIEXPORT
 jint JNICALL JNICALL Java_myJNI_myJNI_intMethod
  (JNIEnv
 *env, jobject obj, jint num)
{
    return num
 * num;
}
 
JNIEXPORT
 jboolean JNICALL JNICALL Java_myJNI_myJNI_booleanMethod
  (JNIEnv
 *env, jobject obj, jboolean boolean)
{
    return !boolean;
}
 
JNIEXPORT
 jstring JNICALL JNICALL Java_myJNI_myJNI_stringMethod
  (JNIEnv
 *env, jobject obj, jstring string)
{
    const char*
 str = (*env)->GetStringUTFChars(env, string, 0);
    char cap[128];
    strcpy(cap,
 str);
    (*env)->ReleaseStringUTFChars(env,
 string, 0);
    return (*env)->NewStringUTF(env,
 strupr(cap));
}
 
JNIEXPORT
 jint JNICALL JNICALL Java_myJNI_myJNI_intArrayMethod
  (JNIEnv
 *env, jobject obj, jintArray array)
{
    int i,
 sum = 0;
    jsize
 len = (*env)->GetArrayLength(env, array);
    jint
 *body = (*env)->GetIntArrayElements(env, array, 0);
     
    for (i
 = 0; i < len; ++i)
    {
        sum
 += body[i];
    }
    (*env)->ReleaseIntArrayElements(env,
 array, body, 0);
    return sum;
}


(*env)->GetStringUTFChars()这个方法, 是用来在Java和C之间转换字符串的, 因为Java本身都使用了双字节的字符, 而C语言本身都是单字节的字符,

所以需要进行转换.

JNIEnv *是每个函数都有的参数, 它包含了很多有用的方法, 使用起来类似Java的反射, 也提供了这样一个编码转换的函数.

GetStringUTFChars()和NewStringUTF(), 第一个是从UTF8转换为C的编码格式, 第二个是根据C的字符串返回一个UTF8字符串.

ReleaseStringUTFChars()是用来释放对象的, 在Java中有虚拟机进行垃圾回收, 但是在C语言中, 这些对象必须手动回收. 否则可能造成内存泄漏.

函数的名字一眼看到就可以猜出功能, jni.h中的大部分函数名都是这样.

之后编译就好了。如果编译不出问题就OK了。


四、运行文件



  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值