Swig java Jni开发指南
目录
Swig java Jni开发指南
- Swig参考文档
- 全局变量
- 常量
- 只读变量
- 枚举
- 函数
- 结构体
- 类
- 调用函数指针
- 回调
- 类型转换
- 两个常用命令
简介:
JNI:Java Native Interface,它允许Java代码和其他语言(尤其C/C++)写的代码进行交互,只要遵守调用约定即可。
JNA:Java Native Access是一个开源的Java框架,是Sun公司推出的一种调用本地方法的技术,是建立在经典的JNI基础之上的一个框架。之所以说它是JNI的替 代者,是因为JNA大大简化了调用本地方法的过程,使用很方便,基本上不需要脱离Java环境就可以完成。
Swig可以根据c或c++代码生成jni代码的工具,大大简化jni的开发
Jnaerator可以根据c或c++代码生成jna代码的工具,大大简化jna的开发
从难易度看,使用jnaerator开发jna最简单,代码基本都是自动生成,但是jna开发有个很大的缺点,就是如果c代码过于复杂,比如出现java调用c,然后c再回调java,java返回的结果c还需要继续处理的时候,经常出现不可控制的crash,而jna算是中间层,这个层出现的错误完全无法调试,被逼无奈,我们的项目先用jna开发,不得不转jni开发,在使用swig的过程中,也遇到不少问题,因此总结如下:
- Swig参考文档
http://www.swig.org/Doc3.0/SWIGDocumentation.html
swig是一个编译时软件开发工具,它能生成将用c/c++编写的原生模块与包括java在内的其他编程语言进行链接的必要代码。Swig不仅是一个代码生成器,还是一个接口编译器。它不定义新的协议,也不是一个组件框架或者一个特定的运行时库。Swig把接口文件看做输入,并生成必要的代码在java中展示接口,从而让java能够理解原生代码中的接口定义。Swig不是一个存根生成器;它产生将要被编译和运行的代码。
Swig可应用于包括windows、mac os x和linux在内的大多数操作系统平台。大家可以参考官网文档安装。
使用swig需要生成一个.i的接口文件,swig接口文件包含函数原型、类和变量声明,它的语法和普通的c/c++头文件一样。除了c/c++关键字和预处理器指令,接口文件还包含swig特有的预处理器指令,该指令可用于优化生成封装代码。 - 全局变量
a) 编写example.h
int counter = 0;
b) 编写example.i
%module example
%{
#include "example.h"
%}
extern int counter;
c) 编写runme.java
/ runme.java
ublic class runme {
static {
System.loadLibrary("example");
}
public static void main(String argv[]) {
System.out.println(example.getCounter());
example.setCounter(1);
System.out.println(example.getCounter());
}
d) 执行以下命令
swig -java example.i
gcc -Wall -Wl,--kill-at -I "%JAVA_HOME%/include" -I "%JAVA_HOME%/include/win32" -shared -s -o example.dll *.c
javac -d . *.java
java -cp . runme
- 常量
a) 编写example.i
%module example
/* 用define指令定义常量 */
#define MAX_WIDTH 640
/* 用%constant指令定义常量 */
%constant int MAX_HEIGHT = 320;
b) 编写runme.java
// runme.java
public class runme {
static {
System.loadLibrary("example");
}
public static void main(String argv[]) {
System.out.println(example.MAX_WIDTH);
System.out.println(example.MAX_HEIGHT);
}
}
c) 执行以下命令,同1.d
- 只读变量
区别只是在生成的包装类中,只读的只有get函数,读写的有get和set函数
a) 编写example.h
/* 只读变量 */
int readOnly;
/* 读-写变量 */
int readWrite;
b) 编写example.i
%module example
%{
#include "example.h"
%}
/* 只读变量 */
%immutable readOnly;
int readOnly;
/* 读-写变量 */
int readWrite;
c) 编写runme.java
// runme.java
public class runme {
static {
System.loadLibrary("example");
}
public static void main(String argv[]) {
System.out.println(example.getReadOnly());
example.setReadWrite(10);
System.out.println(example.getReadWrite());
}
}
d) 执行以下命令,同1.d
- 枚举
a) 编写example.h
/* 匿名枚举 */
enum {ONE = 1, TWO = 2, THREE, FOUR};
/* 命名枚举 */
enum Color { RED = 1, GREEN, BLANK, YELLOW};
enum SeasonEnum{ SPRING = 1,SUMMER,AUTUMN,WINTER};
b) 编写example.i
%module example
%{
#include "example.h"
%}
/* 匿名枚举 */
enum {ONE = 1, TWO = 2, THREE, FOUR};
/* 命名枚举 */
enum Color { RED = 1, GREEN, BLANK, YELLOW};
/* 类型不安全,这个用起来最简单 */
%include "enumtypeunsafe.swg"
%javaconst(1);
enum SeasonEnum{ SPRING = 1,SUMMER,AUTUMN,WINTER};
c) 编写runme.java
// runme.java
public class runme {
static {
System.loadLibrary("example");
}
public static void main(String argv[]) {
System.out.println(example.ONE + "," + example.TWO + "," + example.THREE + "," + example.FOUR);
System.out.println(Color.RED.swigValue() + "," + Color.GREEN.swigValue() + "," + Color.BLANK.swigValue() + "," + Color.YELLOW.swigValue());
System.out.println(SeasonEnum.SPRING + "," + SeasonEnum.SUMMER + "," + SeasonEnum.AUTUMN + "," + SeasonEnum.WINTER);
}
}
d) 执行以下命令,同1.d
- 函数
这里直接抄swigwin-3.0.12\Examples\java\simple
a) 编写example.c
/* File : example.c */
/* A global variable */
double Foo = 3.0;
/* Compute the greatest common divisor of positive integers */
int gcd(int x, int y) {
int g;
g = y;
while (x > 0) {
g = x;
x = y % x;
y = g;
}
return g;
}
b) 编写example.i
/* File : example.i */
%module example
%inline %{
extern int gcd(int x, int y);
extern double Foo;
%}
c) 编写runme.java
public class runme {
static {
try {
System.loadLibrary("example");
} catch (UnsatisfiedLinkError e) {
System.err.println("Native code library failed to load. See the chapter on Dynamic Linking Problems in the SWIG Java documentation for help.\n" + e);
System.exit(1);
}
}
public static void main(String argv[]) {
// Call our gcd() function
int x = 42;
int y = 105;
int g = example.gcd(x,y);
System.out.println("The gcd of " + x + " and " + y + " is " + g);
// Manipulate the Foo global variable
// Output its current value
System.out.println("Foo = " + example.getFoo());
// Change its value
example.setFoo(3.1415926);
// See if the change took effect
System.out.println("Foo = " + example.getFoo());
}
}
d) 执行以下命令,同1.d
- 结构体
a) 编写example.h
// example.h
struct Books
{
char title[50];
char author[50];
char subject[100];
int book_id;
};
extern void show(struct Books* book);
b) 编写example.c
// example.c
#include
#include "example.h"
// struct Books book = {"C 语言", "RUNOOB", "编程语言", 123456};
void show(struct Books* book)
{
printf("title : %s\nauthor: %s\nsubject: %s\nbook_id: %d\n", book->title, book->author, book->subject, book->book_id);
}
c) 编写example.i
/* File : example.i */
%module example
%{
#include "example.h"
%}
%include "example.h"
d) 编写runme.java
// runme.java
public class runme {
static {
System.loadLibrary("example");
}
public static void main(String argv[]) {
Books book = new Books();
book.setTitle("C 语言");
book.setAuthor("RUNOOB");
book.setSubject("编程语言");
book.setBook_id(123456);
example.show(book);
}
}
e) 执行以下命令,同1.d
- 类
这里直接抄swigwin-3.0.12\Examples\java\ class,对比java调用c的结构体来看,两个的处理完全一样,都是把c的结构或类包装成java类使用
a) 编写example.h
/* File : example.h */
class Shape {
public:
Shape() {
nshapes++;
}
virtual ~Shape() {
nshapes--;
}
double x, y;
void move(double dx, double dy);
virtual double area() = 0;
virtual double perimeter() = 0;
static int nshapes;
};
class Circle : public Shape {
private:
double radius;
public:
Circle(double r) : radius(r) { }
virtual double area();
virtual double perimeter();
};
class Square : public Shape {
private:
double width;
public:
Square(double w) : width(w) { }
virtual double area();
virtual double perimeter();
};
b) 编写example.cxx
/* File : example.cxx */
#include "example.h"
#define M_PI 3.14159265358979323846
/* Move the shape to a new location */
void Shape::move(double dx, double dy) {
x += dx;
y += dy;
}
int Shape::nshapes = 0;
double Circle::area() {
return M_PI*radius*radius;
}
double Circle::perimeter() {
return 2*M_PI*radius;
}
double Square::area() {
return width*width;
}
double Square::perimeter() {
return 4*width;
}
c) 编写example.i
/* File : example.i */
%module example
%{
#include "example.h"
%}
/* Let's just grab the original header file here */
%include "example.h"
d) 编写runme.java
// This example illustrates how C++ classes can be used from Java using SWIG.
// The Java class gets mapped onto the C++ class and behaves as if it is a Java class.
public class runme {
static {
try {
System.loadLibrary("example");
} catch (UnsatisfiedLinkError e) {
System.err.println("Native code library failed to load. See the chapter on Dynamic Linking Problems in the SWIG Java documentation for help.\n" + e);
System.exit(1);
}
}
public static void main(String argv[])
{
// ----- Object creation -----
System.out.println( "Creating some objects:" );
Circle c = new Circle(10);
System.out.println( " Created circle " + c );
Square s = new Square(10);
System.out.println( " Created square " + s );
// ----- Access a static member -----
System.out.println( "\nA total of " + Shape.getNshapes() + " shapes were created" );
// ----- Member data access -----
// Notice how we can do this using functions specific to
// the 'Circle' class.
c.setX(20);
c.setY(30);
// Now use the same functions in the base class
Shape shape = s;
shape.setX(-10);
shape.setY(5);
System.out.println( "\nHere is their current position:" );
System.out.println( " Circle = (" + c.getX() + " " + c.getY() + ")" );
System.out.println( " Square = (" + s.getX() + " " + s.getY() + ")" );
// ----- Call some methods -----
System.out.println( "\nHere are some properties of the shapes:" );
Shape[] shapes = {c,s};
for (int i=0; i
e) 执行以下命令
swig -c++ -java example.i
g++ -Wall -Wl,--kill-at -I "%JAVA_HOME%/include" -I "%JAVA_HOME%/include/win32" -shared -s -o example.dll *.cxx
javac -d . *.java
java -cp . runme
- 调用函数指针
重点:这个只能获得函数指针,然后多写个函数把函数指针传递给c层来调用,java层没有找到方法调用
这种方法会出现一些比较奇怪的类名,例如:SWIGTYPE_p_f_float_float__float,为了类名可读,可以用下一部分说的回调来处理,只是不需要java继承,直接get出来,然后调用run就可以用了
a) 编写example.h
/****************************************
* 函数指针结构体
***************************************/
typedef float (*callback_t)(float a, float b);
typedef struct _OP {
callback_t p_add;
callback_t p_sub;
callback_t p_mul;
callback_t p_div;
} OP;
extern OP* get_init_op(void);
extern float add_sub_mul_div(float a, float b, callback_t fun);
b) 编写example.i
/* File : example.i */
%module example
%{
#include "example.h"
%}
%include "example.h"
c) 编写example.c
#include
#include
#include "example.h"
/****************************************
* 加减乘除函数
***************************************/
float ADD(float a, float b)
{
return a + b;
}
float SUB(float a, float b)
{
return a - b;
}
float MUL(float a, float b)
{
return a * b;
}
float DIV(float a, float b)
{
return a / b;
}
/****************************************
* 初始化函数指针
***************************************/
OP* get_init_op(void)
{
OP *op = (OP *)malloc(sizeof(OP));
op->p_add = ADD;
op->p_sub = SUB;
op->p_mul = MUL;
op->p_div = DIV;
return op;
}
/****************************************
* 库函数
***************************************/
float add_sub_mul_div(float a, float b, callback_t fun)
{
return fun(a, b);
}
int main()
{
OP *op = get_init_op();
/* 直接使用函数指针调用函数 */
printf("ADD = %f, SUB = %f, MUL = %f, DIV = %f\n", (op->p_add)(1.3, 2.2), (*op->p_sub)(1.3, 2.2),
(op->p_mul)(1.3, 2.2), (*op->p_div)(1.3, 2.2));
/* 调用回调函数 */
printf("ADD = %f, SUB = %f, MUL = %f, DIV = %f\n",
add_sub_mul_div(1.3, 2.2, ADD),
add_sub_mul_div(1.3, 2.2, SUB),
add_sub_mul_div(1.3, 2.2, MUL),
add_sub_mul_div(1.3, 2.2, DIV));
return 0;
}
d) 编写runme.java
// runme.java
public class runme {
static {
System.loadLibrary("example");
}
public static void main(String argv[]) {
OP op = example.get_init_op();
System.out.println(example.add_sub_mul_div(1.3f, 2.2f,op.getP_add()));
System.out.println(example.add_sub_mul_div(1.3f, 2.2f,op.getP_sub()));
System.out.println(example.add_sub_mul_div(1.3f, 2.2f,op.getP_mul()));
System.out.println(example.add_sub_mul_div(1.3f, 2.2f,op.getP_div()));
}
}
e) 执行以下命令,同1.d
- 回调
这个c代码搞不定,只能通过c++类实现,参考上例修改实现
参考:swigwin-3.0.12\Examples\java\callback
a) 编写example.h
class Callback {
public:
virtual ~Callback() {}
virtual float run(float a, float b) {return 0;}
};
class OP {
public:
Callback *p_add;
Callback *p_sub;
Callback *p_mul;
Callback *p_div;
public:
OP(): p_add(0),p_sub(0),p_mul(0),p_div(0) {}
~OP() {
}
float add_sub_mul_div(float a, float b, Callback* fun) {
if(fun != NULL) {
return fun->run(a, b);
}
printf("add_sub_mul_div error\n");
return 0;
}
};
b) 编写example.i
重点:这里必须设置%module(directors="1") example
并且指定回调类:
%feature("director") Callback;
/* File : example.i */
%module(directors="1") example
%{
#include "example.h"
%}
%feature("director") Callback;
%include "example.h"
c) 编写runme.java
// runme.java
public class runme {
static {
System.loadLibrary("example");
}
public static void main(String argv[]) {
OP op = new OP();
op.setP_add(new JavaCallbackAdd());
op.setP_sub(new JavaCallbackSub());
op.setP_mul(new JavaCallbackMul());
op.setP_div(new JavaCallbackDiv());
System.out.println(op.add_sub_mul_div(1.3f, 2.2f,op.getP_add()));
System.out.println(op.add_sub_mul_div(1.3f, 2.2f,op.getP_sub()));
System.out.println(op.add_sub_mul_div(1.3f, 2.2f,op.getP_mul()));
System.out.println(op.add_sub_mul_div(1.3f, 2.2f,op.getP_div()));
System.out.println(op.getP_add().run(1.3f, 2.2f));
System.out.println(op.getP_sub().run(1.3f, 2.2f));
System.out.println(op.getP_mul().run(1.3f, 2.2f));
System.out.println(op.getP_div().run(1.3f, 2.2f));
}
}
class JavaCallbackAdd extends Callback
{
public float run(float a, float b) {
return a + b;
}
}
class JavaCallbackSub extends Callback
{
public float run(float a, float b) {
return a - b;
}
}
class JavaCallbackMul extends Callback
{
public float run(float a, float b) {
return a * b;
}
}
class JavaCallbackDiv extends Callback
{
public float run(float a, float b) {
return a / b;
}
}
d) 执行以下命令
swig -c++ -java example.i
g++ -Wall -Wl,--kill-at -I "%JAVA_HOME%/include" -I "%JAVA_HOME%/include/win32" -shared -s -o example.dll *.cxx
javac -d . *.java
java -cp . runme
- 类型转换
a) 错误的情况
C函数包装
%module example
int add_option(const unsigned char *data);
得到的包装函数:
public static int add_option(SWIGTYPE_p_unsigned_char data)
其中SWIGTYPE_p_unsigned_char完全无法赋值,也就是无法使用
b) 正确的处理
%module example
%include
%apply signed char *INOUT { unsigned char * };
%apply signed char[] { const unsigned char * };
int add_option(const unsigned char *data);
这样就可以得到包装函数:
public static int add_option(byte[] data)
c) 更多的类型转换
%include "typemaps.i"
%include "stdint.i"
%include "arrays_java.i"
%include "carrays.i"
typedef int8_t Int8;
typedef uint8_t Uint8;
typedef int16_t Int16;
typedef uint16_t Uint16;
typedef uint32_t Int32;
typedef uint32_t Uint32;
- 两个常用命令
a) Javah
javah –jni Java类名,可以通过java类生成.h 头文件
b) javap
javap -s -p Java类名
用来输出一个Java类的方法的签名,用于c调用java类获得methid的时候使用