您的位置:首页 > 编程语言 > C语言/C++

【Java学习之三】用JNI实现Java和C语言的数据传递

2013-11-28 21:51 726 查看
    JNI(Java Native Interface),具体定义我就不多说了。做java和android的开发者应该都知道这个东西。原来做项目的时候使用JNI调用过一个加了com组件的MFC窗口。现在需要通过JNI调用C++实现的函数,该函数每隔一段时间给Java端返回一些数据。今天复习了一下JNI,写了一个小的程序。

    JNI其实就是把Java类中的方法在java端只保留方法的声明,把方法体也就是实现放到其他语言,如C++、matlab中。

    小程序内容是:在C++端有一个int变量,每隔一段时间就+1,并把+1后的值传递给Java端,Java端接收到该数据以后显示到屏幕上。

    具体思想:C++声明一个全局变量int num,两个函数一个counter、一个getNum。在counter里用for循环使num++以后Sleep1秒,getNum把num值返回给JNI。Java端使用两个线程,一个线程运行C++端的counter(),一个线程用while(ture)不停的查询num值,如果发现num改变就打印出来。

    实现如下:

一、Java添加一个类JniTest

    其中声明两个方法counter()和getNum(),不同的就是在声明的时候加上关键字native,代码如下:

package cn.edu.cuit.JNITest;

public class JniTest {

private native void counter();

private native int getNum();

}
    

二、编译Java类,生成JNI的头文件

    cmd进入windows的命令行,使用cd进入到java的工程根目录,如图1:



图1 编译生成jni的头文件
    在编译的时候要可以省略-jni这个参数,直接用javah就行了。此时要注意的是如果没有package cn.edu.cuit.JNITest则直接使用命令:
>javah JniTest
就可以编译了。但是把JniTest类放到包中以后就要在编译的时候加上包的路径,否则编译不过。
    完成编译,查看工程根目录,发现多了一个C语言的头文件cn_edu_cuit_JNITest_JniTest.h。打开此文件内容如下:

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

#ifndef _Included_cn_edu_cuit_JNITest_JniTest
#define _Included_cn_edu_cuit_JNITest_JniTest
#ifdef __cplusplus
extern "C" {
#endif
/*
* Class:     cn_edu_cuit_JNITest_JniTest
* Method:    counter
* Signature: ()V
*/
JNIEXPORT void JNICALL Java_cn_edu_cuit_JNITest_JniTest_counter
(JNIEnv *, jobject);

/*
* Class:     cn_edu_cuit_JNITest_JniTest
* Method:    getNum
* Signature: ()I
*/
JNIEXPORT jint JNICALL Java_cn_edu_cuit_JNITest_JniTest_getNum
(JNIEnv *, jobject);

#ifdef __cplusplus
}
#endif
#endif

注意:把#include<jni.h>改成 #include"jni.h"否则后面会遇到编译错误。

在上面的头文件中有两个函数的声明:
JNIEXPORT void JNICALL Java_cn_edu_cuit_JNITest_JniTest_counter (JNIEnv *, jobject);

JNIEXPORT jint JNICALL Java_cn_edu_cuit_JNITest_JniTest_getNum (JNIEnv *, jobject);

就是我们在java端声明的方法对应的JNI的函数声明。

三、用vs2010建立win32的DLL工程,实现c++端的函数体。
    vs2010 -> 新建项目 -> Win32项目 -> 下一步 ->应用程序类型选择DLL -> 完成。
    在java的jdk安装目录下的jdk1.*** \ \include \\ 下面找到jni.h
    在java的jdk1.*** \ \include \\  win32 \\ 下面找到jni_md.h
    把两个文件拷贝到刚才建立的C++的工程目录下。
    注意用vs2010会有两个JniTest目录,JniTest \\ JniTest 第一个是解决方案的目录,第二哥是工程目录,不要放错位置了。
    把刚才生成的cn_edu_cuit_JNITest_JniTest.h也拷贝到JniTest工程目录下面。
    在C++的工程头文件中添加这三个头文件;在源文件中建立以个JniTest.cpp的文件,如图2:



图2 c++程序的工程文件
   在JniTest.cpp 实现这两个方法:

#include "stdafx.h"
#include "jni.h"
#include <stdio.h>
#include<iostream>
#include "cn_edu_cuit_JNItest_JniTest.h"

using namespace std;

int num = 0;

JNIEXPORT void JNICALL Java_cn_edu_cuit_JNITest_JniTest_counter
(JNIEnv *, jobject)
{
for(int i=0; i<100; i++)
{
num++;
Sleep(1000);
}
}

JNIEXPORT jint JNICALL Java_cn_edu_cuit_JNITest_JniTest_getNum
(JNIEnv *, jobject)
{
return num;
}
    代码很简单我就不解释了,注意添加相关的.h文件。

    编译生成dll文件,注意生成的dll文件是放在解决方案的Debug目录里面,不是在工程目录的Debug里面。之前做JNI用的是VC6.0,生成的dll是在工程目录下的Debug下,用vs2010要注意这个问题。
    在Debug目录下发现JniTest.cpp生成的JniTest.dll文件,把它拷贝到java的工程目录>Chart0.6下,也可以拷贝到一个自己专门存放dll的目录中,并在windows的环境变量中添加这个目录。

四、完善Java端代码
    在Java类JniTest中load这个dll类库,代码如下;

public class JniTest {

private native void counter();

private native int getNum();

static{
System.loadLibrary("JniTest");
}


    然后在继续在类中添加main函数,代码如下:

package cn.edu.cuit.JNITest;

public class JniTest { private native void counter(); private native int getNum(); static{ System.loadLibrary("JniTest"); }

static int oldNum;

public static void main(String[] args)
{
final JniTest jniTest = new JniTest();
Runnable printRunnable = new Runnable() {
public void run() {
while(true)
{
int newNum = jniTest.getNum();
if(newNum != oldNum)
{
System.out.println(newNum);
oldNum = newNum;
}
try {
Thread.sleep(500);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
};
Thread printThread = new Thread(printRunnable);
Runnable counterRunnable = new Runnable() {
public void run() {
jniTest.counter();
}
};

Thread counterThread = new Thread(counterRunnable);
printThread.start();
counterThread.start();
}

}


main函数中启用两个线程,一个counterThread中调用JniTest类中的counter方法,另一个线程printThread每个0.5秒调用一次getNum,得到一个newNum值,与保存的oldNum值做比较,如果相同则说明C++端的num还没有变化,如果不同则打印出这个值,并更新oldNum的值。

Java在传String给C++或者C语言的时候的处理很多地方都找的到,就是使用GetStringUTFChars和NewStringUTF,我就不详述了。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息