将c++静态库实现二次封装供java调用

简介: 工作中常常作为c++开发者,常常需要与java开发人员进行对接,或者他们看重了一些很好的c++库想借用,就需要将这些已有的开发可进行二次封装给java开发调用首先需要从官网下载jdk并安装,例如本人的安装路径:C:\software\java\jdk1.

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


目录
相关文章
|
1月前
|
算法 C++ 容器
C++标准库(速查)总结
C++标准库(速查)总结
61 6
|
1月前
|
存储 算法 C++
C++ STL 初探:打开标准模板库的大门
C++ STL 初探:打开标准模板库的大门
94 10
|
14天前
|
Java API Apache
WK
|
17天前
|
安全 Java 编译器
C++和Java哪个更好用
C++和Java各具优势,选择取决于项目需求、开发者偏好及目标平台特性。C++性能出色,适合游戏、实时系统等;Java平台独立性强,适合跨平台、安全敏感应用。C++提供硬件访问和灵活编程范式,Java有自动内存管理和丰富库支持。两者各有千秋,需根据具体需求选择。
WK
12 1
|
22天前
|
IDE Java 程序员
C++ 程序员的 Java 指南
一个 C++ 程序员自己总结的 Java 学习中应该注意的点。
20 5
|
1月前
|
存储 程序员 C++
C++常用基础知识—STL库(2)
C++常用基础知识—STL库(2)
69 5
|
30天前
|
JSON JavaScript Java
在Java中处理JSON数据:Jackson与Gson库比较
本文介绍了JSON数据交换格式及其在Java中的应用,重点探讨了两个强大的JSON处理库——Jackson和Gson。文章详细讲解了Jackson库的核心功能,包括数据绑定、流式API和树模型,并通过示例演示了如何使用Jackson进行JSON解析和生成。最后,作者分享了一些实用的代码片段和使用技巧,帮助读者更好地理解和应用这些工具。
在Java中处理JSON数据:Jackson与Gson库比较
|
1月前
|
存储 自然语言处理 程序员
C++常用基础知识—STL库(1)
C++常用基础知识—STL库(1)
52 1
WK
|
16天前
|
开发框架 移动开发 Java
C++和Java哪个更适合开发移动应用
本文对比了C++和Java在移动应用开发中的优劣,从市场需求、学习难度、开发效率、跨平台性和应用领域等方面进行了详细分析。Java在Android开发中占据优势,而C++则适合对性能要求较高的场景。选择应根据具体需求和个人偏好综合考虑。
WK
31 0
WK
|
17天前
|
安全 Java 编译器
C++和Java哪个更适合开发web网站
在Web开发领域,C++和Java各具优势。C++以其高性能、低级控制和跨平台性著称,适用于需要高吞吐量和低延迟的场景,如实时交易系统和在线游戏服务器。Java则凭借其跨平台性、丰富的生态系统和强大的安全性,广泛应用于企业级Web开发,如企业管理系统和电子商务平台。选择时需根据项目需求和技术储备综合考虑。
WK
23 0