工作中常常作為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);
.......................................
//擷取封包資料
bool getMsg(MSGItem &_item);
//取對應ip的裝置id
int GetIDByIP(int _id);
//批量取對應ip的裝置id
int GetIDByIPRange(int _startId, int _endId,std::map<int,int> &_idMaps);
.......................................................
現在建立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;
}
//JMSGRecive.java
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);
public int GetIDByIP(int _id)
return GetIDByIP_c(mNativeMSGRecive,_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;
//LibJavaEt.java,主程式類
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);
* Method: getMsg_c
* Signature: (ILlib_et1100/JMSGItem;)Z
JNIEXPORT jboolean JNICALL Java_lib_1et1100_JMSGRecive_getMsg_1c
(JNIEnv *, jobject, jint, jobject);
* Method: GetIPByID_c
* Signature: (II)I
JNIEXPORT jint JNICALL Java_lib_1et1100_JMSGRecive_GetIDByIP_1c
(JNIEnv *, jobject, jint obj, jint _id);
* 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);
........................................................................
[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"
(JNIEnv *, jobject)
MSGRecive* p = new MSGRecive();
return (jint)p;
(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;
(JNIEnv *, jobject, jint obj, jint _id)
return p->GetIDByIP(_id);
(JNIEnv *env, jobject, jint obj, jint startId, jint endId, jobject rmap)
std::map<int,int> _idMaps;
jint ret = p->GetIDByIPRange(startId,endId,_idMaps);
cp_map(env,_idMaps,rmap);
//最終項目生成一個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項目使用