工作中常常作为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项目使用