大家好,今天分享的是使用C/C++编写一个读取串口数据的代码,然后将其编译成Windows下的动态链接库(.dll文件),然后写一个简单的java demo来调用C/C++接口。
众所周知,java开发项目会比较方便,尤其是在一些大型项目中,java开发效率会比较高,但是一些底层的东西和一些追求效率的东西,依然会倾向使用C/C++,这是他们不可替代的优势。所以有时会需要两者混合起来,C/C++完成一部分较底层的功能,提供接口给java调用。
由于本人主要是从事嵌入式相关,对java也不是很熟,所以今天主要是把整个流程过一遍,把整个流程打通,搞清楚如何制作动态库,如何被java调用即可。关于动态库的内容,还可以参考我之前的文章。
还有就是今天所有的操作都是在命令行中完成,不会使用任何IDE ,这也符合我们嵌入式开发的习惯,能用命令行就没必要去安装臃肿的IDE软件,所以大家需要先在自己的DOS窗口中安装好必要的工具,g++用于编译c++代码,安装java的jdk以提供java环境,另外把nmake路径添加到环境变量中就可以使用Makefile了。
一、编写cpp功能函数
这一部分就是具体的功能实现,比如在本次中,我们需要读取串口数据,那么至少涉及四个接口:打开串口,设置波特率等参数,读取数据,关闭串口。我们需要使用c++代码把这四个接口的具体实现写出来,新建一个文件夹,用于存放文件,在里面新建一个dllApi.cpp和dllApi.h文件。
dllApi.h:
#ifndef DLLAPI_H
#define DLLAPI_H
#include
#include
#include
#include
using namespace std;
class ComHelper {
public:
// bool Open(void); //打开串口
void DLL_API_Set(int baud); //设置串口信息
char* DLL_API_Read(char str[],int length);
bool DLL_API_Close(void);
bool DLL_API_OPEN(void);
};
dllApi.cpp:
#include "dllApi.h"
HANDLE hCom ;
bool ComHelper::DLL_API_OPEN(void)
{
int num;
const char* com="COM";
char buf[100]={0};
cout<<"请输入要打开的串口号,输入1打开COM1"<
这里面主要是涉及两个很重要的函数,CreateFile 和 ReadFile 函数,这两个函数是Windows下的API,可以直接调用,关于具体的函数功能及用法,这里暂时不讨论,其实和linux下的驱动是很类似的。
二、编写一个java的demo
Java2cpp.java :
public class Java2cpp
{
static
{
System.loadLibrary("javaCallcpp");
}
public native boolean DLL_OPEN();
public native void DLL_Set(int baud); //设置串口信息
public native String DLL_Read(char str[],int length);
public native boolean DLL_Close();
public static void main(String args[])
{
System.out.println("code test....");
boolean ret;
char buf[]={0};
String str;
Java2cpp com = new Java2cpp();
ret=com.DLL_OPEN();
if(!ret)
{
System.out.println("打开串口失败");
return;
}
System.out.println("打开串口成功");
com.DLL_Set(115200);
while(true)
{
str=com.DLL_Read(buf, 100);
System.out.println(str);
}
}
}
这里主要注意两个点,一个是使用System.loadLibrary( "javaCallcpp" );导入了一个库,这个库的名字是javaCallcpp 也就是说待会儿我们要生成一个javaCallcpp.dll的文件。第二点是public native boolean DLL_OPEN ();等几个API。
使用native关键字说明这个方法是原生函数,也就是这个方法是用C/C++语言实现的,并且被编译成了DLL,由java去调用。使用native关键字说明这个方法是原生函数,也就是这个方法是用C/C++语言实现的,并且被编译成了DLL,由java去调用。这些函数的实现体在DLL中,JDK的源代码中并不包含,你应该是看不到的。对于不同的平台它们也是不同的。这也是java的底层机制,实际上java就是在不同的平台上调用不同的native方法实现对操作系统的访问的。
这个时候还没有dll文件,因此有了第三步:
三、生成dll文件
首先进入到文件目录,在命令行中使用
javac -h ./ Java2cpp.java
命令生成Java2cpp.h文件,这个头文件是不可修改的,大概长这样
接下来新建一个Java2cpp.cpp文件,在这个文件中调用第一步中实现的接口
Java2cpp.cpp :
#include "Java2cpp.h"
#include "dllApi.h"
JNIEXPORT jboolean JNICALL Java_Java2cpp_DLL_1OPEN
(JNIEnv *, jobject)
{
ComHelper com;
bool var=0;
var=com.DLL_API_OPEN();
return var;
}
JNIEXPORT void JNICALL Java_Java2cpp_DLL_1Set
(JNIEnv *, jobject, jint baud)
{
ComHelper com;
com.DLL_API_Set(115200);
return;
}
JNIEXPORT jstring JNICALL Java_Java2cpp_DLL_1Read
(JNIEnv * env, jobject, jcharArray ay, jint)
{
char array[1024];
char* buf=array;
int len=strlen(buf);
jstring ret;
ComHelper com;
com.DLL_API_Read(array,100);
//将char[] 转化为jstring
//定义java String类 strClass
jclass strClass = (env)->FindClass("Ljava/lang/String;");
//获取java String类方法String(byte[],String)的构造器,用于将本地byte[]数组转换为一个新String
jmethodID ctorID = (env)->GetMethodID(strClass, ", "([BLjava/lang/String;)V");
//建立byte数组
jbyteArray bytes = (env)->NewByteArray((jsize)strlen(buf));
//将char* 转换为byte数组
(env)->SetByteArrayRegion(bytes, 0, (jsize)strlen(buf), (jbyte*)buf);
//设置String, 保存语言类型,用于byte数组转换至String时的参数
jstring encoding = (env)->NewStringUTF("gbk");
//将byte数组转换为java String,并输出
ret= (jstring)(env)->NewObject(strClass, ctorID, bytes, encoding);
return ret;
}
JNIEXPORT jboolean JNICALL Java_Java2cpp_DLL_1Close
(JNIEnv *, jobject)
{
bool ret;
ComHelper com;
ret=com.DLL_API_Close();
return ret;
}
实际上你也可以直接在这里实现具体的功能代码,这样就省掉第一步了,不过为了一个分层的思想,更方便维护,还是不要省掉第一步比较好。
在这个文件中,使用#include "dllApi.h" 来调用第一步中的接口,然后这个文件是被java程序调用的,这里要稍微注意一下数据类型的转化。 比如char[] 转化为jstring。
然后在命令行中将前面的dllApi.cpp 和这个Java2cpp.cpp同时编译成dll文件。
g++ -shared -fPIC Java2cpp.cpp dllApi.cpp -o javaCallcpp.dll -I "F:\\Program Files\\Java\\jdk-11.0.12\\include" -I "F:\\Program Files\\Java\\jdk-11.0.12\\include\\win32"
这样在目录中就出现了javaCallcpp.dll文件。
四、编译并运行java程序
在命令行中输入
javac Java2cpp.java
生成Java2cpp.class文件,.class文件就是java编译后的可执行文件
最后在命令行中输入
java Java2cpp //注意没有.class后缀
就可以运行java程序了。
这样就成功实现了java调用dll库,我们也可以将上面那些命令写成Makefile文件,和linux下的Makefile是一样的,只不过在Windows下不是make命令,而是nmake,使用时需要将nmake的路径添加到系统环境变量中。
总结:
1、编写cpp具体的功能接口代码
2、编写java程序,使用native关键字声明调用本地接口
3、javac -h (在旧版本中直接使用javah)生成头文件,根据头文件编写对应cpp源文件
4、使用g++ 编译生成.dll文件
5、使用javac xxx.java生成xxx.class文件并执行
当然如果不习惯使用命令行,也可以结合Visual Studio 和 Eclipse 两个IDE进行操作,在这里不做阐述。
全部0条评论
快来发表一下你的评论吧 !