C++封装成Jni库的详细步骤


前段时间用C++实现了一个数据库连接的类,类中包含了一些连接和断开操作。我想在java中调用这些功能。jni方法提供了只是函数接口的调用而没有类的调用(有关函数接口调用的网上有很多资料非常简单,可以自行查阅),后来决定采用用c/c++函数重新封装类里的操作的方法,使得java在调用时直接调用c/c++提供里面的函数接口而不直接调用类。在看该文时建议先看一下jni的使用。
现在一步步往下做。为了简化操作,这里不使用数据库连接的类,而是编写了一个有关两个整数运算的类来模拟数据库类。该类实现整数的加减乘除。这个非常重要,但是很普遍,可以上网查看一下,都有配置环境的介绍,这里不在叙述。

第一步:配置java环境

这个非常重要,但是很普遍,可以上网查看一下,都有配置环境的介绍,这里不在叙述。

第二步:实现一个类,包含一些简单操作

//Twono.h

#ifndef TWONO_H
#define TWONO_H


class TwoNo{
    int i,j;
public:
    TwoNo();
    TwoNo(int,int);
    int Add();//加
    int Divide();//除
    int Multi();//乘
    int Sub();//减
    ~TwoNo();

};
#endif


////Twono.cpp

#include "twono.h"

TwoNo::TwoNo()
{
    i = 0;
    j = 0;
    
}
TwoNo::TwoNo(int a,int b)
{
    i = a;
    j = b;
}
TwoNo::~TwoNo()
{
}

int TwoNo::Add()
{
    return i+j;
}

int TwoNo::Sub()
{
    return i-j;
}

int TwoNo::Multi()
{
    return i*j;
}

int TwoNo::Divide()
{
    return i/j;
}

做好该类之后,我打成libTwono.so包,并将它放到/usr/lib下: g++ -fPIC -shared -o libTwono.so Twono.cpp

接下来为了能在java中使用类TwoNo中的Add方法,我现在在Add.java文件中来声明native函数add(),add()函数封装了类Twono中的Add方法,native限定词是实现jni本地调用的关键字,说明了该方法是需要通过其他语言来实现的。

//Add.java
public  class Add
{
    static
    {
        System.load("//usr/lib/libA.so");//libA.so是用C封装类操作后生成的包,这里可以先随便命名
    }
    public native int add(int a,int b); //声明的add()函数,在后面他封装了类Twono中的Add()函数
    public static void  main(String[] args)
    {    了
        Add a = new Add();
        System.out.println("hello" + a.add(3,1));
    }   
}

该java文件的预期输出结果应该是hello4.
用命令javac Add.java 生成Add.class
用命令javah Add 生成Add.h ,Add.h是后面Add.cpp的头文件,该头文件包含了java中映射到其他语言转换后的函数,具体形式如下:

//Add.h
#include <jni.h>


#ifndef _Included_Add
#define _Included_Add
#ifdef __cplusplus
extern "C" {
#endif

JNIEXPORT jint JNICALL Java_Add_add
  (JNIEnv *, jobject, jint, jint);       //该函数就是Add.java中的add()函数,具体形式如果有不明白的可以上网查看一下,有非常多的介绍,前面两个参数可以一般不理,他是jni函数都带有的。如果
  //你使用到java中传过来的参数类型像string之类的,但int,bool,byte之类的没必要使用前面两个参数,后面jint,jint 就是add()函数本身的参数

#ifdef __cplusplus
}
#endif
#endif

第三步:编写Add.cpp 封装类Twono中的Add()

//Add.cpp
#include "Add.h"
#include <iostream>
#include "twono.h"       //包含你所使用类的头文件
using namespace std;
JNIEXPORT jint JNICALL Java_Add_add
  (JNIEnv *, jobject, jint a, jint b)
{    TwoNo t(a,b);      //声明该类
    return t.Add();
}
//在.cpp中使用了类Twono中的Add();

第四步:将该Add.cpp生成动态连接库

生成命令: g++ -I/java/jdk1.5.0_08/include -L/usr/lib -shared -o libA.so Add.cpp -lTwono
在编译生成动态连接库的时候需要包含你要使用的类的动态链接库,上面的-I/java/jdk1.5.0_08/include指明了java的一些头文件,-L/usr/lib指明了要使用到的一些类库的所在地,像libTwono.so就在它下面,-lTwono表示Add.cpp生成的动态链接库libA.so需要使用到libTwono.so里的函数,这些库可以包含多个,只要正确指明他的路径和名称即可
并将生成的libA.so放到正确位置

第五步:重新编译

重新来编译Add.java
javac Add.java
java Add
如果没问题的话就应该可以出结果了:hello4

可能出现的一些问题:

1:使用g++编译和gcc编译c和cpp文件生成动态链接库时可能会出现一些格式不符合的问题,比如
Exception in thread “main” java.lang.NoClassDefFoundError: Hell
因此,如果使用gcc编译没通过,你可以使用g++生成
2:完成上面步骤后报错:
Exception in thread “main” java.lang.UnsatisfiedLinkError: /usr/lib/libA.so: /usr/lib/libA.so: undefined symbol: _ZN5TwoNoC1Eii
at java.lang.ClassLoader$NativeLibrary.load(Native Method)
at java.lang.ClassLoader.loadLibrary0(ClassLoader.java:1751)
at java.lang.ClassLoader.loadLibrary(ClassLoader.java:1647)
at java.lang.Runtime.load0(Runtime.java:769)
at java.lang.System.load(System.java:968)
at Add.(Add.java:5)

该错误应该是生成libA.so时没有包含它所需要的动态连接库libTwono,因此在g++ -I/java/jdk1.5.0_08/include -L/usr/lib -shared -o libA.so Add.cpp -lTwono
最后一定要包含-lTwono(即它所需要的库)

猜你喜欢

转载自blog.csdn.net/buknow/article/details/86064142