目录

一、jdk准备

二、c++封装

        2.1 建立对应的java类及结构

        2.2 使用javah命令(javah  类的全路径)

        2.3 c++封装成java调用的动态库

        2.4 java调用

三、实现总结


一、jdk准备

        工作中常常作为c++开发者,常常需要与java开发人员进行对接,或者他们看重了一些很好的c++库想借用,就需要将这些已有的开发可进行二次封装给java开发调用

首先需要从官网下载jdk并安装,例如本人的安装路径:C:\software\java\jdk1.8.0_45\

二、c++封装

        假如我们目前已有一组c++的头文件及库文件

        2.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类,并该类引用了ReData.h文件中的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

//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

//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

//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.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

        2.3 c++封装成java调用的动态库

        [1]需要建立一个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命令生成的头文件加入到项目中。

        [2]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));
                }
            }
        }
    }
};

                [3]实现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里,

        2.4 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

三、实现总结

        梳理一下,

        【1】首先知道 c++lib的头文件及库需要调用,然后在java工程/src/lib_et1100建立对应的java类;

        【2】通过javah命令生成java工程,bin下的c++头文件及/bin/lib_et110的.class文件;

        【3】然后建立库工程jk_dll,将c++lib的头文件及库以及javah命令输出的头文件加载到项目,并实现javah命令输出的头文件的源代码;

        【4】jk_dll项目生成动态库到java工程bin下,供java项目使用。