本文共 8247 字,大约阅读时间需要 27 分钟。
工作中常常作为c++开发者,常常需要与java开发人员进行对接,或者他们看重了一些很好的c++库想借用,就需要将这些已有的开发可进行二次封装给java开发调用
首先需要从官网下载jdk并安装,例如本人的安装路径:C:\software\java\jdk1.8.0_45\
假如我们目前已有一组c++的头文件及库文件
[1]建立对应的java类及结构
c++头文件ReData.h里有一个结构数据:
///ReData.h
struct MSGItem
{ MSGItem() : chl_id(0), t_sec(0),t_usec(0),_direct(0),_msg("") { }; ~MSGItem() { }; MSGItem& operator=(const MSGItem& rhs) { if(this!=&rhs){ this->chl_id = rhs.chl_id; this->t_sec = rhs.t_sec; this->t_usec = rhs.t_usec; this->_direct = rhs._direct; this->_msg = rhs._msg; } return *this; }; unsigned int chl_id; unsigned int t_sec; /* seconds */ unsigned int t_usec; /* and microseconds */ int _direct; std::string _msg; };//ReData.h
假如有一个MSGRecive类,并该类引用了MSGItem结构,
//MSGRecive.h
class MSGRecive
{ public: MSGRecive(void); ~MSGRecive(void); public:.......................................
//获取报文数据
bool getMsg(MSGItem &_item); //取对应ip的设备id int GetIDByIP(int _id); //批量取对应ip的设备id int GetIDByIPRange(int _startId, int _endId,std::map<int,int> &_idMaps);.......................................................
};
//MSGRecive.h
现在建立java与c++结构数据对应的结构及类,例如
建立一个java工程,在src\lib_et1100目录下建立对应的java类
//JMSGItem.java
package lib_et1100;
public class JMSGItem { public int chl_id; public int t_sec; /* seconds */ public int t_usec; /* and microseconds */ public int _direct; public String _msg; }//JMSGItem.java
//JMSGRecive.java
package lib_et1100;
import java.util.Map; import lib_et1100.JMSGItem; public class JMSGRecive {static
{ System.loadLibrary("jk_dll"); } public JMSGRecive() { mNativeMSGRecive = init(); }........................................................
//获取报文数据
public boolean getMsg(JMSGItem _item) { return getMsg_c(mNativeMSGRecive,_item); } //取对应ip的设备id public int GetIDByIP(int _id) { return GetIDByIP_c(mNativeMSGRecive,_id); } //批量取对应ip的设备id public int GetIDByIPRange(int _startId, int _endId, Map _idMaps) { return GetIDByIPRange_c(mNativeMSGRecive,_startId,_endId,_idMaps); }........................................................................................
private native int init();
private native int GetIDByIP_c(int mMSGRecive,int _id);
private native boolean getMsg_c(int mMSGRecive,JMSGItem _item); private native int GetIDByIPRange_c(int mMSGRecive,int _startId, int _endId, Map _idMaps);private int mNativeMSGRecive;
}//JMSGRecive.java
//LibJavaEt.java,主程序类
package lib_et1100;
public class LibJavaEt {
public static void main(String[] args) {
JMSGRecive msg = new JMSGRecive();
..............................................................
JMSGItem it = new JMSGItem();
boolean ret = msg.getMsg(it);.....................................................................
}
}//LibJavaEt.java
[2]使用javah命令(javah 类的全路径)
javah .classpath -jni
//.classpath conf///
<?xml version="1.0" encoding="UTF-8"?>
<classpath> <classpathentry kind="src" path="src"/> <classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER"/> <classpathentry kind="con" path="org.eclipse.jdt.USER_LIBRARY/jk_dll"> <attributes> <attribute name="org.eclipse.jdt.launching.CLASSPATH_ATTR_LIBRARY_PATH_ENTRY" value="jk_lib_sample/bin"/> </attributes> </classpathentry> <classpathentry kind="output" path="bin"/> </classpath>///
.classpath配置java源src及输出bin目录,
然后生成本地方法的C++头文件,例如lib_et1100_JMSGRecive.h到bin目录,并在bin下会生成一组对应的java的.class文件,
例如lib_et1100/JMSGItem.class, lib_et1100/JMSGRecive.class,
在lib_et1100_JMSGRecive.h文件中,lib_et1100_JMSGRecive对应lib_et1100/JMSGItem.class的输出
javah .classpath
//lib_et1100_JMSGRecive.h
#include <jni.h>
#ifdef __cplusplus
extern "C" { #endif..................................................................................................
/*
* Class: lib_et1100_JMSGRecive * Method: init * Signature: ()I */JNIEXPORT jint JNICALL Java_lib_1et1100_JMSGRecive_init
(JNIEnv *, jobject);/*
* Class: lib_et1100_JMSGRecive * Method: getMsg_c * Signature: (ILlib_et1100/JMSGItem;)Z */ JNIEXPORT jboolean JNICALL Java_lib_1et1100_JMSGRecive_getMsg_1c (JNIEnv *, jobject, jint, jobject);/*
* Class: lib_et1100_JMSGRecive * Method: GetIPByID_c * Signature: (II)I */JNIEXPORT jint JNICALL Java_lib_1et1100_JMSGRecive_GetIDByIP_1c
(JNIEnv *, jobject, jint obj, jint _id);/*
* Class: lib_et1100_JMSGRecive * Method: GetIDByIPRange_c * Signature: (IIILjava/util/Map;)I */JNIEXPORT jint JNICALL Java_lib_1et1100_JMSGRecive_GetIDByIPRange_1c
(JNIEnv *env, jobject, jint obj, jint startId, jint endId, jobject rmap); ........................................................................#ifdef __cplusplus
} #endif//lib_et1100_JMSGRecive.h
[3]需要建立一个c++项目将这些库封装成java调用的动态库,
我们建立一个c++项目并设置jdk的头及库调用
C:\software\java\jdk1.8.0_45\include
C:\software\java\jdk1.8.0_45\include\win
C:\software\java\jdk1.8.0_45\lib
同时将需要java调用的c++的头及库文件加入项目
并将javah命令生成的头文件加入到项目中,
[4]c++数据结构转换Java数据结构
我们在StructDataTran.h将c++结构数据进行转换
//StructDataTran.h
#include <jni.h>//JNI是Java Native Interface的缩写,中文为JAVA本地调用。使用JNI可以很方便的用我们的Java程序调用C/C++程序。
#include <map>
#include "ReData.h"
void cp_msgitem(JNIEnv *env,MSGItem_item,jobject ritem)
{ jclass ritem_c = env->GetObjectClass(ritem); if(NULL!=ritem_c) { //获取相关数据; jfieldID _direct = env->GetFieldID(ritem_c,"_direct","I"); jfieldID chl_id = env->GetFieldID(ritem_c,"chl_id","I"); jfieldID t_sec = env->GetFieldID(ritem_c,"t_sec","I"); jfieldID t_usec = env->GetFieldID(ritem_c,"t_usec","I"); jfieldID _msg = env->GetFieldID(ritem_c,"_msg","Ljava/lang/String;"); //设置相关数据 env->SetIntField(ritem,_direct,_item._direct); env->SetIntField(ritem,chl_id,_item.chl_id); env->SetIntField(ritem,t_sec,_item.t_sec); env->SetIntField(ritem,t_usec,_item.t_usec); jstring str = env->NewStringUTF(_item._msg.c_str()); env->SetObjectField(ritem,_msg,str); }};
//还需要转换标准库的一些结构及容器类
void cp_map(JNIEnv *env,std::map<int,int> _idMaps,jobject rmap)
{ if(!_idMaps.empty()){ jclass ritem_c = env->GetObjectClass(rmap); //jclass ritem_c = env->FindClass("java/util/HashMap"); if(NULL!=ritem_c) { jmethodID HashMap_put = env->GetMethodID(ritem_c, "put", "(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;"); if (HashMap_put == NULL) printf("method ID jput not valid\n\n"); else { for(std::map<int,int>::iterator it=_idMaps.begin();it!=_idMaps.end();it++) { char key[64]={0}; itoa(it->first,key,10); char val[64]={0}; itoa(it->second,val,10); env->CallVoidMethod(rmap,HashMap_put,env->NewStringUTF(key),env->NewStringUTF(val)); } } } } };[5]实现javah命令生成的头文件的源代码
///lib_et1100_JMSGRecive.cpp
#include "lib_et1100_JMSGRecive.h"
#include "MSGRecive.h"
#include "StructDataTran.h"..................................................................................................
JNIEXPORT jint JNICALL Java_lib_1et1100_JMSGRecive_init
(JNIEnv *, jobject){
MSGRecive* p = new MSGRecive(); return (jint)p; }; JNIEXPORT jboolean JNICALL Java_lib_1et1100_JMSGRecive_getMsg_1c (JNIEnv *, jobject, jint, jobject){
MSGRecive* p = (MSGRecive*)obj; MSGItem _item; jboolean ret = p->getMsg(_item); if(!ret){ printf("getMsg error!\n"); }else{ cp_msgitem(env,_item,ritem); } return ret; };JNIEXPORT jint JNICALL Java_lib_1et1100_JMSGRecive_GetIDByIP_1c
(JNIEnv *, jobject, jint obj, jint _id) { MSGRecive* p = (MSGRecive*)obj; return p->GetIDByIP(_id); };JNIEXPORT jint JNICALL Java_lib_1et1100_JMSGRecive_GetIDByIPRange_1c
(JNIEnv *env, jobject, jint obj, jint startId, jint endId, jobject rmap) { MSGRecive* p = (MSGRecive*)obj; std::map<int,int> _idMaps; jint ret = p->GetIDByIPRange(startId,endId,_idMaps); cp_map(env,_idMaps,rmap); return ret; };........................................................................
///lib_et1100_JMSGRecive.cpp
//最终项目生成一个dll文件,例如jk_dll.dll文件,将动态库输出到java工程的bin里,
[6]java调用
现在在java工程里,这些java的类及结构数据调用类似
注意JNI为JAVA本地调用,会丧失平台可移植性,工程结构如下
java工程:
jk_et1100_interface bin lib_et1100 JMSGItem.class JMSGRecive.class LibJavaEt.class jk_dll.dll lib_et1100_JMSGRecive.h src lib_et1100 JMSGItem.java JMSGRecive.java LibJavaEt.java .classpath .project jk_dll库项目: jk_dll jk_et1100_interface bin lib_et1100_JMSGRecive.h c++lib ReData.h MSGRecive.h c++Lib.lib src lib_et1100_JMSGRecive.cpp StructDataTran.h StructDataTran.cpp梳理一下,首先知道 c++lib的头文件及库需要调用,然后在java工程/src/lib_et1100建立对应的java类
通过javah命令生成java工程下,bin下的c++头文件及/bin/lib_et110的.class文件,
然后建立库工程jk_dll,将c++lib的头文件及库以及javah命令输出的头文件加载到项目,并实现javah命令输出的头文件的源代码,
jk_dll项目生成动态库到java工程bin下,供java项目使用
转载地址:http://brkdm.baihongyu.com/